@backblaze-labs/b2-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/LICENSE +21 -0
  3. package/README.md +664 -0
  4. package/dist/auth/account-info.d.cts +51 -0
  5. package/dist/auth/account-info.d.cts.map +1 -0
  6. package/dist/auth/account-info.d.ts +51 -0
  7. package/dist/auth/account-info.d.ts.map +1 -0
  8. package/dist/auth/file.cjs +210 -0
  9. package/dist/auth/file.cjs.map +1 -0
  10. package/dist/auth/file.d.cts +157 -0
  11. package/dist/auth/file.d.cts.map +1 -0
  12. package/dist/auth/file.d.ts +157 -0
  13. package/dist/auth/file.d.ts.map +1 -0
  14. package/dist/auth/file.js +210 -0
  15. package/dist/auth/file.js.map +1 -0
  16. package/dist/auth/in-memory.cjs +184 -0
  17. package/dist/auth/in-memory.cjs.map +1 -0
  18. package/dist/auth/in-memory.d.cts +147 -0
  19. package/dist/auth/in-memory.d.cts.map +1 -0
  20. package/dist/auth/in-memory.d.ts +147 -0
  21. package/dist/auth/in-memory.d.ts.map +1 -0
  22. package/dist/auth/in-memory.js +184 -0
  23. package/dist/auth/in-memory.js.map +1 -0
  24. package/dist/auth/index.cjs +10 -0
  25. package/dist/auth/index.cjs.map +1 -0
  26. package/dist/auth/index.d.cts +6 -0
  27. package/dist/auth/index.d.cts.map +1 -0
  28. package/dist/auth/index.d.ts +6 -0
  29. package/dist/auth/index.d.ts.map +1 -0
  30. package/dist/auth/index.js +10 -0
  31. package/dist/auth/index.js.map +1 -0
  32. package/dist/auth/realms.cjs +12 -0
  33. package/dist/auth/realms.cjs.map +1 -0
  34. package/dist/auth/realms.d.cts +12 -0
  35. package/dist/auth/realms.d.cts.map +1 -0
  36. package/dist/auth/realms.d.ts +12 -0
  37. package/dist/auth/realms.d.ts.map +1 -0
  38. package/dist/auth/realms.js +12 -0
  39. package/dist/auth/realms.js.map +1 -0
  40. package/dist/auth/upload-url-pool.cjs +52 -0
  41. package/dist/auth/upload-url-pool.cjs.map +1 -0
  42. package/dist/auth/upload-url-pool.d.cts +35 -0
  43. package/dist/auth/upload-url-pool.d.cts.map +1 -0
  44. package/dist/auth/upload-url-pool.d.ts +35 -0
  45. package/dist/auth/upload-url-pool.d.ts.map +1 -0
  46. package/dist/auth/upload-url-pool.js +52 -0
  47. package/dist/auth/upload-url-pool.js.map +1 -0
  48. package/dist/bucket.cjs +789 -0
  49. package/dist/bucket.cjs.map +1 -0
  50. package/dist/bucket.d.cts +659 -0
  51. package/dist/bucket.d.cts.map +1 -0
  52. package/dist/bucket.d.ts +659 -0
  53. package/dist/bucket.d.ts.map +1 -0
  54. package/dist/bucket.js +789 -0
  55. package/dist/bucket.js.map +1 -0
  56. package/dist/client.cjs +230 -0
  57. package/dist/client.cjs.map +1 -0
  58. package/dist/client.d.cts +222 -0
  59. package/dist/client.d.cts.map +1 -0
  60. package/dist/client.d.ts +222 -0
  61. package/dist/client.d.ts.map +1 -0
  62. package/dist/client.js +230 -0
  63. package/dist/client.js.map +1 -0
  64. package/dist/copy/large.cjs +74 -0
  65. package/dist/copy/large.cjs.map +1 -0
  66. package/dist/copy/large.d.cts +53 -0
  67. package/dist/copy/large.d.cts.map +1 -0
  68. package/dist/copy/large.d.ts +53 -0
  69. package/dist/copy/large.d.ts.map +1 -0
  70. package/dist/copy/large.js +74 -0
  71. package/dist/copy/large.js.map +1 -0
  72. package/dist/download/parallel.cjs +122 -0
  73. package/dist/download/parallel.cjs.map +1 -0
  74. package/dist/download/parallel.d.cts +42 -0
  75. package/dist/download/parallel.d.cts.map +1 -0
  76. package/dist/download/parallel.d.ts +42 -0
  77. package/dist/download/parallel.d.ts.map +1 -0
  78. package/dist/download/parallel.js +122 -0
  79. package/dist/download/parallel.js.map +1 -0
  80. package/dist/download/single.cjs +117 -0
  81. package/dist/download/single.cjs.map +1 -0
  82. package/dist/download/single.d.cts +152 -0
  83. package/dist/download/single.d.cts.map +1 -0
  84. package/dist/download/single.d.ts +152 -0
  85. package/dist/download/single.d.ts.map +1 -0
  86. package/dist/download/single.js +117 -0
  87. package/dist/download/single.js.map +1 -0
  88. package/dist/errors/index.cjs +268 -0
  89. package/dist/errors/index.cjs.map +1 -0
  90. package/dist/errors/index.d.cts +264 -0
  91. package/dist/errors/index.d.cts.map +1 -0
  92. package/dist/errors/index.d.ts +264 -0
  93. package/dist/errors/index.d.ts.map +1 -0
  94. package/dist/errors/index.js +268 -0
  95. package/dist/errors/index.js.map +1 -0
  96. package/dist/http/retry.cjs +36 -0
  97. package/dist/http/retry.cjs.map +1 -0
  98. package/dist/http/retry.d.cts +34 -0
  99. package/dist/http/retry.d.cts.map +1 -0
  100. package/dist/http/retry.d.ts +34 -0
  101. package/dist/http/retry.d.ts.map +1 -0
  102. package/dist/http/retry.js +36 -0
  103. package/dist/http/retry.js.map +1 -0
  104. package/dist/http/transport.cjs +143 -0
  105. package/dist/http/transport.cjs.map +1 -0
  106. package/dist/http/transport.d.cts +122 -0
  107. package/dist/http/transport.d.cts.map +1 -0
  108. package/dist/http/transport.d.ts +122 -0
  109. package/dist/http/transport.d.ts.map +1 -0
  110. package/dist/http/transport.js +143 -0
  111. package/dist/http/transport.js.map +1 -0
  112. package/dist/http/url-guard.cjs +97 -0
  113. package/dist/http/url-guard.cjs.map +1 -0
  114. package/dist/http/url-guard.d.cts +88 -0
  115. package/dist/http/url-guard.d.cts.map +1 -0
  116. package/dist/http/url-guard.d.ts +88 -0
  117. package/dist/http/url-guard.d.ts.map +1 -0
  118. package/dist/http/url-guard.js +97 -0
  119. package/dist/http/url-guard.js.map +1 -0
  120. package/dist/http/user-agent.cjs +51 -0
  121. package/dist/http/user-agent.cjs.map +1 -0
  122. package/dist/http/user-agent.d.cts +28 -0
  123. package/dist/http/user-agent.d.cts.map +1 -0
  124. package/dist/http/user-agent.d.ts +28 -0
  125. package/dist/http/user-agent.d.ts.map +1 -0
  126. package/dist/http/user-agent.js +51 -0
  127. package/dist/http/user-agent.js.map +1 -0
  128. package/dist/index.cjs +78 -0
  129. package/dist/index.cjs.map +1 -0
  130. package/dist/index.d.cts +39 -0
  131. package/dist/index.d.cts.map +1 -0
  132. package/dist/index.d.ts +39 -0
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +78 -0
  135. package/dist/index.js.map +1 -0
  136. package/dist/notifications/index.cjs +7 -0
  137. package/dist/notifications/index.cjs.map +1 -0
  138. package/dist/notifications/index.d.cts +3 -0
  139. package/dist/notifications/index.d.cts.map +1 -0
  140. package/dist/notifications/index.d.ts +3 -0
  141. package/dist/notifications/index.d.ts.map +1 -0
  142. package/dist/notifications/index.js +7 -0
  143. package/dist/notifications/index.js.map +1 -0
  144. package/dist/notifications/webhook.cjs +106 -0
  145. package/dist/notifications/webhook.cjs.map +1 -0
  146. package/dist/notifications/webhook.d.cts +146 -0
  147. package/dist/notifications/webhook.d.cts.map +1 -0
  148. package/dist/notifications/webhook.d.ts +146 -0
  149. package/dist/notifications/webhook.d.ts.map +1 -0
  150. package/dist/notifications/webhook.js +106 -0
  151. package/dist/notifications/webhook.js.map +1 -0
  152. package/dist/object.cjs +212 -0
  153. package/dist/object.cjs.map +1 -0
  154. package/dist/object.d.cts +263 -0
  155. package/dist/object.d.cts.map +1 -0
  156. package/dist/object.d.ts +263 -0
  157. package/dist/object.d.ts.map +1 -0
  158. package/dist/object.js +212 -0
  159. package/dist/object.js.map +1 -0
  160. package/dist/package.json.cjs +9 -0
  161. package/dist/package.json.cjs.map +1 -0
  162. package/dist/package.json.js +9 -0
  163. package/dist/package.json.js.map +1 -0
  164. package/dist/raw/encoding.cjs +47 -0
  165. package/dist/raw/encoding.cjs.map +1 -0
  166. package/dist/raw/encoding.d.cts +46 -0
  167. package/dist/raw/encoding.d.cts.map +1 -0
  168. package/dist/raw/encoding.d.ts +46 -0
  169. package/dist/raw/encoding.d.ts.map +1 -0
  170. package/dist/raw/encoding.js +47 -0
  171. package/dist/raw/encoding.js.map +1 -0
  172. package/dist/raw/index.cjs +600 -0
  173. package/dist/raw/index.cjs.map +1 -0
  174. package/dist/raw/index.d.cts +381 -0
  175. package/dist/raw/index.d.cts.map +1 -0
  176. package/dist/raw/index.d.ts +381 -0
  177. package/dist/raw/index.d.ts.map +1 -0
  178. package/dist/raw/index.js +601 -0
  179. package/dist/raw/index.js.map +1 -0
  180. package/dist/s3/index.cjs +23 -0
  181. package/dist/s3/index.cjs.map +1 -0
  182. package/dist/s3/index.d.cts +74 -0
  183. package/dist/s3/index.d.cts.map +1 -0
  184. package/dist/s3/index.d.ts +74 -0
  185. package/dist/s3/index.d.ts.map +1 -0
  186. package/dist/s3/index.js +23 -0
  187. package/dist/s3/index.js.map +1 -0
  188. package/dist/simulator/capabilities.cjs +53 -0
  189. package/dist/simulator/capabilities.cjs.map +1 -0
  190. package/dist/simulator/capabilities.d.cts +15 -0
  191. package/dist/simulator/capabilities.d.cts.map +1 -0
  192. package/dist/simulator/capabilities.d.ts +15 -0
  193. package/dist/simulator/capabilities.d.ts.map +1 -0
  194. package/dist/simulator/capabilities.js +53 -0
  195. package/dist/simulator/capabilities.js.map +1 -0
  196. package/dist/simulator/index.cjs +1559 -0
  197. package/dist/simulator/index.cjs.map +1 -0
  198. package/dist/simulator/index.d.cts +495 -0
  199. package/dist/simulator/index.d.cts.map +1 -0
  200. package/dist/simulator/index.d.ts +495 -0
  201. package/dist/simulator/index.d.ts.map +1 -0
  202. package/dist/simulator/index.js +1560 -0
  203. package/dist/simulator/index.js.map +1 -0
  204. package/dist/simulator/validation.cjs +174 -0
  205. package/dist/simulator/validation.cjs.map +1 -0
  206. package/dist/simulator/validation.d.cts +104 -0
  207. package/dist/simulator/validation.d.cts.map +1 -0
  208. package/dist/simulator/validation.d.ts +104 -0
  209. package/dist/simulator/validation.d.ts.map +1 -0
  210. package/dist/simulator/validation.js +174 -0
  211. package/dist/simulator/validation.js.map +1 -0
  212. package/dist/streams/collect.cjs +26 -0
  213. package/dist/streams/collect.cjs.map +1 -0
  214. package/dist/streams/collect.d.cts +18 -0
  215. package/dist/streams/collect.d.cts.map +1 -0
  216. package/dist/streams/collect.d.ts +18 -0
  217. package/dist/streams/collect.d.ts.map +1 -0
  218. package/dist/streams/collect.js +26 -0
  219. package/dist/streams/collect.js.map +1 -0
  220. package/dist/streams/hash.cjs +123 -0
  221. package/dist/streams/hash.cjs.map +1 -0
  222. package/dist/streams/hash.d.cts +42 -0
  223. package/dist/streams/hash.d.cts.map +1 -0
  224. package/dist/streams/hash.d.ts +42 -0
  225. package/dist/streams/hash.d.ts.map +1 -0
  226. package/dist/streams/hash.js +101 -0
  227. package/dist/streams/hash.js.map +1 -0
  228. package/dist/streams/index.cjs +13 -0
  229. package/dist/streams/index.cjs.map +1 -0
  230. package/dist/streams/index.d.cts +7 -0
  231. package/dist/streams/index.d.cts.map +1 -0
  232. package/dist/streams/index.d.ts +7 -0
  233. package/dist/streams/index.d.ts.map +1 -0
  234. package/dist/streams/index.js +13 -0
  235. package/dist/streams/index.js.map +1 -0
  236. package/dist/streams/progress.cjs +47 -0
  237. package/dist/streams/progress.cjs.map +1 -0
  238. package/dist/streams/progress.d.cts +53 -0
  239. package/dist/streams/progress.d.cts.map +1 -0
  240. package/dist/streams/progress.d.ts +53 -0
  241. package/dist/streams/progress.d.ts.map +1 -0
  242. package/dist/streams/progress.js +47 -0
  243. package/dist/streams/progress.js.map +1 -0
  244. package/dist/streams/source.cjs +155 -0
  245. package/dist/streams/source.cjs.map +1 -0
  246. package/dist/streams/source.d.cts +140 -0
  247. package/dist/streams/source.d.cts.map +1 -0
  248. package/dist/streams/source.d.ts +140 -0
  249. package/dist/streams/source.d.ts.map +1 -0
  250. package/dist/streams/source.js +155 -0
  251. package/dist/streams/source.js.map +1 -0
  252. package/dist/sync/actions/index.cjs +191 -0
  253. package/dist/sync/actions/index.cjs.map +1 -0
  254. package/dist/sync/actions/index.d.cts +167 -0
  255. package/dist/sync/actions/index.d.cts.map +1 -0
  256. package/dist/sync/actions/index.d.ts +167 -0
  257. package/dist/sync/actions/index.d.ts.map +1 -0
  258. package/dist/sync/actions/index.js +191 -0
  259. package/dist/sync/actions/index.js.map +1 -0
  260. package/dist/sync/index.cjs +23 -0
  261. package/dist/sync/index.cjs.map +1 -0
  262. package/dist/sync/index.d.cts +21 -0
  263. package/dist/sync/index.d.cts.map +1 -0
  264. package/dist/sync/index.d.ts +21 -0
  265. package/dist/sync/index.d.ts.map +1 -0
  266. package/dist/sync/index.js +23 -0
  267. package/dist/sync/index.js.map +1 -0
  268. package/dist/sync/pairing.cjs +31 -0
  269. package/dist/sync/pairing.cjs.map +1 -0
  270. package/dist/sync/pairing.d.cts +13 -0
  271. package/dist/sync/pairing.d.cts.map +1 -0
  272. package/dist/sync/pairing.d.ts +13 -0
  273. package/dist/sync/pairing.d.ts.map +1 -0
  274. package/dist/sync/pairing.js +31 -0
  275. package/dist/sync/pairing.js.map +1 -0
  276. package/dist/sync/policies/compare.cjs +14 -0
  277. package/dist/sync/policies/compare.cjs.map +1 -0
  278. package/dist/sync/policies/compare.d.cts +13 -0
  279. package/dist/sync/policies/compare.d.cts.map +1 -0
  280. package/dist/sync/policies/compare.d.ts +13 -0
  281. package/dist/sync/policies/compare.d.ts.map +1 -0
  282. package/dist/sync/policies/compare.js +14 -0
  283. package/dist/sync/policies/compare.js.map +1 -0
  284. package/dist/sync/policies/index.cjs +74 -0
  285. package/dist/sync/policies/index.cjs.map +1 -0
  286. package/dist/sync/policies/index.d.cts +42 -0
  287. package/dist/sync/policies/index.d.cts.map +1 -0
  288. package/dist/sync/policies/index.d.ts +42 -0
  289. package/dist/sync/policies/index.d.ts.map +1 -0
  290. package/dist/sync/policies/index.js +74 -0
  291. package/dist/sync/policies/index.js.map +1 -0
  292. package/dist/sync/scanners/b2.cjs +57 -0
  293. package/dist/sync/scanners/b2.cjs.map +1 -0
  294. package/dist/sync/scanners/b2.d.cts +20 -0
  295. package/dist/sync/scanners/b2.d.cts.map +1 -0
  296. package/dist/sync/scanners/b2.d.ts +20 -0
  297. package/dist/sync/scanners/b2.d.ts.map +1 -0
  298. package/dist/sync/scanners/b2.js +57 -0
  299. package/dist/sync/scanners/b2.js.map +1 -0
  300. package/dist/sync/scanners/local.cjs +58 -0
  301. package/dist/sync/scanners/local.cjs.map +1 -0
  302. package/dist/sync/scanners/local.d.cts +24 -0
  303. package/dist/sync/scanners/local.d.cts.map +1 -0
  304. package/dist/sync/scanners/local.d.ts +24 -0
  305. package/dist/sync/scanners/local.d.ts.map +1 -0
  306. package/dist/sync/scanners/local.js +58 -0
  307. package/dist/sync/scanners/local.js.map +1 -0
  308. package/dist/sync/synchronizer.cjs +203 -0
  309. package/dist/sync/synchronizer.cjs.map +1 -0
  310. package/dist/sync/synchronizer.d.cts +52 -0
  311. package/dist/sync/synchronizer.d.cts.map +1 -0
  312. package/dist/sync/synchronizer.d.ts +52 -0
  313. package/dist/sync/synchronizer.d.ts.map +1 -0
  314. package/dist/sync/synchronizer.js +181 -0
  315. package/dist/sync/synchronizer.js.map +1 -0
  316. package/dist/sync/types.d.cts +121 -0
  317. package/dist/sync/types.d.cts.map +1 -0
  318. package/dist/sync/types.d.ts +121 -0
  319. package/dist/sync/types.d.ts.map +1 -0
  320. package/dist/types/auth.cjs +58 -0
  321. package/dist/types/auth.cjs.map +1 -0
  322. package/dist/types/auth.d.cts +150 -0
  323. package/dist/types/auth.d.cts.map +1 -0
  324. package/dist/types/auth.d.ts +150 -0
  325. package/dist/types/auth.d.ts.map +1 -0
  326. package/dist/types/auth.js +58 -0
  327. package/dist/types/auth.js.map +1 -0
  328. package/dist/types/bucket.cjs +44 -0
  329. package/dist/types/bucket.cjs.map +1 -0
  330. package/dist/types/bucket.d.cts +245 -0
  331. package/dist/types/bucket.d.cts.map +1 -0
  332. package/dist/types/bucket.d.ts +245 -0
  333. package/dist/types/bucket.d.ts.map +1 -0
  334. package/dist/types/bucket.js +44 -0
  335. package/dist/types/bucket.js.map +1 -0
  336. package/dist/types/download.d.cts +110 -0
  337. package/dist/types/download.d.cts.map +1 -0
  338. package/dist/types/download.d.ts +110 -0
  339. package/dist/types/download.d.ts.map +1 -0
  340. package/dist/types/encryption.cjs +343 -0
  341. package/dist/types/encryption.cjs.map +1 -0
  342. package/dist/types/encryption.d.cts +132 -0
  343. package/dist/types/encryption.d.cts.map +1 -0
  344. package/dist/types/encryption.d.ts +132 -0
  345. package/dist/types/encryption.d.ts.map +1 -0
  346. package/dist/types/encryption.js +321 -0
  347. package/dist/types/encryption.js.map +1 -0
  348. package/dist/types/errors.d.cts +16 -0
  349. package/dist/types/errors.d.cts.map +1 -0
  350. package/dist/types/errors.d.ts +16 -0
  351. package/dist/types/errors.d.ts.map +1 -0
  352. package/dist/types/file.cjs +23 -0
  353. package/dist/types/file.cjs.map +1 -0
  354. package/dist/types/file.d.cts +220 -0
  355. package/dist/types/file.d.cts.map +1 -0
  356. package/dist/types/file.d.ts +220 -0
  357. package/dist/types/file.d.ts.map +1 -0
  358. package/dist/types/file.js +23 -0
  359. package/dist/types/file.js.map +1 -0
  360. package/dist/types/ids.cjs +27 -0
  361. package/dist/types/ids.cjs.map +1 -0
  362. package/dist/types/ids.d.cts +84 -0
  363. package/dist/types/ids.d.cts.map +1 -0
  364. package/dist/types/ids.d.ts +84 -0
  365. package/dist/types/ids.d.ts.map +1 -0
  366. package/dist/types/ids.js +27 -0
  367. package/dist/types/ids.js.map +1 -0
  368. package/dist/types/index.d.cts +25 -0
  369. package/dist/types/index.d.cts.map +1 -0
  370. package/dist/types/index.d.ts +25 -0
  371. package/dist/types/index.d.ts.map +1 -0
  372. package/dist/types/key.d.cts +84 -0
  373. package/dist/types/key.d.cts.map +1 -0
  374. package/dist/types/key.d.ts +84 -0
  375. package/dist/types/key.d.ts.map +1 -0
  376. package/dist/types/lock.cjs +17 -0
  377. package/dist/types/lock.cjs.map +1 -0
  378. package/dist/types/lock.d.cts +92 -0
  379. package/dist/types/lock.d.cts.map +1 -0
  380. package/dist/types/lock.d.ts +92 -0
  381. package/dist/types/lock.d.ts.map +1 -0
  382. package/dist/types/lock.js +17 -0
  383. package/dist/types/lock.js.map +1 -0
  384. package/dist/types/notifications.cjs +24 -0
  385. package/dist/types/notifications.cjs.map +1 -0
  386. package/dist/types/notifications.d.cts +97 -0
  387. package/dist/types/notifications.d.cts.map +1 -0
  388. package/dist/types/notifications.d.ts +97 -0
  389. package/dist/types/notifications.d.ts.map +1 -0
  390. package/dist/types/notifications.js +24 -0
  391. package/dist/types/notifications.js.map +1 -0
  392. package/dist/types/replication.d.cts +42 -0
  393. package/dist/types/replication.d.cts.map +1 -0
  394. package/dist/types/replication.d.ts +42 -0
  395. package/dist/types/replication.d.ts.map +1 -0
  396. package/dist/types/upload.d.cts +219 -0
  397. package/dist/types/upload.d.cts.map +1 -0
  398. package/dist/types/upload.d.ts +219 -0
  399. package/dist/types/upload.d.ts.map +1 -0
  400. package/dist/upload/cancel.cjs +10 -0
  401. package/dist/upload/cancel.cjs.map +1 -0
  402. package/dist/upload/cancel.d.cts +23 -0
  403. package/dist/upload/cancel.d.cts.map +1 -0
  404. package/dist/upload/cancel.d.ts +23 -0
  405. package/dist/upload/cancel.d.ts.map +1 -0
  406. package/dist/upload/cancel.js +10 -0
  407. package/dist/upload/cancel.js.map +1 -0
  408. package/dist/upload/concurrency.cjs +54 -0
  409. package/dist/upload/concurrency.cjs.map +1 -0
  410. package/dist/upload/concurrency.d.cts +46 -0
  411. package/dist/upload/concurrency.d.cts.map +1 -0
  412. package/dist/upload/concurrency.d.ts +46 -0
  413. package/dist/upload/concurrency.d.ts.map +1 -0
  414. package/dist/upload/concurrency.js +54 -0
  415. package/dist/upload/concurrency.js.map +1 -0
  416. package/dist/upload/large.cjs +222 -0
  417. package/dist/upload/large.cjs.map +1 -0
  418. package/dist/upload/large.d.cts +71 -0
  419. package/dist/upload/large.d.cts.map +1 -0
  420. package/dist/upload/large.d.ts +71 -0
  421. package/dist/upload/large.d.ts.map +1 -0
  422. package/dist/upload/large.js +222 -0
  423. package/dist/upload/large.js.map +1 -0
  424. package/dist/upload/resume.cjs +34 -0
  425. package/dist/upload/resume.cjs.map +1 -0
  426. package/dist/upload/resume.d.cts +33 -0
  427. package/dist/upload/resume.d.cts.map +1 -0
  428. package/dist/upload/resume.d.ts +33 -0
  429. package/dist/upload/resume.d.ts.map +1 -0
  430. package/dist/upload/resume.js +34 -0
  431. package/dist/upload/resume.js.map +1 -0
  432. package/dist/upload/single.cjs +47 -0
  433. package/dist/upload/single.cjs.map +1 -0
  434. package/dist/upload/single.d.cts +51 -0
  435. package/dist/upload/single.d.cts.map +1 -0
  436. package/dist/upload/single.d.ts +51 -0
  437. package/dist/upload/single.d.ts.map +1 -0
  438. package/dist/upload/single.js +47 -0
  439. package/dist/upload/single.js.map +1 -0
  440. package/dist/upload/stream.cjs +198 -0
  441. package/dist/upload/stream.cjs.map +1 -0
  442. package/dist/upload/stream.d.cts +61 -0
  443. package/dist/upload/stream.d.cts.map +1 -0
  444. package/dist/upload/stream.d.ts +61 -0
  445. package/dist/upload/stream.d.ts.map +1 -0
  446. package/dist/upload/stream.js +198 -0
  447. package/dist/upload/stream.js.map +1 -0
  448. package/dist/util/best-effort.cjs +10 -0
  449. package/dist/util/best-effort.cjs.map +1 -0
  450. package/dist/util/best-effort.d.cts +31 -0
  451. package/dist/util/best-effort.d.cts.map +1 -0
  452. package/dist/util/best-effort.d.ts +31 -0
  453. package/dist/util/best-effort.d.ts.map +1 -0
  454. package/dist/util/best-effort.js +10 -0
  455. package/dist/util/best-effort.js.map +1 -0
  456. package/dist/util/defaults.cjs +11 -0
  457. package/dist/util/defaults.cjs.map +1 -0
  458. package/dist/util/defaults.d.cts +54 -0
  459. package/dist/util/defaults.d.cts.map +1 -0
  460. package/dist/util/defaults.d.ts +54 -0
  461. package/dist/util/defaults.d.ts.map +1 -0
  462. package/dist/util/defaults.js +11 -0
  463. package/dist/util/defaults.js.map +1 -0
  464. package/dist/util/normalize.cjs +16 -0
  465. package/dist/util/normalize.cjs.map +1 -0
  466. package/dist/util/normalize.d.cts +62 -0
  467. package/dist/util/normalize.d.cts.map +1 -0
  468. package/dist/util/normalize.d.ts +62 -0
  469. package/dist/util/normalize.d.ts.map +1 -0
  470. package/dist/util/normalize.js +16 -0
  471. package/dist/util/normalize.js.map +1 -0
  472. package/dist/util/paginator.cjs +20 -0
  473. package/dist/util/paginator.cjs.map +1 -0
  474. package/dist/util/paginator.d.cts +120 -0
  475. package/dist/util/paginator.d.cts.map +1 -0
  476. package/dist/util/paginator.d.ts +120 -0
  477. package/dist/util/paginator.d.ts.map +1 -0
  478. package/dist/util/paginator.js +20 -0
  479. package/dist/util/paginator.js.map +1 -0
  480. package/dist/util/plan-ranges.cjs +28 -0
  481. package/dist/util/plan-ranges.cjs.map +1 -0
  482. package/dist/util/plan-ranges.d.cts +56 -0
  483. package/dist/util/plan-ranges.d.cts.map +1 -0
  484. package/dist/util/plan-ranges.d.ts +56 -0
  485. package/dist/util/plan-ranges.d.ts.map +1 -0
  486. package/dist/util/plan-ranges.js +28 -0
  487. package/dist/util/plan-ranges.js.map +1 -0
  488. package/dist/util/text-codec.cjs +7 -0
  489. package/dist/util/text-codec.cjs.map +1 -0
  490. package/dist/util/text-codec.d.cts +26 -0
  491. package/dist/util/text-codec.d.cts.map +1 -0
  492. package/dist/util/text-codec.d.ts +26 -0
  493. package/dist/util/text-codec.d.ts.map +1 -0
  494. package/dist/util/text-codec.js +7 -0
  495. package/dist/util/text-codec.js.map +1 -0
  496. package/dist/util/to-error.cjs +7 -0
  497. package/dist/util/to-error.cjs.map +1 -0
  498. package/dist/util/to-error.d.cts +15 -0
  499. package/dist/util/to-error.d.cts.map +1 -0
  500. package/dist/util/to-error.d.ts +15 -0
  501. package/dist/util/to-error.d.ts.map +1 -0
  502. package/dist/util/to-error.js +7 -0
  503. package/dist/util/to-error.js.map +1 -0
  504. package/dist/version.cjs +6 -0
  505. package/dist/version.cjs.map +1 -0
  506. package/dist/version.d.cts +14 -0
  507. package/dist/version.d.cts.map +1 -0
  508. package/dist/version.d.ts +14 -0
  509. package/dist/version.d.ts.map +1 -0
  510. package/dist/version.js +6 -0
  511. package/dist/version.js.map +1 -0
  512. package/package.json +198 -0
@@ -0,0 +1,53 @@
1
+ import { AccountInfo } from '../auth/account-info.js';
2
+ import { RawClient } from '../raw/index.js';
3
+ import { EncryptionSetting } from '../types/encryption.js';
4
+ import { FileVersion } from '../types/file.js';
5
+ import { BucketId, FileId } from '../types/ids.js';
6
+ /** Options for a server-side multipart copy. */
7
+ export interface CopyLargeFileOptions {
8
+ /** ID of the source file version to copy. */
9
+ readonly sourceFileId: FileId;
10
+ /** Destination file name in the target bucket. */
11
+ readonly fileName: string;
12
+ /** Target bucket. Defaults to the source's bucket. */
13
+ readonly destinationBucketId?: BucketId;
14
+ /** Size of each part in bytes. Defaults to the account's recommended part size. */
15
+ readonly partSize?: number;
16
+ /**
17
+ * Maximum number of parts copied in parallel. Defaults to
18
+ * {@link DEFAULT_TRANSFER_CONCURRENCY}.
19
+ */
20
+ readonly concurrency?: number;
21
+ /** MIME type for the destination file. Defaults to `b2/x-auto`. */
22
+ readonly contentType?: string;
23
+ /** Custom file info merged into the destination. */
24
+ readonly fileInfo?: Record<string, string>;
25
+ /** Server-side encryption applied to the destination. */
26
+ readonly destinationServerSideEncryption?: EncryptionSetting;
27
+ /** SSE-C settings used to read the source if it was uploaded with SSE-C. */
28
+ readonly sourceServerSideEncryption?: EncryptionSetting;
29
+ /**
30
+ * Optional abort signal. Checked before dispatching each part and
31
+ * between parts; an aborted signal cancels remaining parts and rolls
32
+ * back the unfinished large file via best-effort `cancelLargeFile`.
33
+ *
34
+ * Aborting does NOT roll back parts already accepted by B2; those
35
+ * exist on the unfinished large file until the cancel call completes
36
+ * or B2's lifecycle expires the in-progress upload (24 hours).
37
+ */
38
+ readonly signal?: AbortSignal;
39
+ }
40
+ /**
41
+ * Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.
42
+ * The source bytes never traverse the client; B2 copies each range internally.
43
+ *
44
+ * Falls back to a single `copyFile` call when the source fits in one part.
45
+ *
46
+ * @param raw - Low-level B2 API client.
47
+ * @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).
48
+ * @param options - Copy parameters including source, destination, and concurrency.
49
+ *
50
+ * @returns The resulting destination {@link FileVersion}.
51
+ */
52
+ export declare function copyLargeFile(raw: RawClient, accountInfo: AccountInfo, options: CopyLargeFileOptions): Promise<FileVersion>;
53
+ //# sourceMappingURL=large.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"large.d.ts","sourceRoot":"","sources":["../../src/copy/large.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAsB,MAAM,iBAAiB,CAAA;AAMhF,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,QAAQ,CAAA;IACvC,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,yDAAyD;IACzD,QAAQ,CAAC,+BAA+B,CAAC,EAAE,iBAAiB,CAAA;IAC5D,4EAA4E;IAC5E,QAAQ,CAAC,0BAA0B,CAAC,EAAE,iBAAiB,CAAA;IACvD;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC,CA0FtB"}
@@ -0,0 +1,74 @@
1
+ import { fileId } from "../types/ids.js";
2
+ import { cancelLargeFileBestEffort } from "../upload/cancel.js";
3
+ import { Semaphore } from "../upload/concurrency.js";
4
+ import { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from "../util/defaults.js";
5
+ import { byteRangeHeader, planRanges } from "../util/plan-ranges.js";
6
+ async function copyLargeFile(raw, accountInfo, options) {
7
+ const recommendedPartSize = accountInfo.getRecommendedPartSize();
8
+ const minPartSize = accountInfo.getAbsoluteMinimumPartSize();
9
+ const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize);
10
+ const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY;
11
+ const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
12
+ fileId: options.sourceFileId
13
+ });
14
+ const totalSize = sourceInfo.contentLength;
15
+ if (totalSize <= partSize) {
16
+ return raw.copyFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
17
+ sourceFileId: options.sourceFileId,
18
+ fileName: options.fileName,
19
+ ...options.destinationBucketId !== void 0 ? { destinationBucketId: options.destinationBucketId } : {},
20
+ ...options.contentType !== void 0 ? { contentType: options.contentType } : {},
21
+ ...options.fileInfo !== void 0 ? { fileInfo: options.fileInfo } : {},
22
+ ...options.destinationServerSideEncryption !== void 0 ? { destinationServerSideEncryption: options.destinationServerSideEncryption } : {},
23
+ ...options.sourceServerSideEncryption !== void 0 ? { sourceServerSideEncryption: options.sourceServerSideEncryption } : {}
24
+ });
25
+ }
26
+ const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId;
27
+ const startResp = await raw.startLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
28
+ bucketId: destBucketId,
29
+ fileName: options.fileName,
30
+ contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,
31
+ fileInfo: options.fileInfo ?? {},
32
+ ...options.destinationServerSideEncryption !== void 0 ? { serverSideEncryption: options.destinationServerSideEncryption } : {}
33
+ });
34
+ const largeFileId = startResp.fileId;
35
+ const ranges = planRanges(totalSize, partSize);
36
+ const partSha1s = new Array(ranges.length);
37
+ const sem = new Semaphore(concurrency);
38
+ try {
39
+ options.signal?.throwIfAborted();
40
+ await Promise.all(
41
+ ranges.map(async (range) => {
42
+ await sem.acquire();
43
+ try {
44
+ options.signal?.throwIfAborted();
45
+ const resp = await raw.copyPart(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
46
+ sourceFileId: options.sourceFileId,
47
+ // `startLargeFile` returns `LargeFileId`; `copyPart` takes the
48
+ // same value typed as `FileId`. Re-brand via the factory.
49
+ largeFileId: fileId(largeFileId),
50
+ partNumber: range.partNumber,
51
+ range: byteRangeHeader(range.start, range.end),
52
+ ...options.sourceServerSideEncryption !== void 0 ? { sourceServerSideEncryption: options.sourceServerSideEncryption } : {},
53
+ ...options.destinationServerSideEncryption !== void 0 ? { destinationServerSideEncryption: options.destinationServerSideEncryption } : {}
54
+ });
55
+ partSha1s[range.partNumber - 1] = resp.contentSha1;
56
+ } finally {
57
+ sem.release();
58
+ }
59
+ })
60
+ );
61
+ options.signal?.throwIfAborted();
62
+ return await raw.finishLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {
63
+ fileId: largeFileId,
64
+ partSha1Array: partSha1s
65
+ });
66
+ } catch (err) {
67
+ await cancelLargeFileBestEffort(raw, accountInfo, largeFileId);
68
+ throw err;
69
+ }
70
+ }
71
+ export {
72
+ copyLargeFile
73
+ };
74
+ //# sourceMappingURL=large.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"large.js","sources":["../../src/copy/large.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport type { FileVersion } from '../types/file.ts'\nimport { type BucketId, type FileId, fileId as fileIdOf } from '../types/ids.ts'\nimport { cancelLargeFileBestEffort } from '../upload/cancel.ts'\nimport { Semaphore } from '../upload/concurrency.ts'\nimport { DEFAULT_CONTENT_TYPE, DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { byteRangeHeader, planRanges } from '../util/plan-ranges.ts'\n\n/** Options for a server-side multipart copy. */\nexport interface CopyLargeFileOptions {\n /** ID of the source file version to copy. */\n readonly sourceFileId: FileId\n /** Destination file name in the target bucket. */\n readonly fileName: string\n /** Target bucket. Defaults to the source's bucket. */\n readonly destinationBucketId?: BucketId\n /** Size of each part in bytes. Defaults to the account's recommended part size. */\n readonly partSize?: number\n /**\n * Maximum number of parts copied in parallel. Defaults to\n * {@link DEFAULT_TRANSFER_CONCURRENCY}.\n */\n readonly concurrency?: number\n /** MIME type for the destination file. Defaults to `b2/x-auto`. */\n readonly contentType?: string\n /** Custom file info merged into the destination. */\n readonly fileInfo?: Record<string, string>\n /** Server-side encryption applied to the destination. */\n readonly destinationServerSideEncryption?: EncryptionSetting\n /** SSE-C settings used to read the source if it was uploaded with SSE-C. */\n readonly sourceServerSideEncryption?: EncryptionSetting\n /**\n * Optional abort signal. Checked before dispatching each part and\n * between parts; an aborted signal cancels remaining parts and rolls\n * back the unfinished large file via best-effort `cancelLargeFile`.\n *\n * Aborting does NOT roll back parts already accepted by B2; those\n * exist on the unfinished large file until the cancel call completes\n * or B2's lifecycle expires the in-progress upload (24 hours).\n */\n readonly signal?: AbortSignal\n}\n\n/**\n * Performs a server-side copy of a file using the multipart `b2_copy_part` protocol.\n * The source bytes never traverse the client; B2 copies each range internally.\n *\n * Falls back to a single `copyFile` call when the source fits in one part.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state (used to resolve API URL, recommended part size).\n * @param options - Copy parameters including source, destination, and concurrency.\n *\n * @returns The resulting destination {@link FileVersion}.\n */\nexport async function copyLargeFile(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: CopyLargeFileOptions,\n): Promise<FileVersion> {\n const recommendedPartSize = accountInfo.getRecommendedPartSize()\n const minPartSize = accountInfo.getAbsoluteMinimumPartSize()\n const partSize = Math.max(options.partSize ?? recommendedPartSize, minPartSize)\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n\n // Discover the source size via getFileInfo.\n const sourceInfo = await raw.getFileInfo(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: options.sourceFileId,\n })\n const totalSize = sourceInfo.contentLength\n\n // Below the part threshold, take the single-call fast path.\n if (totalSize <= partSize) {\n return raw.copyFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n sourceFileId: options.sourceFileId,\n fileName: options.fileName,\n ...(options.destinationBucketId !== undefined\n ? { destinationBucketId: options.destinationBucketId }\n : {}),\n ...(options.contentType !== undefined ? { contentType: options.contentType } : {}),\n ...(options.fileInfo !== undefined ? { fileInfo: options.fileInfo } : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n })\n }\n\n // Resolve destination bucket (defaults to source's bucket). Both operands\n // are already typed `BucketId`, so no cast is needed.\n const destBucketId = options.destinationBucketId ?? sourceInfo.bucketId\n\n // Start the multipart file.\n const startResp = await raw.startLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n bucketId: destBucketId,\n fileName: options.fileName,\n contentType: options.contentType ?? sourceInfo.contentType ?? DEFAULT_CONTENT_TYPE,\n fileInfo: options.fileInfo ?? {},\n ...(options.destinationServerSideEncryption !== undefined\n ? { serverSideEncryption: options.destinationServerSideEncryption }\n : {}),\n })\n const largeFileId = startResp.fileId\n\n const ranges = planRanges(totalSize, partSize)\n const partSha1s: string[] = new Array(ranges.length)\n const sem = new Semaphore(concurrency)\n\n try {\n options.signal?.throwIfAborted()\n await Promise.all(\n ranges.map(async (range) => {\n await sem.acquire()\n try {\n // Re-check the abort flag after every queue handoff so the\n // first cancelled task short-circuits the remaining parts.\n options.signal?.throwIfAborted()\n const resp = await raw.copyPart(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n sourceFileId: options.sourceFileId,\n // `startLargeFile` returns `LargeFileId`; `copyPart` takes the\n // same value typed as `FileId`. Re-brand via the factory.\n largeFileId: fileIdOf(largeFileId),\n partNumber: range.partNumber,\n range: byteRangeHeader(range.start, range.end),\n ...(options.sourceServerSideEncryption !== undefined\n ? { sourceServerSideEncryption: options.sourceServerSideEncryption }\n : {}),\n ...(options.destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption: options.destinationServerSideEncryption }\n : {}),\n })\n partSha1s[range.partNumber - 1] = resp.contentSha1\n } finally {\n sem.release()\n }\n }),\n )\n\n options.signal?.throwIfAborted()\n return await raw.finishLargeFile(accountInfo.getApiUrl(), accountInfo.getAuthToken(), {\n fileId: largeFileId,\n partSha1Array: partSha1s,\n })\n } catch (err) {\n await cancelLargeFileBestEffort(raw, accountInfo, largeFileId)\n throw err\n }\n}\n"],"names":["fileIdOf"],"mappings":";;;;;AAyDA,eAAsB,cACpB,KACA,aACA,SACsB;AACtB,QAAM,sBAAsB,YAAY,uBAAA;AACxC,QAAM,cAAc,YAAY,2BAAA;AAChC,QAAM,WAAW,KAAK,IAAI,QAAQ,YAAY,qBAAqB,WAAW;AAC9E,QAAM,cAAc,QAAQ,eAAe;AAG3C,QAAM,aAAa,MAAM,IAAI,YAAY,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,IAC5F,QAAQ,QAAQ;AAAA,EAAA,CACjB;AACD,QAAM,YAAY,WAAW;AAG7B,MAAI,aAAa,UAAU;AACzB,WAAO,IAAI,SAAS,YAAY,aAAa,YAAY,gBAAgB;AAAA,MACvE,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ;AAAA,MAClB,GAAI,QAAQ,wBAAwB,SAChC,EAAE,qBAAqB,QAAQ,oBAAA,IAC/B,CAAA;AAAA,MACJ,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,YAAA,IAAgB,CAAA;AAAA,MAC/E,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,SAAA,IAAa,CAAA;AAAA,MACtE,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,iCAAiC,QAAQ,gCAAA,IAC3C,CAAA;AAAA,MACJ,GAAI,QAAQ,+BAA+B,SACvC,EAAE,4BAA4B,QAAQ,+BACtC,CAAA;AAAA,IAAC,CACN;AAAA,EACH;AAIA,QAAM,eAAe,QAAQ,uBAAuB,WAAW;AAG/D,QAAM,YAAY,MAAM,IAAI,eAAe,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,IAC9F,UAAU;AAAA,IACV,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ,eAAe,WAAW,eAAe;AAAA,IAC9D,UAAU,QAAQ,YAAY,CAAA;AAAA,IAC9B,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,sBAAsB,QAAQ,oCAChC,CAAA;AAAA,EAAC,CACN;AACD,QAAM,cAAc,UAAU;AAE9B,QAAM,SAAS,WAAW,WAAW,QAAQ;AAC7C,QAAM,YAAsB,IAAI,MAAM,OAAO,MAAM;AACnD,QAAM,MAAM,IAAI,UAAU,WAAW;AAErC,MAAI;AACF,YAAQ,QAAQ,eAAA;AAChB,UAAM,QAAQ;AAAA,MACZ,OAAO,IAAI,OAAO,UAAU;AAC1B,cAAM,IAAI,QAAA;AACV,YAAI;AAGF,kBAAQ,QAAQ,eAAA;AAChB,gBAAM,OAAO,MAAM,IAAI,SAAS,YAAY,UAAA,GAAa,YAAY,gBAAgB;AAAA,YACnF,cAAc,QAAQ;AAAA;AAAA;AAAA,YAGtB,aAAaA,OAAS,WAAW;AAAA,YACjC,YAAY,MAAM;AAAA,YAClB,OAAO,gBAAgB,MAAM,OAAO,MAAM,GAAG;AAAA,YAC7C,GAAI,QAAQ,+BAA+B,SACvC,EAAE,4BAA4B,QAAQ,2BAAA,IACtC,CAAA;AAAA,YACJ,GAAI,QAAQ,oCAAoC,SAC5C,EAAE,iCAAiC,QAAQ,oCAC3C,CAAA;AAAA,UAAC,CACN;AACD,oBAAU,MAAM,aAAa,CAAC,IAAI,KAAK;AAAA,QACzC,UAAA;AACE,cAAI,QAAA;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IAAA;AAGH,YAAQ,QAAQ,eAAA;AAChB,WAAO,MAAM,IAAI,gBAAgB,YAAY,aAAa,YAAY,gBAAgB;AAAA,MACpF,QAAQ;AAAA,MACR,eAAe;AAAA,IAAA,CAChB;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,0BAA0B,KAAK,aAAa,WAAW;AAC7D,UAAM;AAAA,EACR;AACF;"}
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const retry = require("../http/retry.cjs");
4
+ const collect = require("../streams/collect.cjs");
5
+ const defaults = require("../util/defaults.cjs");
6
+ const planRanges = require("../util/plan-ranges.cjs");
7
+ function createParallelDownloadStream(raw, accountInfo, options) {
8
+ const rangeSize = options.rangeSize ?? 10 * 1024 * 1024;
9
+ const concurrency = options.concurrency ?? defaults.DEFAULT_TRANSFER_CONCURRENCY;
10
+ const totalSize = options.totalSize;
11
+ const retryOptions = {
12
+ ...retry.DEFAULT_RETRY_OPTIONS,
13
+ ...options.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
14
+ };
15
+ const abort = options.signal;
16
+ const ranges = planRanges.planRanges(totalSize, rangeSize);
17
+ const windowSize = concurrency * 2;
18
+ const inflight = /* @__PURE__ */ new Map();
19
+ const buffer = /* @__PURE__ */ new Map();
20
+ let nextToSchedule = 0;
21
+ let nextToEmit = 0;
22
+ let firstError = null;
23
+ function scheduleNext() {
24
+ while (firstError === null && // Honour abort here so a completed range that triggers a top-up
25
+ // doesn't queue one final fetch after the caller aborted. Without
26
+ // this gate, one extra range request fires post-abort before the
27
+ // `pull()` loop notices.
28
+ abort?.aborted !== true && nextToSchedule < ranges.length && inflight.size + buffer.size < windowSize) {
29
+ const range = ranges[nextToSchedule];
30
+ if (range === void 0) break;
31
+ const idx = nextToSchedule;
32
+ nextToSchedule++;
33
+ const task = (async () => {
34
+ try {
35
+ const data = await fetchRangeWithRetry(
36
+ raw,
37
+ accountInfo,
38
+ options.fileId,
39
+ range.start,
40
+ range.end,
41
+ retryOptions,
42
+ abort
43
+ );
44
+ buffer.set(idx, data);
45
+ } catch (err) {
46
+ if (firstError === null) firstError = err;
47
+ } finally {
48
+ inflight.delete(idx);
49
+ }
50
+ })();
51
+ inflight.set(idx, task);
52
+ }
53
+ }
54
+ return new ReadableStream({
55
+ start(controller) {
56
+ try {
57
+ abort?.throwIfAborted();
58
+ scheduleNext();
59
+ } catch (err) {
60
+ controller.error(err);
61
+ }
62
+ },
63
+ async pull(controller) {
64
+ try {
65
+ while (!buffer.has(nextToEmit)) {
66
+ abort?.throwIfAborted();
67
+ if (firstError !== null) throw firstError;
68
+ if (inflight.size === 0) {
69
+ controller.close();
70
+ return;
71
+ }
72
+ await Promise.race(inflight.values());
73
+ }
74
+ const data = buffer.get(nextToEmit);
75
+ if (data !== void 0) {
76
+ buffer.delete(nextToEmit);
77
+ nextToEmit++;
78
+ controller.enqueue(data);
79
+ }
80
+ scheduleNext();
81
+ if (nextToEmit >= ranges.length && buffer.size === 0 && inflight.size === 0 && firstError === null) {
82
+ controller.close();
83
+ }
84
+ } catch (err) {
85
+ controller.error(err);
86
+ }
87
+ },
88
+ cancel() {
89
+ buffer.clear();
90
+ }
91
+ });
92
+ }
93
+ async function fetchRangeWithRetry(raw, accountInfo, fileId, start, end, retryOptions, signal) {
94
+ let lastError;
95
+ for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {
96
+ if (attempt > 0) {
97
+ const delay = retry.computeBackoff(attempt - 1, retryOptions);
98
+ await retry.sleep(delay, signal);
99
+ }
100
+ try {
101
+ signal?.throwIfAborted();
102
+ const resp = await raw.downloadFileById(
103
+ accountInfo.getDownloadUrl(),
104
+ accountInfo.getAuthToken(),
105
+ fileId,
106
+ {
107
+ range: planRanges.byteRangeHeader(start, end),
108
+ ...signal !== void 0 ? { signal } : {}
109
+ }
110
+ );
111
+ if (!resp.body) throw new Error("Download chunk has no body");
112
+ return await collect.collectStream(resp.body);
113
+ } catch (err) {
114
+ lastError = err;
115
+ if (signal?.aborted) throw err;
116
+ if (err instanceof DOMException && err.name === "AbortError") throw err;
117
+ }
118
+ }
119
+ throw lastError instanceof Error ? lastError : new Error("Range download failed after retries");
120
+ }
121
+ exports.createParallelDownloadStream = createParallelDownloadStream;
122
+ //# sourceMappingURL=parallel.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel.cjs","sources":["../../src/download/parallel.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport { DEFAULT_RETRY_OPTIONS, computeBackoff, sleep } from '../http/retry.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { collectStream } from '../streams/collect.ts'\nimport type { FileId } from '../types/ids.ts'\nimport { DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { byteRangeHeader, planRanges } from '../util/plan-ranges.ts'\n\n/** Options for downloading a file using concurrent byte-range requests. */\nexport interface ParallelDownloadOptions {\n /** ID of the file to download. */\n readonly fileId: FileId\n /** Total file size in bytes (must be known in advance). */\n readonly totalSize: number\n /** Size of each ranged chunk in bytes. Defaults to 10 MB. */\n readonly rangeSize?: number\n /** Maximum number of chunks fetched in parallel. Defaults to 4. */\n readonly concurrency?: number\n /** Maximum retry attempts per range on transient failures. Defaults to 5. */\n readonly maxRetries?: number\n /** Signal to abort the download. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Creates a readable stream that downloads a file using parallel byte-range requests.\n *\n * The file is split into fixed-size ranges fetched in a **sliding\n * window** keyed off the consumer's read pace. The window size is\n * `concurrency * 2`: up to `concurrency` ranges are in flight at once,\n * plus up to `concurrency` completed-but-not-yet-emitted ranges buffered\n * ahead of the read head. New fetches kick off only when the consumer\n * reads a chunk, so a slow downstream pipe (e.g. a saturated network\n * sink or a `pipeTo` consumer that drains slowly) backpressures into\n * the SDK and bounds peak memory to `(concurrency * 2) * rangeSize`.\n *\n * The previous eager implementation scheduled every range into\n * `Promise.all` up front; a slow head-of-line range could hold the\n * entire file in memory while later ranges finished and waited.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param options - Parallel download parameters (file ID, size, concurrency).\n *\n * @returns A `ReadableStream` that yields file bytes in order.\n */\nexport function createParallelDownloadStream(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: ParallelDownloadOptions,\n): ReadableStream<Uint8Array> {\n const rangeSize = options.rangeSize ?? 10 * 1024 * 1024\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n const totalSize = options.totalSize\n // `maxRetries` is part of `DEFAULT_RETRY_OPTIONS`; only override when\n // the caller supplied an explicit value so the shared default stays\n // the single source of truth.\n const retryOptions = {\n ...DEFAULT_RETRY_OPTIONS,\n ...(options.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),\n }\n const abort = options.signal\n\n const ranges = planRanges(totalSize, rangeSize)\n\n // `inflight` holds currently-fetching range promises; `buffer` holds\n // completed ranges waiting for their slot in the emit order. Total\n // scheduled-but-not-emitted = inflight.size + buffer.size is capped\n // at `windowSize`, so peak memory is bounded regardless of any\n // single range's latency.\n const windowSize = concurrency * 2\n const inflight = new Map<number, Promise<void>>()\n const buffer = new Map<number, Uint8Array>()\n let nextToSchedule = 0\n let nextToEmit = 0\n let firstError: unknown = null\n\n function scheduleNext(): void {\n while (\n firstError === null &&\n // Honour abort here so a completed range that triggers a top-up\n // doesn't queue one final fetch after the caller aborted. Without\n // this gate, one extra range request fires post-abort before the\n // `pull()` loop notices.\n abort?.aborted !== true &&\n nextToSchedule < ranges.length &&\n inflight.size + buffer.size < windowSize\n ) {\n const range = ranges[nextToSchedule]\n if (range === undefined) break\n const idx = nextToSchedule\n nextToSchedule++\n const task = (async () => {\n try {\n const data = await fetchRangeWithRetry(\n raw,\n accountInfo,\n options.fileId,\n range.start,\n range.end,\n retryOptions,\n abort,\n )\n buffer.set(idx, data)\n } catch (err) {\n if (firstError === null) firstError = err\n } finally {\n inflight.delete(idx)\n }\n })()\n inflight.set(idx, task)\n }\n }\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n // Pre-warm: kick off the initial window so the first `pull` has\n // something to await. An already-aborted signal surfaces as a\n // stream error rather than a synchronous throw, matching the\n // contract callers had with the previous eager implementation.\n try {\n abort?.throwIfAborted()\n scheduleNext()\n } catch (err) {\n controller.error(err)\n }\n },\n async pull(controller) {\n try {\n // Wait until the next in-order chunk is available.\n while (!buffer.has(nextToEmit)) {\n abort?.throwIfAborted()\n if (firstError !== null) throw firstError\n /* v8 ignore start -- defensive close: every code path that\n populates `nextToEmit < ranges.length` also schedules into\n `inflight`. The pre-warm in `start()` plus `scheduleNext()`\n after each enqueue guarantee that whenever there are\n pending ranges, at least one is in flight. This guard\n exists only so a future refactor can't accidentally stall\n the stream by leaving both maps empty mid-stream. */\n if (inflight.size === 0) {\n controller.close()\n return\n }\n /* v8 ignore stop */\n await Promise.race(inflight.values())\n }\n\n const data = buffer.get(nextToEmit)\n if (data !== undefined) {\n buffer.delete(nextToEmit)\n nextToEmit++\n controller.enqueue(data)\n }\n\n // Top up the window now that we freed a slot by emitting.\n scheduleNext()\n\n if (\n nextToEmit >= ranges.length &&\n buffer.size === 0 &&\n inflight.size === 0 &&\n firstError === null\n ) {\n controller.close()\n }\n } catch (err) {\n controller.error(err)\n }\n },\n cancel() {\n // Abort propagation handles in-flight requests; just drop buffered\n // data so it can be GC'd promptly.\n buffer.clear()\n },\n })\n}\n\n/**\n * Fetches a single byte range with bounded retry on transient failures.\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param fileId - ID of the file being downloaded.\n * @param start - Inclusive byte offset where the range begins.\n * @param end - Inclusive byte offset where the range ends.\n * @param retryOptions - Retry settings controlling attempts and backoff.\n * @param signal - Optional abort signal that cancels the range and any pending retry.\n *\n * @returns The range's bytes, or throws after exhausting all retry attempts.\n */\nasync function fetchRangeWithRetry(\n raw: RawClient,\n accountInfo: AccountInfo,\n fileId: FileId,\n start: number,\n end: number,\n retryOptions: typeof DEFAULT_RETRY_OPTIONS,\n signal: AbortSignal | undefined,\n): Promise<Uint8Array> {\n let lastError: unknown\n for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = computeBackoff(attempt - 1, retryOptions)\n await sleep(delay, signal)\n }\n try {\n signal?.throwIfAborted()\n const resp = await raw.downloadFileById(\n accountInfo.getDownloadUrl(),\n accountInfo.getAuthToken(),\n fileId,\n {\n range: byteRangeHeader(start, end),\n ...(signal !== undefined ? { signal } : {}),\n },\n )\n if (!resp.body) throw new Error('Download chunk has no body')\n return await collectStream(resp.body)\n } catch (err) {\n lastError = err\n // Honour AbortSignal cancellation: don't retry past an abort.\n if (signal?.aborted) throw err\n if (err instanceof DOMException && err.name === 'AbortError') throw err\n }\n }\n throw lastError instanceof Error ? lastError : new Error('Range download failed after retries')\n}\n"],"names":["DEFAULT_TRANSFER_CONCURRENCY","DEFAULT_RETRY_OPTIONS","planRanges","computeBackoff","sleep","byteRangeHeader","collectStream"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { AccountInfo } from '../auth/account-info.js';
2
+ import { RawClient } from '../raw/index.js';
3
+ import { FileId } from '../types/ids.js';
4
+ /** Options for downloading a file using concurrent byte-range requests. */
5
+ export interface ParallelDownloadOptions {
6
+ /** ID of the file to download. */
7
+ readonly fileId: FileId;
8
+ /** Total file size in bytes (must be known in advance). */
9
+ readonly totalSize: number;
10
+ /** Size of each ranged chunk in bytes. Defaults to 10 MB. */
11
+ readonly rangeSize?: number;
12
+ /** Maximum number of chunks fetched in parallel. Defaults to 4. */
13
+ readonly concurrency?: number;
14
+ /** Maximum retry attempts per range on transient failures. Defaults to 5. */
15
+ readonly maxRetries?: number;
16
+ /** Signal to abort the download. */
17
+ readonly signal?: AbortSignal;
18
+ }
19
+ /**
20
+ * Creates a readable stream that downloads a file using parallel byte-range requests.
21
+ *
22
+ * The file is split into fixed-size ranges fetched in a **sliding
23
+ * window** keyed off the consumer's read pace. The window size is
24
+ * `concurrency * 2`: up to `concurrency` ranges are in flight at once,
25
+ * plus up to `concurrency` completed-but-not-yet-emitted ranges buffered
26
+ * ahead of the read head. New fetches kick off only when the consumer
27
+ * reads a chunk, so a slow downstream pipe (e.g. a saturated network
28
+ * sink or a `pipeTo` consumer that drains slowly) backpressures into
29
+ * the SDK and bounds peak memory to `(concurrency * 2) * rangeSize`.
30
+ *
31
+ * The previous eager implementation scheduled every range into
32
+ * `Promise.all` up front; a slow head-of-line range could hold the
33
+ * entire file in memory while later ranges finished and waited.
34
+ *
35
+ * @param raw - Low-level B2 API client.
36
+ * @param accountInfo - Authorized account state.
37
+ * @param options - Parallel download parameters (file ID, size, concurrency).
38
+ *
39
+ * @returns A `ReadableStream` that yields file bytes in order.
40
+ */
41
+ export declare function createParallelDownloadStream(raw: RawClient, accountInfo: AccountInfo, options: ParallelDownloadOptions): ReadableStream<Uint8Array>;
42
+ //# sourceMappingURL=parallel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel.d.ts","sourceRoot":"","sources":["../../src/download/parallel.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAI7C,2EAA2E;AAC3E,MAAM,WAAW,uBAAuB;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,uBAAuB,GAC/B,cAAc,CAAC,UAAU,CAAC,CA8H5B"}
@@ -0,0 +1,42 @@
1
+ import { AccountInfo } from '../auth/account-info.js';
2
+ import { RawClient } from '../raw/index.js';
3
+ import { FileId } from '../types/ids.js';
4
+ /** Options for downloading a file using concurrent byte-range requests. */
5
+ export interface ParallelDownloadOptions {
6
+ /** ID of the file to download. */
7
+ readonly fileId: FileId;
8
+ /** Total file size in bytes (must be known in advance). */
9
+ readonly totalSize: number;
10
+ /** Size of each ranged chunk in bytes. Defaults to 10 MB. */
11
+ readonly rangeSize?: number;
12
+ /** Maximum number of chunks fetched in parallel. Defaults to 4. */
13
+ readonly concurrency?: number;
14
+ /** Maximum retry attempts per range on transient failures. Defaults to 5. */
15
+ readonly maxRetries?: number;
16
+ /** Signal to abort the download. */
17
+ readonly signal?: AbortSignal;
18
+ }
19
+ /**
20
+ * Creates a readable stream that downloads a file using parallel byte-range requests.
21
+ *
22
+ * The file is split into fixed-size ranges fetched in a **sliding
23
+ * window** keyed off the consumer's read pace. The window size is
24
+ * `concurrency * 2`: up to `concurrency` ranges are in flight at once,
25
+ * plus up to `concurrency` completed-but-not-yet-emitted ranges buffered
26
+ * ahead of the read head. New fetches kick off only when the consumer
27
+ * reads a chunk, so a slow downstream pipe (e.g. a saturated network
28
+ * sink or a `pipeTo` consumer that drains slowly) backpressures into
29
+ * the SDK and bounds peak memory to `(concurrency * 2) * rangeSize`.
30
+ *
31
+ * The previous eager implementation scheduled every range into
32
+ * `Promise.all` up front; a slow head-of-line range could hold the
33
+ * entire file in memory while later ranges finished and waited.
34
+ *
35
+ * @param raw - Low-level B2 API client.
36
+ * @param accountInfo - Authorized account state.
37
+ * @param options - Parallel download parameters (file ID, size, concurrency).
38
+ *
39
+ * @returns A `ReadableStream` that yields file bytes in order.
40
+ */
41
+ export declare function createParallelDownloadStream(raw: RawClient, accountInfo: AccountInfo, options: ParallelDownloadOptions): ReadableStream<Uint8Array>;
42
+ //# sourceMappingURL=parallel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel.d.ts","sourceRoot":"","sources":["../../src/download/parallel.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAI7C,2EAA2E;AAC3E,MAAM,WAAW,uBAAuB;IACtC,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,uBAAuB,GAC/B,cAAc,CAAC,UAAU,CAAC,CA8H5B"}
@@ -0,0 +1,122 @@
1
+ import { DEFAULT_RETRY_OPTIONS, computeBackoff, sleep } from "../http/retry.js";
2
+ import { collectStream } from "../streams/collect.js";
3
+ import { DEFAULT_TRANSFER_CONCURRENCY } from "../util/defaults.js";
4
+ import { planRanges, byteRangeHeader } from "../util/plan-ranges.js";
5
+ function createParallelDownloadStream(raw, accountInfo, options) {
6
+ const rangeSize = options.rangeSize ?? 10 * 1024 * 1024;
7
+ const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY;
8
+ const totalSize = options.totalSize;
9
+ const retryOptions = {
10
+ ...DEFAULT_RETRY_OPTIONS,
11
+ ...options.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
12
+ };
13
+ const abort = options.signal;
14
+ const ranges = planRanges(totalSize, rangeSize);
15
+ const windowSize = concurrency * 2;
16
+ const inflight = /* @__PURE__ */ new Map();
17
+ const buffer = /* @__PURE__ */ new Map();
18
+ let nextToSchedule = 0;
19
+ let nextToEmit = 0;
20
+ let firstError = null;
21
+ function scheduleNext() {
22
+ while (firstError === null && // Honour abort here so a completed range that triggers a top-up
23
+ // doesn't queue one final fetch after the caller aborted. Without
24
+ // this gate, one extra range request fires post-abort before the
25
+ // `pull()` loop notices.
26
+ abort?.aborted !== true && nextToSchedule < ranges.length && inflight.size + buffer.size < windowSize) {
27
+ const range = ranges[nextToSchedule];
28
+ if (range === void 0) break;
29
+ const idx = nextToSchedule;
30
+ nextToSchedule++;
31
+ const task = (async () => {
32
+ try {
33
+ const data = await fetchRangeWithRetry(
34
+ raw,
35
+ accountInfo,
36
+ options.fileId,
37
+ range.start,
38
+ range.end,
39
+ retryOptions,
40
+ abort
41
+ );
42
+ buffer.set(idx, data);
43
+ } catch (err) {
44
+ if (firstError === null) firstError = err;
45
+ } finally {
46
+ inflight.delete(idx);
47
+ }
48
+ })();
49
+ inflight.set(idx, task);
50
+ }
51
+ }
52
+ return new ReadableStream({
53
+ start(controller) {
54
+ try {
55
+ abort?.throwIfAborted();
56
+ scheduleNext();
57
+ } catch (err) {
58
+ controller.error(err);
59
+ }
60
+ },
61
+ async pull(controller) {
62
+ try {
63
+ while (!buffer.has(nextToEmit)) {
64
+ abort?.throwIfAborted();
65
+ if (firstError !== null) throw firstError;
66
+ if (inflight.size === 0) {
67
+ controller.close();
68
+ return;
69
+ }
70
+ await Promise.race(inflight.values());
71
+ }
72
+ const data = buffer.get(nextToEmit);
73
+ if (data !== void 0) {
74
+ buffer.delete(nextToEmit);
75
+ nextToEmit++;
76
+ controller.enqueue(data);
77
+ }
78
+ scheduleNext();
79
+ if (nextToEmit >= ranges.length && buffer.size === 0 && inflight.size === 0 && firstError === null) {
80
+ controller.close();
81
+ }
82
+ } catch (err) {
83
+ controller.error(err);
84
+ }
85
+ },
86
+ cancel() {
87
+ buffer.clear();
88
+ }
89
+ });
90
+ }
91
+ async function fetchRangeWithRetry(raw, accountInfo, fileId, start, end, retryOptions, signal) {
92
+ let lastError;
93
+ for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {
94
+ if (attempt > 0) {
95
+ const delay = computeBackoff(attempt - 1, retryOptions);
96
+ await sleep(delay, signal);
97
+ }
98
+ try {
99
+ signal?.throwIfAborted();
100
+ const resp = await raw.downloadFileById(
101
+ accountInfo.getDownloadUrl(),
102
+ accountInfo.getAuthToken(),
103
+ fileId,
104
+ {
105
+ range: byteRangeHeader(start, end),
106
+ ...signal !== void 0 ? { signal } : {}
107
+ }
108
+ );
109
+ if (!resp.body) throw new Error("Download chunk has no body");
110
+ return await collectStream(resp.body);
111
+ } catch (err) {
112
+ lastError = err;
113
+ if (signal?.aborted) throw err;
114
+ if (err instanceof DOMException && err.name === "AbortError") throw err;
115
+ }
116
+ }
117
+ throw lastError instanceof Error ? lastError : new Error("Range download failed after retries");
118
+ }
119
+ export {
120
+ createParallelDownloadStream
121
+ };
122
+ //# sourceMappingURL=parallel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel.js","sources":["../../src/download/parallel.ts"],"sourcesContent":["import type { AccountInfo } from '../auth/account-info.ts'\nimport { DEFAULT_RETRY_OPTIONS, computeBackoff, sleep } from '../http/retry.ts'\nimport type { RawClient } from '../raw/index.ts'\nimport { collectStream } from '../streams/collect.ts'\nimport type { FileId } from '../types/ids.ts'\nimport { DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { byteRangeHeader, planRanges } from '../util/plan-ranges.ts'\n\n/** Options for downloading a file using concurrent byte-range requests. */\nexport interface ParallelDownloadOptions {\n /** ID of the file to download. */\n readonly fileId: FileId\n /** Total file size in bytes (must be known in advance). */\n readonly totalSize: number\n /** Size of each ranged chunk in bytes. Defaults to 10 MB. */\n readonly rangeSize?: number\n /** Maximum number of chunks fetched in parallel. Defaults to 4. */\n readonly concurrency?: number\n /** Maximum retry attempts per range on transient failures. Defaults to 5. */\n readonly maxRetries?: number\n /** Signal to abort the download. */\n readonly signal?: AbortSignal\n}\n\n/**\n * Creates a readable stream that downloads a file using parallel byte-range requests.\n *\n * The file is split into fixed-size ranges fetched in a **sliding\n * window** keyed off the consumer's read pace. The window size is\n * `concurrency * 2`: up to `concurrency` ranges are in flight at once,\n * plus up to `concurrency` completed-but-not-yet-emitted ranges buffered\n * ahead of the read head. New fetches kick off only when the consumer\n * reads a chunk, so a slow downstream pipe (e.g. a saturated network\n * sink or a `pipeTo` consumer that drains slowly) backpressures into\n * the SDK and bounds peak memory to `(concurrency * 2) * rangeSize`.\n *\n * The previous eager implementation scheduled every range into\n * `Promise.all` up front; a slow head-of-line range could hold the\n * entire file in memory while later ranges finished and waited.\n *\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param options - Parallel download parameters (file ID, size, concurrency).\n *\n * @returns A `ReadableStream` that yields file bytes in order.\n */\nexport function createParallelDownloadStream(\n raw: RawClient,\n accountInfo: AccountInfo,\n options: ParallelDownloadOptions,\n): ReadableStream<Uint8Array> {\n const rangeSize = options.rangeSize ?? 10 * 1024 * 1024\n const concurrency = options.concurrency ?? DEFAULT_TRANSFER_CONCURRENCY\n const totalSize = options.totalSize\n // `maxRetries` is part of `DEFAULT_RETRY_OPTIONS`; only override when\n // the caller supplied an explicit value so the shared default stays\n // the single source of truth.\n const retryOptions = {\n ...DEFAULT_RETRY_OPTIONS,\n ...(options.maxRetries !== undefined ? { maxRetries: options.maxRetries } : {}),\n }\n const abort = options.signal\n\n const ranges = planRanges(totalSize, rangeSize)\n\n // `inflight` holds currently-fetching range promises; `buffer` holds\n // completed ranges waiting for their slot in the emit order. Total\n // scheduled-but-not-emitted = inflight.size + buffer.size is capped\n // at `windowSize`, so peak memory is bounded regardless of any\n // single range's latency.\n const windowSize = concurrency * 2\n const inflight = new Map<number, Promise<void>>()\n const buffer = new Map<number, Uint8Array>()\n let nextToSchedule = 0\n let nextToEmit = 0\n let firstError: unknown = null\n\n function scheduleNext(): void {\n while (\n firstError === null &&\n // Honour abort here so a completed range that triggers a top-up\n // doesn't queue one final fetch after the caller aborted. Without\n // this gate, one extra range request fires post-abort before the\n // `pull()` loop notices.\n abort?.aborted !== true &&\n nextToSchedule < ranges.length &&\n inflight.size + buffer.size < windowSize\n ) {\n const range = ranges[nextToSchedule]\n if (range === undefined) break\n const idx = nextToSchedule\n nextToSchedule++\n const task = (async () => {\n try {\n const data = await fetchRangeWithRetry(\n raw,\n accountInfo,\n options.fileId,\n range.start,\n range.end,\n retryOptions,\n abort,\n )\n buffer.set(idx, data)\n } catch (err) {\n if (firstError === null) firstError = err\n } finally {\n inflight.delete(idx)\n }\n })()\n inflight.set(idx, task)\n }\n }\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n // Pre-warm: kick off the initial window so the first `pull` has\n // something to await. An already-aborted signal surfaces as a\n // stream error rather than a synchronous throw, matching the\n // contract callers had with the previous eager implementation.\n try {\n abort?.throwIfAborted()\n scheduleNext()\n } catch (err) {\n controller.error(err)\n }\n },\n async pull(controller) {\n try {\n // Wait until the next in-order chunk is available.\n while (!buffer.has(nextToEmit)) {\n abort?.throwIfAborted()\n if (firstError !== null) throw firstError\n /* v8 ignore start -- defensive close: every code path that\n populates `nextToEmit < ranges.length` also schedules into\n `inflight`. The pre-warm in `start()` plus `scheduleNext()`\n after each enqueue guarantee that whenever there are\n pending ranges, at least one is in flight. This guard\n exists only so a future refactor can't accidentally stall\n the stream by leaving both maps empty mid-stream. */\n if (inflight.size === 0) {\n controller.close()\n return\n }\n /* v8 ignore stop */\n await Promise.race(inflight.values())\n }\n\n const data = buffer.get(nextToEmit)\n if (data !== undefined) {\n buffer.delete(nextToEmit)\n nextToEmit++\n controller.enqueue(data)\n }\n\n // Top up the window now that we freed a slot by emitting.\n scheduleNext()\n\n if (\n nextToEmit >= ranges.length &&\n buffer.size === 0 &&\n inflight.size === 0 &&\n firstError === null\n ) {\n controller.close()\n }\n } catch (err) {\n controller.error(err)\n }\n },\n cancel() {\n // Abort propagation handles in-flight requests; just drop buffered\n // data so it can be GC'd promptly.\n buffer.clear()\n },\n })\n}\n\n/**\n * Fetches a single byte range with bounded retry on transient failures.\n * @param raw - Low-level B2 API client.\n * @param accountInfo - Authorized account state.\n * @param fileId - ID of the file being downloaded.\n * @param start - Inclusive byte offset where the range begins.\n * @param end - Inclusive byte offset where the range ends.\n * @param retryOptions - Retry settings controlling attempts and backoff.\n * @param signal - Optional abort signal that cancels the range and any pending retry.\n *\n * @returns The range's bytes, or throws after exhausting all retry attempts.\n */\nasync function fetchRangeWithRetry(\n raw: RawClient,\n accountInfo: AccountInfo,\n fileId: FileId,\n start: number,\n end: number,\n retryOptions: typeof DEFAULT_RETRY_OPTIONS,\n signal: AbortSignal | undefined,\n): Promise<Uint8Array> {\n let lastError: unknown\n for (let attempt = 0; attempt <= retryOptions.maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = computeBackoff(attempt - 1, retryOptions)\n await sleep(delay, signal)\n }\n try {\n signal?.throwIfAborted()\n const resp = await raw.downloadFileById(\n accountInfo.getDownloadUrl(),\n accountInfo.getAuthToken(),\n fileId,\n {\n range: byteRangeHeader(start, end),\n ...(signal !== undefined ? { signal } : {}),\n },\n )\n if (!resp.body) throw new Error('Download chunk has no body')\n return await collectStream(resp.body)\n } catch (err) {\n lastError = err\n // Honour AbortSignal cancellation: don't retry past an abort.\n if (signal?.aborted) throw err\n if (err instanceof DOMException && err.name === 'AbortError') throw err\n }\n }\n throw lastError instanceof Error ? lastError : new Error('Range download failed after retries')\n}\n"],"names":[],"mappings":"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"}