@backblaze-labs/b2-sdk 0.1.0 → 0.2.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 (701) hide show
  1. package/CHANGELOG.md +110 -5
  2. package/README.md +144 -33
  3. package/dist/_virtual/_b2-sdk-version-json.cjs +4 -0
  4. package/dist/_virtual/_b2-sdk-version-json.js +4 -0
  5. package/dist/auth/account-info.d.cts +32 -0
  6. package/dist/auth/account-info.d.cts.map +1 -1
  7. package/dist/auth/account-info.d.ts +32 -0
  8. package/dist/auth/account-info.d.ts.map +1 -1
  9. package/dist/auth/file.cjs +408 -207
  10. package/dist/auth/file.cjs.map +1 -1
  11. package/dist/auth/file.d.cts +77 -3
  12. package/dist/auth/file.d.cts.map +1 -1
  13. package/dist/auth/file.d.ts +77 -3
  14. package/dist/auth/file.d.ts.map +1 -1
  15. package/dist/auth/file.js +407 -207
  16. package/dist/auth/file.js.map +1 -1
  17. package/dist/auth/in-memory.cjs +212 -183
  18. package/dist/auth/in-memory.cjs.map +1 -1
  19. package/dist/auth/in-memory.d.cts +8 -0
  20. package/dist/auth/in-memory.d.cts.map +1 -1
  21. package/dist/auth/in-memory.d.ts +8 -0
  22. package/dist/auth/in-memory.d.ts.map +1 -1
  23. package/dist/auth/in-memory.js +211 -182
  24. package/dist/auth/in-memory.js.map +1 -1
  25. package/dist/auth/index.cjs +7 -9
  26. package/dist/auth/index.d.cts +3 -2
  27. package/dist/auth/index.d.cts.map +1 -1
  28. package/dist/auth/index.d.ts +3 -2
  29. package/dist/auth/index.d.ts.map +1 -1
  30. package/dist/auth/index.js +2 -8
  31. package/dist/auth/realms.cjs +80 -7
  32. package/dist/auth/realms.cjs.map +1 -1
  33. package/dist/auth/realms.d.cts +27 -4
  34. package/dist/auth/realms.d.cts.map +1 -1
  35. package/dist/auth/realms.d.ts +27 -4
  36. package/dist/auth/realms.d.ts.map +1 -1
  37. package/dist/auth/realms.js +80 -9
  38. package/dist/auth/realms.js.map +1 -1
  39. package/dist/auth/upload-url-pool.cjs +55 -51
  40. package/dist/auth/upload-url-pool.cjs.map +1 -1
  41. package/dist/auth/upload-url-pool.js +55 -51
  42. package/dist/auth/upload-url-pool.js.map +1 -1
  43. package/dist/bucket.cjs +785 -787
  44. package/dist/bucket.cjs.map +1 -1
  45. package/dist/bucket.d.cts +35 -47
  46. package/dist/bucket.d.cts.map +1 -1
  47. package/dist/bucket.d.ts +35 -47
  48. package/dist/bucket.d.ts.map +1 -1
  49. package/dist/bucket.js +780 -782
  50. package/dist/bucket.js.map +1 -1
  51. package/dist/client.cjs +246 -228
  52. package/dist/client.cjs.map +1 -1
  53. package/dist/client.d.cts +25 -17
  54. package/dist/client.d.cts.map +1 -1
  55. package/dist/client.d.ts +25 -17
  56. package/dist/client.d.ts.map +1 -1
  57. package/dist/client.js +243 -225
  58. package/dist/client.js.map +1 -1
  59. package/dist/copy/large.cjs +114 -71
  60. package/dist/copy/large.cjs.map +1 -1
  61. package/dist/copy/large.d.cts +6 -0
  62. package/dist/copy/large.d.cts.map +1 -1
  63. package/dist/copy/large.d.ts +6 -0
  64. package/dist/copy/large.d.ts.map +1 -1
  65. package/dist/copy/large.js +112 -69
  66. package/dist/copy/large.js.map +1 -1
  67. package/dist/download/checksum.cjs +89 -0
  68. package/dist/download/checksum.cjs.map +1 -0
  69. package/dist/download/checksum.d.cts +40 -0
  70. package/dist/download/checksum.d.cts.map +1 -0
  71. package/dist/download/checksum.d.ts +40 -0
  72. package/dist/download/checksum.d.ts.map +1 -0
  73. package/dist/download/checksum.js +87 -0
  74. package/dist/download/checksum.js.map +1 -0
  75. package/dist/download/parallel.cjs +214 -118
  76. package/dist/download/parallel.cjs.map +1 -1
  77. package/dist/download/parallel.d.cts +5 -1
  78. package/dist/download/parallel.d.cts.map +1 -1
  79. package/dist/download/parallel.d.ts +5 -1
  80. package/dist/download/parallel.d.ts.map +1 -1
  81. package/dist/download/parallel.js +212 -116
  82. package/dist/download/parallel.js.map +1 -1
  83. package/dist/download/single.cjs +188 -97
  84. package/dist/download/single.cjs.map +1 -1
  85. package/dist/download/single.d.cts +6 -1
  86. package/dist/download/single.d.cts.map +1 -1
  87. package/dist/download/single.d.ts +6 -1
  88. package/dist/download/single.d.ts.map +1 -1
  89. package/dist/download/single.js +186 -98
  90. package/dist/download/single.js.map +1 -1
  91. package/dist/errors/index.cjs +699 -243
  92. package/dist/errors/index.cjs.map +1 -1
  93. package/dist/errors/index.d.cts +285 -82
  94. package/dist/errors/index.d.cts.map +1 -1
  95. package/dist/errors/index.d.ts +285 -82
  96. package/dist/errors/index.d.ts.map +1 -1
  97. package/dist/errors/index.js +678 -261
  98. package/dist/errors/index.js.map +1 -1
  99. package/dist/http/retry.cjs +45 -28
  100. package/dist/http/retry.cjs.map +1 -1
  101. package/dist/http/retry.d.cts +15 -2
  102. package/dist/http/retry.d.cts.map +1 -1
  103. package/dist/http/retry.d.ts +15 -2
  104. package/dist/http/retry.d.ts.map +1 -1
  105. package/dist/http/retry.js +46 -31
  106. package/dist/http/retry.js.map +1 -1
  107. package/dist/http/transport.cjs +438 -140
  108. package/dist/http/transport.cjs.map +1 -1
  109. package/dist/http/transport.d.cts +21 -2
  110. package/dist/http/transport.d.cts.map +1 -1
  111. package/dist/http/transport.d.ts +21 -2
  112. package/dist/http/transport.d.ts.map +1 -1
  113. package/dist/http/transport.js +437 -140
  114. package/dist/http/transport.js.map +1 -1
  115. package/dist/http/url-guard.cjs +154 -89
  116. package/dist/http/url-guard.cjs.map +1 -1
  117. package/dist/http/url-guard.d.cts +25 -3
  118. package/dist/http/url-guard.d.cts.map +1 -1
  119. package/dist/http/url-guard.d.ts +25 -3
  120. package/dist/http/url-guard.d.ts.map +1 -1
  121. package/dist/http/url-guard.js +152 -90
  122. package/dist/http/url-guard.js.map +1 -1
  123. package/dist/http/user-agent.cjs +84 -44
  124. package/dist/http/user-agent.cjs.map +1 -1
  125. package/dist/http/user-agent.js +84 -46
  126. package/dist/http/user-agent.js.map +1 -1
  127. package/dist/index.cjs +101 -77
  128. package/dist/index.d.cts +27 -21
  129. package/dist/index.d.cts.map +1 -1
  130. package/dist/index.d.ts +27 -21
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +15 -71
  133. package/dist/internal/b2-naming.cjs +66 -0
  134. package/dist/internal/b2-naming.cjs.map +1 -0
  135. package/dist/internal/b2-naming.d.cts +43 -0
  136. package/dist/internal/b2-naming.d.cts.map +1 -0
  137. package/dist/internal/b2-naming.d.ts +43 -0
  138. package/dist/internal/b2-naming.d.ts.map +1 -0
  139. package/dist/internal/b2-naming.js +60 -0
  140. package/dist/internal/b2-naming.js.map +1 -0
  141. package/dist/internal/upload-retry-options.cjs +21 -0
  142. package/dist/internal/upload-retry-options.cjs.map +1 -0
  143. package/dist/internal/upload-retry-options.d.cts +12 -0
  144. package/dist/internal/upload-retry-options.d.cts.map +1 -0
  145. package/dist/internal/upload-retry-options.d.ts +12 -0
  146. package/dist/internal/upload-retry-options.d.ts.map +1 -0
  147. package/dist/internal/upload-retry-options.js +21 -0
  148. package/dist/internal/upload-retry-options.js.map +1 -0
  149. package/dist/internal/url-redaction.cjs +31 -0
  150. package/dist/internal/url-redaction.cjs.map +1 -0
  151. package/dist/internal/url-redaction.d.cts +17 -0
  152. package/dist/internal/url-redaction.d.cts.map +1 -0
  153. package/dist/internal/url-redaction.d.ts +17 -0
  154. package/dist/internal/url-redaction.d.ts.map +1 -0
  155. package/dist/internal/url-redaction.js +31 -0
  156. package/dist/internal/url-redaction.js.map +1 -0
  157. package/dist/notifications/index.cjs +4 -6
  158. package/dist/notifications/index.d.cts +1 -1
  159. package/dist/notifications/index.d.cts.map +1 -1
  160. package/dist/notifications/index.d.ts +1 -1
  161. package/dist/notifications/index.d.ts.map +1 -1
  162. package/dist/notifications/index.js +1 -6
  163. package/dist/notifications/webhook.cjs +195 -85
  164. package/dist/notifications/webhook.cjs.map +1 -1
  165. package/dist/notifications/webhook.d.cts.map +1 -1
  166. package/dist/notifications/webhook.d.ts.map +1 -1
  167. package/dist/notifications/webhook.js +195 -87
  168. package/dist/notifications/webhook.js.map +1 -1
  169. package/dist/object.cjs +253 -210
  170. package/dist/object.cjs.map +1 -1
  171. package/dist/object.d.cts +9 -55
  172. package/dist/object.d.cts.map +1 -1
  173. package/dist/object.d.ts +9 -55
  174. package/dist/object.d.ts.map +1 -1
  175. package/dist/object.js +249 -206
  176. package/dist/object.js.map +1 -1
  177. package/dist/raw/encoding.cjs +74 -35
  178. package/dist/raw/encoding.cjs.map +1 -1
  179. package/dist/raw/encoding.js +74 -38
  180. package/dist/raw/encoding.js.map +1 -1
  181. package/dist/raw/index.cjs +678 -587
  182. package/dist/raw/index.cjs.map +1 -1
  183. package/dist/raw/index.d.cts +121 -18
  184. package/dist/raw/index.d.cts.map +1 -1
  185. package/dist/raw/index.d.ts +121 -18
  186. package/dist/raw/index.d.ts.map +1 -1
  187. package/dist/raw/index.js +674 -589
  188. package/dist/raw/index.js.map +1 -1
  189. package/dist/s3/index.cjs +355 -18
  190. package/dist/s3/index.cjs.map +1 -1
  191. package/dist/s3/index.d.cts +233 -5
  192. package/dist/s3/index.d.cts.map +1 -1
  193. package/dist/s3/index.d.ts +233 -5
  194. package/dist/s3/index.d.ts.map +1 -1
  195. package/dist/s3/index.js +351 -22
  196. package/dist/s3/index.js.map +1 -1
  197. package/dist/s3/sigv4.cjs +149 -0
  198. package/dist/s3/sigv4.cjs.map +1 -0
  199. package/dist/s3/sigv4.d.cts +43 -0
  200. package/dist/s3/sigv4.d.cts.map +1 -0
  201. package/dist/s3/sigv4.d.ts +43 -0
  202. package/dist/s3/sigv4.d.ts.map +1 -0
  203. package/dist/s3/sigv4.js +148 -0
  204. package/dist/s3/sigv4.js.map +1 -0
  205. package/dist/s3/validation.cjs +69 -0
  206. package/dist/s3/validation.cjs.map +1 -0
  207. package/dist/s3/validation.d.cts +43 -0
  208. package/dist/s3/validation.d.cts.map +1 -0
  209. package/dist/s3/validation.d.ts +43 -0
  210. package/dist/s3/validation.d.ts.map +1 -0
  211. package/dist/s3/validation.js +66 -0
  212. package/dist/s3/validation.js.map +1 -0
  213. package/dist/simulator/capabilities.cjs +63 -48
  214. package/dist/simulator/capabilities.cjs.map +1 -1
  215. package/dist/simulator/capabilities.js +63 -49
  216. package/dist/simulator/capabilities.js.map +1 -1
  217. package/dist/simulator/index.cjs +2124 -1548
  218. package/dist/simulator/index.cjs.map +1 -1
  219. package/dist/simulator/index.d.cts +79 -11
  220. package/dist/simulator/index.d.cts.map +1 -1
  221. package/dist/simulator/index.d.ts +79 -11
  222. package/dist/simulator/index.d.ts.map +1 -1
  223. package/dist/simulator/index.js +2112 -1546
  224. package/dist/simulator/index.js.map +1 -1
  225. package/dist/simulator/validation.cjs +191 -153
  226. package/dist/simulator/validation.cjs.map +1 -1
  227. package/dist/simulator/validation.d.cts +3 -21
  228. package/dist/simulator/validation.d.cts.map +1 -1
  229. package/dist/simulator/validation.d.ts +3 -21
  230. package/dist/simulator/validation.d.ts.map +1 -1
  231. package/dist/simulator/validation.js +188 -162
  232. package/dist/simulator/validation.js.map +1 -1
  233. package/dist/streams/collect.cjs +74 -24
  234. package/dist/streams/collect.cjs.map +1 -1
  235. package/dist/streams/collect.d.cts +14 -1
  236. package/dist/streams/collect.d.cts.map +1 -1
  237. package/dist/streams/collect.d.ts +14 -1
  238. package/dist/streams/collect.d.ts.map +1 -1
  239. package/dist/streams/collect.js +74 -25
  240. package/dist/streams/collect.js.map +1 -1
  241. package/dist/streams/file-source.cjs +287 -0
  242. package/dist/streams/file-source.cjs.map +1 -0
  243. package/dist/streams/file-source.d.cts +96 -0
  244. package/dist/streams/file-source.d.cts.map +1 -0
  245. package/dist/streams/file-source.d.ts +96 -0
  246. package/dist/streams/file-source.d.ts.map +1 -0
  247. package/dist/streams/file-source.js +286 -0
  248. package/dist/streams/file-source.js.map +1 -0
  249. package/dist/streams/hash.cjs +207 -115
  250. package/dist/streams/hash.cjs.map +1 -1
  251. package/dist/streams/hash.d.cts +5 -4
  252. package/dist/streams/hash.d.cts.map +1 -1
  253. package/dist/streams/hash.d.ts +5 -4
  254. package/dist/streams/hash.d.ts.map +1 -1
  255. package/dist/streams/hash.js +208 -95
  256. package/dist/streams/hash.js.map +1 -1
  257. package/dist/streams/index.cjs +12 -12
  258. package/dist/streams/index.d.cts +3 -3
  259. package/dist/streams/index.d.cts.map +1 -1
  260. package/dist/streams/index.d.ts +3 -3
  261. package/dist/streams/index.d.ts.map +1 -1
  262. package/dist/streams/index.js +2 -10
  263. package/dist/streams/progress.cjs +60 -46
  264. package/dist/streams/progress.cjs.map +1 -1
  265. package/dist/streams/progress.js +60 -46
  266. package/dist/streams/progress.js.map +1 -1
  267. package/dist/streams/source.cjs +316 -146
  268. package/dist/streams/source.cjs.map +1 -1
  269. package/dist/streams/source.d.cts +56 -18
  270. package/dist/streams/source.d.cts.map +1 -1
  271. package/dist/streams/source.d.ts +56 -18
  272. package/dist/streams/source.d.ts.map +1 -1
  273. package/dist/streams/source.js +312 -149
  274. package/dist/streams/source.js.map +1 -1
  275. package/dist/sync/actions/index.cjs +236 -184
  276. package/dist/sync/actions/index.cjs.map +1 -1
  277. package/dist/sync/actions/index.d.cts +29 -21
  278. package/dist/sync/actions/index.d.cts.map +1 -1
  279. package/dist/sync/actions/index.d.ts +29 -21
  280. package/dist/sync/actions/index.d.ts.map +1 -1
  281. package/dist/sync/actions/index.js +236 -190
  282. package/dist/sync/actions/index.js.map +1 -1
  283. package/dist/sync/b2-sha1-reader.cjs +148 -0
  284. package/dist/sync/b2-sha1-reader.cjs.map +1 -0
  285. package/dist/sync/b2-sha1-reader.d.cts +58 -0
  286. package/dist/sync/b2-sha1-reader.d.cts.map +1 -0
  287. package/dist/sync/b2-sha1-reader.d.ts +58 -0
  288. package/dist/sync/b2-sha1-reader.d.ts.map +1 -0
  289. package/dist/sync/b2-sha1-reader.js +145 -0
  290. package/dist/sync/b2-sha1-reader.js.map +1 -0
  291. package/dist/sync/download-staging.cjs +265 -0
  292. package/dist/sync/download-staging.cjs.map +1 -0
  293. package/dist/sync/download-staging.d.cts +54 -0
  294. package/dist/sync/download-staging.d.cts.map +1 -0
  295. package/dist/sync/download-staging.d.ts +54 -0
  296. package/dist/sync/download-staging.d.ts.map +1 -0
  297. package/dist/sync/download-staging.js +259 -0
  298. package/dist/sync/download-staging.js.map +1 -0
  299. package/dist/sync/filesystem-errors.cjs +32 -0
  300. package/dist/sync/filesystem-errors.cjs.map +1 -0
  301. package/dist/sync/filesystem-errors.d.cts +8 -0
  302. package/dist/sync/filesystem-errors.d.cts.map +1 -0
  303. package/dist/sync/filesystem-errors.d.ts +8 -0
  304. package/dist/sync/filesystem-errors.d.ts.map +1 -0
  305. package/dist/sync/filesystem-errors.js +32 -0
  306. package/dist/sync/filesystem-errors.js.map +1 -0
  307. package/dist/sync/filters.cjs +217 -0
  308. package/dist/sync/filters.cjs.map +1 -0
  309. package/dist/sync/filters.d.cts +51 -0
  310. package/dist/sync/filters.d.cts.map +1 -0
  311. package/dist/sync/filters.d.ts +51 -0
  312. package/dist/sync/filters.d.ts.map +1 -0
  313. package/dist/sync/filters.js +213 -0
  314. package/dist/sync/filters.js.map +1 -0
  315. package/dist/sync/index.cjs +39 -22
  316. package/dist/sync/index.d.cts +14 -9
  317. package/dist/sync/index.d.cts.map +1 -1
  318. package/dist/sync/index.d.ts +14 -9
  319. package/dist/sync/index.d.ts.map +1 -1
  320. package/dist/sync/index.js +9 -20
  321. package/dist/sync/local-file-identity.cjs +61 -0
  322. package/dist/sync/local-file-identity.cjs.map +1 -0
  323. package/dist/sync/local-file-identity.d.cts +37 -0
  324. package/dist/sync/local-file-identity.d.cts.map +1 -0
  325. package/dist/sync/local-file-identity.d.ts +37 -0
  326. package/dist/sync/local-file-identity.d.ts.map +1 -0
  327. package/dist/sync/local-file-identity.js +60 -0
  328. package/dist/sync/local-file-identity.js.map +1 -0
  329. package/dist/sync/local-file-io.cjs +364 -0
  330. package/dist/sync/local-file-io.cjs.map +1 -0
  331. package/dist/sync/local-file-io.d.cts +75 -0
  332. package/dist/sync/local-file-io.d.cts.map +1 -0
  333. package/dist/sync/local-file-io.d.ts +75 -0
  334. package/dist/sync/local-file-io.d.ts.map +1 -0
  335. package/dist/sync/local-file-io.js +360 -0
  336. package/dist/sync/local-file-io.js.map +1 -0
  337. package/dist/sync/local-filesystem-root.cjs +27 -0
  338. package/dist/sync/local-filesystem-root.cjs.map +1 -0
  339. package/dist/sync/local-filesystem-root.d.cts +18 -0
  340. package/dist/sync/local-filesystem-root.d.cts.map +1 -0
  341. package/dist/sync/local-filesystem-root.d.ts +18 -0
  342. package/dist/sync/local-filesystem-root.d.ts.map +1 -0
  343. package/dist/sync/local-filesystem-root.js +26 -0
  344. package/dist/sync/local-filesystem-root.js.map +1 -0
  345. package/dist/sync/local-sha1.cjs +119 -0
  346. package/dist/sync/local-sha1.cjs.map +1 -0
  347. package/dist/sync/local-sha1.d.cts +39 -0
  348. package/dist/sync/local-sha1.d.cts.map +1 -0
  349. package/dist/sync/local-sha1.d.ts +39 -0
  350. package/dist/sync/local-sha1.d.ts.map +1 -0
  351. package/dist/sync/local-sha1.js +117 -0
  352. package/dist/sync/local-sha1.js.map +1 -0
  353. package/dist/sync/pairing.cjs +118 -29
  354. package/dist/sync/pairing.cjs.map +1 -1
  355. package/dist/sync/pairing.d.cts +7 -2
  356. package/dist/sync/pairing.d.cts.map +1 -1
  357. package/dist/sync/pairing.d.ts +7 -2
  358. package/dist/sync/pairing.d.ts.map +1 -1
  359. package/dist/sync/pairing.js +120 -31
  360. package/dist/sync/pairing.js.map +1 -1
  361. package/dist/sync/path-order.cjs +30 -0
  362. package/dist/sync/path-order.cjs.map +1 -0
  363. package/dist/sync/path-order.d.cts +28 -0
  364. package/dist/sync/path-order.d.cts.map +1 -0
  365. package/dist/sync/path-order.d.ts +28 -0
  366. package/dist/sync/path-order.d.ts.map +1 -0
  367. package/dist/sync/path-order.js +29 -0
  368. package/dist/sync/path-order.js.map +1 -0
  369. package/dist/sync/path-safety.cjs +111 -0
  370. package/dist/sync/path-safety.cjs.map +1 -0
  371. package/dist/sync/path-safety.d.cts +78 -0
  372. package/dist/sync/path-safety.d.cts.map +1 -0
  373. package/dist/sync/path-safety.d.ts +78 -0
  374. package/dist/sync/path-safety.d.ts.map +1 -0
  375. package/dist/sync/path-safety.js +105 -0
  376. package/dist/sync/path-safety.js.map +1 -0
  377. package/dist/sync/paths.d.cts +19 -0
  378. package/dist/sync/paths.d.cts.map +1 -0
  379. package/dist/sync/paths.d.ts +19 -0
  380. package/dist/sync/paths.d.ts.map +1 -0
  381. package/dist/sync/policies/compare.cjs +389 -11
  382. package/dist/sync/policies/compare.cjs.map +1 -1
  383. package/dist/sync/policies/compare.d.cts +100 -2
  384. package/dist/sync/policies/compare.d.cts.map +1 -1
  385. package/dist/sync/policies/compare.d.ts +100 -2
  386. package/dist/sync/policies/compare.d.ts.map +1 -1
  387. package/dist/sync/policies/compare.js +387 -13
  388. package/dist/sync/policies/compare.js.map +1 -1
  389. package/dist/sync/policies/index.cjs +76 -65
  390. package/dist/sync/policies/index.cjs.map +1 -1
  391. package/dist/sync/policies/index.d.cts +35 -6
  392. package/dist/sync/policies/index.d.cts.map +1 -1
  393. package/dist/sync/policies/index.d.ts +35 -6
  394. package/dist/sync/policies/index.d.ts.map +1 -1
  395. package/dist/sync/policies/index.js +76 -65
  396. package/dist/sync/policies/index.js.map +1 -1
  397. package/dist/sync/prefix.cjs +96 -0
  398. package/dist/sync/prefix.cjs.map +1 -0
  399. package/dist/sync/prefix.d.cts +55 -0
  400. package/dist/sync/prefix.d.cts.map +1 -0
  401. package/dist/sync/prefix.d.ts +55 -0
  402. package/dist/sync/prefix.d.ts.map +1 -0
  403. package/dist/sync/prefix.js +92 -0
  404. package/dist/sync/prefix.js.map +1 -0
  405. package/dist/sync/regexp-safety.cjs +192 -0
  406. package/dist/sync/regexp-safety.cjs.map +1 -0
  407. package/dist/sync/regexp-safety.d.cts +29 -0
  408. package/dist/sync/regexp-safety.d.cts.map +1 -0
  409. package/dist/sync/regexp-safety.d.ts +29 -0
  410. package/dist/sync/regexp-safety.d.ts.map +1 -0
  411. package/dist/sync/regexp-safety.js +190 -0
  412. package/dist/sync/regexp-safety.js.map +1 -0
  413. package/dist/sync/scan-events.cjs +33 -0
  414. package/dist/sync/scan-events.cjs.map +1 -0
  415. package/dist/sync/scan-events.d.cts +17 -0
  416. package/dist/sync/scan-events.d.cts.map +1 -0
  417. package/dist/sync/scan-events.d.ts +17 -0
  418. package/dist/sync/scan-events.d.ts.map +1 -0
  419. package/dist/sync/scan-events.js +32 -0
  420. package/dist/sync/scan-events.js.map +1 -0
  421. package/dist/sync/scan-limit.cjs +33 -0
  422. package/dist/sync/scan-limit.cjs.map +1 -0
  423. package/dist/sync/scan-limit.d.cts +21 -0
  424. package/dist/sync/scan-limit.d.cts.map +1 -0
  425. package/dist/sync/scan-limit.d.ts +21 -0
  426. package/dist/sync/scan-limit.d.ts.map +1 -0
  427. package/dist/sync/scan-limit.js +31 -0
  428. package/dist/sync/scan-limit.js.map +1 -0
  429. package/dist/sync/scanners/b2.cjs +239 -55
  430. package/dist/sync/scanners/b2.cjs.map +1 -1
  431. package/dist/sync/scanners/b2.d.cts +26 -7
  432. package/dist/sync/scanners/b2.d.cts.map +1 -1
  433. package/dist/sync/scanners/b2.d.ts +26 -7
  434. package/dist/sync/scanners/b2.d.ts.map +1 -1
  435. package/dist/sync/scanners/b2.js +239 -55
  436. package/dist/sync/scanners/b2.js.map +1 -1
  437. package/dist/sync/scanners/local.cjs +215 -56
  438. package/dist/sync/scanners/local.cjs.map +1 -1
  439. package/dist/sync/scanners/local.d.cts +22 -7
  440. package/dist/sync/scanners/local.d.cts.map +1 -1
  441. package/dist/sync/scanners/local.d.ts +22 -7
  442. package/dist/sync/scanners/local.d.ts.map +1 -1
  443. package/dist/sync/scanners/local.js +216 -57
  444. package/dist/sync/scanners/local.js.map +1 -1
  445. package/dist/sync/sha1-metadata.cjs +94 -0
  446. package/dist/sync/sha1-metadata.cjs.map +1 -0
  447. package/dist/sync/sha1-metadata.d.cts +82 -0
  448. package/dist/sync/sha1-metadata.d.cts.map +1 -0
  449. package/dist/sync/sha1-metadata.d.ts +82 -0
  450. package/dist/sync/sha1-metadata.d.ts.map +1 -0
  451. package/dist/sync/sha1-metadata.js +89 -0
  452. package/dist/sync/sha1-metadata.js.map +1 -0
  453. package/dist/sync/sha1-options.cjs +23 -0
  454. package/dist/sync/sha1-options.cjs.map +1 -0
  455. package/dist/sync/sha1-options.d.cts +14 -0
  456. package/dist/sync/sha1-options.d.cts.map +1 -0
  457. package/dist/sync/sha1-options.d.ts +14 -0
  458. package/dist/sync/sha1-options.d.ts.map +1 -0
  459. package/dist/sync/sha1-options.js +21 -0
  460. package/dist/sync/sha1-options.js.map +1 -0
  461. package/dist/sync/synchronizer.cjs +782 -197
  462. package/dist/sync/synchronizer.cjs.map +1 -1
  463. package/dist/sync/synchronizer.d.cts +47 -1
  464. package/dist/sync/synchronizer.d.cts.map +1 -1
  465. package/dist/sync/synchronizer.d.ts +47 -1
  466. package/dist/sync/synchronizer.d.ts.map +1 -1
  467. package/dist/sync/synchronizer.js +779 -173
  468. package/dist/sync/synchronizer.js.map +1 -1
  469. package/dist/sync/types.d.cts +208 -16
  470. package/dist/sync/types.d.cts.map +1 -1
  471. package/dist/sync/types.d.ts +208 -16
  472. package/dist/sync/types.d.ts.map +1 -1
  473. package/dist/types/auth.cjs +76 -56
  474. package/dist/types/auth.cjs.map +1 -1
  475. package/dist/types/auth.d.cts +67 -9
  476. package/dist/types/auth.d.cts.map +1 -1
  477. package/dist/types/auth.d.ts +67 -9
  478. package/dist/types/auth.d.ts.map +1 -1
  479. package/dist/types/auth.js +77 -57
  480. package/dist/types/auth.js.map +1 -1
  481. package/dist/types/bucket.cjs +74 -38
  482. package/dist/types/bucket.cjs.map +1 -1
  483. package/dist/types/bucket.js +75 -41
  484. package/dist/types/bucket.js.map +1 -1
  485. package/dist/types/encryption.cjs +364 -327
  486. package/dist/types/encryption.cjs.map +1 -1
  487. package/dist/types/encryption.d.cts +16 -0
  488. package/dist/types/encryption.d.cts.map +1 -1
  489. package/dist/types/encryption.d.ts +16 -0
  490. package/dist/types/encryption.d.ts.map +1 -1
  491. package/dist/types/encryption.js +364 -310
  492. package/dist/types/encryption.js.map +1 -1
  493. package/dist/types/errors.cjs +47 -0
  494. package/dist/types/errors.cjs.map +1 -0
  495. package/dist/types/errors.d.cts +13 -4
  496. package/dist/types/errors.d.cts.map +1 -1
  497. package/dist/types/errors.d.ts +13 -4
  498. package/dist/types/errors.d.ts.map +1 -1
  499. package/dist/types/errors.js +47 -0
  500. package/dist/types/errors.js.map +1 -0
  501. package/dist/types/file.cjs +36 -19
  502. package/dist/types/file.cjs.map +1 -1
  503. package/dist/types/file.d.cts +2 -2
  504. package/dist/types/file.d.cts.map +1 -1
  505. package/dist/types/file.d.ts +2 -2
  506. package/dist/types/file.d.ts.map +1 -1
  507. package/dist/types/file.js +37 -21
  508. package/dist/types/file.js.map +1 -1
  509. package/dist/types/ids.cjs +53 -9
  510. package/dist/types/ids.cjs.map +1 -1
  511. package/dist/types/ids.js +54 -15
  512. package/dist/types/ids.js.map +1 -1
  513. package/dist/types/index.d.cts +12 -11
  514. package/dist/types/index.d.cts.map +1 -1
  515. package/dist/types/index.d.ts +12 -11
  516. package/dist/types/index.d.ts.map +1 -1
  517. package/dist/types/key.d.cts +44 -8
  518. package/dist/types/key.d.cts.map +1 -1
  519. package/dist/types/key.d.ts +44 -8
  520. package/dist/types/key.d.ts.map +1 -1
  521. package/dist/types/lock.cjs +33 -13
  522. package/dist/types/lock.cjs.map +1 -1
  523. package/dist/types/lock.js +34 -15
  524. package/dist/types/lock.js.map +1 -1
  525. package/dist/types/notifications.cjs +44 -22
  526. package/dist/types/notifications.cjs.map +1 -1
  527. package/dist/types/notifications.js +45 -23
  528. package/dist/types/notifications.js.map +1 -1
  529. package/dist/types/upload.d.cts +39 -5
  530. package/dist/types/upload.d.cts.map +1 -1
  531. package/dist/types/upload.d.ts +39 -5
  532. package/dist/types/upload.d.ts.map +1 -1
  533. package/dist/upload/abort-scope.cjs +91 -0
  534. package/dist/upload/abort-scope.cjs.map +1 -0
  535. package/dist/upload/abort-scope.d.cts +48 -0
  536. package/dist/upload/abort-scope.d.cts.map +1 -0
  537. package/dist/upload/abort-scope.d.ts +48 -0
  538. package/dist/upload/abort-scope.d.ts.map +1 -0
  539. package/dist/upload/abort-scope.js +88 -0
  540. package/dist/upload/abort-scope.js.map +1 -0
  541. package/dist/upload/cancel.cjs +183 -8
  542. package/dist/upload/cancel.cjs.map +1 -1
  543. package/dist/upload/cancel.d.cts +110 -2
  544. package/dist/upload/cancel.d.cts.map +1 -1
  545. package/dist/upload/cancel.d.ts +110 -2
  546. package/dist/upload/cancel.d.ts.map +1 -1
  547. package/dist/upload/cancel.js +178 -9
  548. package/dist/upload/cancel.js.map +1 -1
  549. package/dist/upload/concurrency.cjs +79 -52
  550. package/dist/upload/concurrency.cjs.map +1 -1
  551. package/dist/upload/concurrency.js +79 -53
  552. package/dist/upload/concurrency.js.map +1 -1
  553. package/dist/upload/finish.cjs +59 -0
  554. package/dist/upload/finish.cjs.map +1 -0
  555. package/dist/upload/finish.d.cts +26 -0
  556. package/dist/upload/finish.d.cts.map +1 -0
  557. package/dist/upload/finish.d.ts +26 -0
  558. package/dist/upload/finish.d.ts.map +1 -0
  559. package/dist/upload/finish.js +59 -0
  560. package/dist/upload/finish.js.map +1 -0
  561. package/dist/upload/large.cjs +301 -218
  562. package/dist/upload/large.cjs.map +1 -1
  563. package/dist/upload/large.d.cts +76 -9
  564. package/dist/upload/large.d.cts.map +1 -1
  565. package/dist/upload/large.d.ts +76 -9
  566. package/dist/upload/large.d.ts.map +1 -1
  567. package/dist/upload/large.js +301 -218
  568. package/dist/upload/large.js.map +1 -1
  569. package/dist/upload/options.cjs +28 -0
  570. package/dist/upload/options.cjs.map +1 -0
  571. package/dist/upload/options.d.cts +104 -0
  572. package/dist/upload/options.d.cts.map +1 -0
  573. package/dist/upload/options.d.ts +104 -0
  574. package/dist/upload/options.d.ts.map +1 -0
  575. package/dist/upload/options.js +27 -0
  576. package/dist/upload/options.js.map +1 -0
  577. package/dist/upload/resume.cjs +333 -33
  578. package/dist/upload/resume.cjs.map +1 -1
  579. package/dist/upload/resume.d.cts +82 -12
  580. package/dist/upload/resume.d.cts.map +1 -1
  581. package/dist/upload/resume.d.ts +82 -12
  582. package/dist/upload/resume.d.ts.map +1 -1
  583. package/dist/upload/resume.js +330 -33
  584. package/dist/upload/resume.js.map +1 -1
  585. package/dist/upload/retry.cjs +186 -0
  586. package/dist/upload/retry.cjs.map +1 -0
  587. package/dist/upload/retry.d.cts +127 -0
  588. package/dist/upload/retry.d.cts.map +1 -0
  589. package/dist/upload/retry.d.ts +127 -0
  590. package/dist/upload/retry.d.ts.map +1 -0
  591. package/dist/upload/retry.js +182 -0
  592. package/dist/upload/retry.js.map +1 -0
  593. package/dist/upload/single.cjs +68 -44
  594. package/dist/upload/single.cjs.map +1 -1
  595. package/dist/upload/single.d.cts +2 -1
  596. package/dist/upload/single.d.cts.map +1 -1
  597. package/dist/upload/single.d.ts +2 -1
  598. package/dist/upload/single.d.ts.map +1 -1
  599. package/dist/upload/single.js +68 -44
  600. package/dist/upload/single.js.map +1 -1
  601. package/dist/upload/stream.cjs +317 -193
  602. package/dist/upload/stream.cjs.map +1 -1
  603. package/dist/upload/stream.d.cts +3 -1
  604. package/dist/upload/stream.d.cts.map +1 -1
  605. package/dist/upload/stream.d.ts +3 -1
  606. package/dist/upload/stream.d.ts.map +1 -1
  607. package/dist/upload/stream.js +316 -192
  608. package/dist/upload/stream.js.map +1 -1
  609. package/dist/util/best-effort.cjs +42 -8
  610. package/dist/util/best-effort.cjs.map +1 -1
  611. package/dist/util/best-effort.d.cts +2 -1
  612. package/dist/util/best-effort.d.cts.map +1 -1
  613. package/dist/util/best-effort.d.ts +2 -1
  614. package/dist/util/best-effort.d.ts.map +1 -1
  615. package/dist/util/best-effort.js +43 -9
  616. package/dist/util/best-effort.js.map +1 -1
  617. package/dist/util/bytes.cjs +23 -0
  618. package/dist/util/bytes.cjs.map +1 -0
  619. package/dist/util/bytes.d.cts +15 -0
  620. package/dist/util/bytes.d.cts.map +1 -0
  621. package/dist/util/bytes.d.ts +15 -0
  622. package/dist/util/bytes.d.ts.map +1 -0
  623. package/dist/util/bytes.js +23 -0
  624. package/dist/util/bytes.js.map +1 -0
  625. package/dist/util/crypto.cjs +68 -0
  626. package/dist/util/crypto.cjs.map +1 -0
  627. package/dist/util/crypto.d.cts +26 -0
  628. package/dist/util/crypto.d.cts.map +1 -0
  629. package/dist/util/crypto.d.ts +26 -0
  630. package/dist/util/crypto.d.ts.map +1 -0
  631. package/dist/util/crypto.js +66 -0
  632. package/dist/util/crypto.js.map +1 -0
  633. package/dist/util/defaults.cjs +25 -9
  634. package/dist/util/defaults.cjs.map +1 -1
  635. package/dist/util/defaults.js +26 -11
  636. package/dist/util/defaults.js.map +1 -1
  637. package/dist/util/error-reason.cjs +41 -0
  638. package/dist/util/error-reason.cjs.map +1 -0
  639. package/dist/util/error-reason.d.cts +9 -0
  640. package/dist/util/error-reason.d.cts.map +1 -0
  641. package/dist/util/error-reason.d.ts +9 -0
  642. package/dist/util/error-reason.d.ts.map +1 -0
  643. package/dist/util/error-reason.js +41 -0
  644. package/dist/util/error-reason.js.map +1 -0
  645. package/dist/util/http.cjs +23 -0
  646. package/dist/util/http.cjs.map +1 -0
  647. package/dist/util/http.d.cts +13 -0
  648. package/dist/util/http.d.cts.map +1 -0
  649. package/dist/util/http.d.ts +13 -0
  650. package/dist/util/http.d.ts.map +1 -0
  651. package/dist/util/http.js +23 -0
  652. package/dist/util/http.js.map +1 -0
  653. package/dist/util/normalize.cjs +66 -7
  654. package/dist/util/normalize.cjs.map +1 -1
  655. package/dist/util/normalize.js +67 -10
  656. package/dist/util/normalize.js.map +1 -1
  657. package/dist/util/paginator.cjs +63 -14
  658. package/dist/util/paginator.cjs.map +1 -1
  659. package/dist/util/paginator.js +64 -16
  660. package/dist/util/paginator.js.map +1 -1
  661. package/dist/util/plan-ranges.cjs +47 -22
  662. package/dist/util/plan-ranges.cjs.map +1 -1
  663. package/dist/util/plan-ranges.js +48 -24
  664. package/dist/util/plan-ranges.js.map +1 -1
  665. package/dist/util/sha1.cjs +27 -0
  666. package/dist/util/sha1.cjs.map +1 -0
  667. package/dist/util/sha1.d.cts +17 -0
  668. package/dist/util/sha1.d.cts.map +1 -0
  669. package/dist/util/sha1.d.ts +17 -0
  670. package/dist/util/sha1.d.ts.map +1 -0
  671. package/dist/util/sha1.js +26 -0
  672. package/dist/util/sha1.js.map +1 -0
  673. package/dist/util/text-codec.cjs +29 -5
  674. package/dist/util/text-codec.cjs.map +1 -1
  675. package/dist/util/text-codec.js +30 -7
  676. package/dist/util/text-codec.js.map +1 -1
  677. package/dist/util/to-error.cjs +18 -4
  678. package/dist/util/to-error.cjs.map +1 -1
  679. package/dist/util/to-error.js +19 -5
  680. package/dist/util/to-error.js.map +1 -1
  681. package/dist/version.cjs +17 -5
  682. package/dist/version.cjs.map +1 -1
  683. package/dist/version.d.cts +3 -3
  684. package/dist/version.d.ts +3 -3
  685. package/dist/version.js +19 -6
  686. package/dist/version.js.map +1 -1
  687. package/package.json +19 -16
  688. package/dist/auth/index.cjs.map +0 -1
  689. package/dist/auth/index.js.map +0 -1
  690. package/dist/index.cjs.map +0 -1
  691. package/dist/index.js.map +0 -1
  692. package/dist/notifications/index.cjs.map +0 -1
  693. package/dist/notifications/index.js.map +0 -1
  694. package/dist/package.json.cjs +0 -9
  695. package/dist/package.json.cjs.map +0 -1
  696. package/dist/package.json.js +0 -9
  697. package/dist/package.json.js.map +0 -1
  698. package/dist/streams/index.cjs.map +0 -1
  699. package/dist/streams/index.js.map +0 -1
  700. package/dist/sync/index.cjs.map +0 -1
  701. package/dist/sync/index.js.map +0 -1
@@ -0,0 +1,360 @@
1
+ import { sanitizeErrorReason } from "../util/error-reason.js";
2
+ import { assertSameScannedRegularFile } from "./local-file-identity.js";
3
+ import { assertPathInsideRoot, hasErrorCode, makeReservedSyncTempFileName, noFollowFlag, safeRelativePathSegments } from "./path-safety.js";
4
+ import { DOWNLOAD_STAGING_DIRECTORY_NAME, assertDownloadPathSameDevice, createDownloadStagingDirectory, isDownloadStagingDirectorySegment } from "./download-staging.js";
5
+ import { readStreamChunkWithTimeout } from "./b2-sha1-reader.js";
6
+ //#region src/sync/local-file-io.ts
7
+ /** @internal */
8
+ var localFileIoTestHooks = {};
9
+ /**
10
+ * Verifies that a previously scanned local file still points at the same regular file.
11
+ *
12
+ * @param path - Scanned local path and file identity.
13
+ *
14
+ * @internal
15
+ */
16
+ async function validateScannedLocalFile(path) {
17
+ await (await openValidatedScannedLocalFile(path)).close();
18
+ }
19
+ async function openValidatedScannedLocalFile(path) {
20
+ const { constants } = await import("node:fs");
21
+ const { open } = await import("node:fs/promises");
22
+ const flags = constants.O_RDONLY | noFollowFlag(constants) | (constants.O_NONBLOCK ?? 0);
23
+ const handle = await open(path.absolutePath, flags).catch((err) => {
24
+ if (hasErrorCode(err, "ELOOP")) throw new Error("local file changed before upload: not a regular file");
25
+ throw new Error(`local file changed before upload: could not open scanned file: ${sanitizeErrorReason(err)}`);
26
+ });
27
+ try {
28
+ assertSameScannedRegularFile(await handle.stat(), path, "upload", { platform: localFileIoTestHooks.platform });
29
+ return handle;
30
+ } catch (err) {
31
+ await handle.close().catch(() => {});
32
+ throw err;
33
+ }
34
+ }
35
+ /**
36
+ * Streams a B2 download under a local sync root with path, timeout, and size checks.
37
+ *
38
+ * @param root - Local sync root.
39
+ * @param relPath - Destination path relative to the root.
40
+ * @param body - Download body stream.
41
+ * @param options - Expected byte count, idle timeout, and optional abort signal.
42
+ *
43
+ * @internal
44
+ */
45
+ async function writeLocalStreamInsideRoot(root, relPath, body, options) {
46
+ const { constants } = await import("node:fs");
47
+ const { link, lstat, mkdir, open, realpath, rename, rm, stat } = await import("node:fs/promises");
48
+ const path = await import("node:path");
49
+ const { randomUUID } = await import("node:crypto");
50
+ assertValidExpectedBytes(options.expectedBytes);
51
+ const segments = safeRelativePathSegments(relPath);
52
+ if (isDownloadStagingDirectorySegment(segments[0])) throw new Error(`unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is reserved for SDK download staging`);
53
+ const rootRealPath = await realpath(root);
54
+ let current = rootRealPath;
55
+ for (const segment of segments.slice(0, -1)) {
56
+ current = path.join(current, segment);
57
+ try {
58
+ await mkdir(current);
59
+ } catch (err) {
60
+ if (!hasErrorCode(err, "EEXIST")) throw err;
61
+ }
62
+ if (!(await lstat(current)).isDirectory()) throw new Error("unsafe local destination path: parent is not a directory");
63
+ await localFileIoTestHooks.afterParentDirectoryValidated?.(current);
64
+ }
65
+ const destPath = path.join(rootRealPath, ...segments);
66
+ assertPathInsideRoot(rootRealPath, destPath, path);
67
+ const parentRealPath = await realpath(path.dirname(destPath));
68
+ const finalPath = path.join(parentRealPath, path.basename(destPath));
69
+ assertPathInsideRoot(rootRealPath, finalPath, path);
70
+ try {
71
+ const targetStats = await lstat(finalPath);
72
+ if (targetStats.isSymbolicLink()) throw new Error("unsafe local destination path: target is a symbolic link");
73
+ if (targetStats.isFile() && targetStats.nlink > 1) throw new Error("unsafe local destination path: target has multiple hard links");
74
+ } catch (err) {
75
+ if (!hasErrorCode(err, "ENOENT")) throw err;
76
+ }
77
+ const statForDeviceCheck = localFileIoTestHooks.statForDeviceCheck ?? stat;
78
+ await assertDownloadPathSameDevice(rootRealPath, parentRealPath, statForDeviceCheck, "unsafe local destination path: cannot publish download across filesystems");
79
+ let parentHandle;
80
+ let anchoredParentPath;
81
+ /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */
82
+ if (globalThis.process?.platform === "linux" && constants.O_DIRECTORY !== void 0 && localFileIoTestHooks.disableProcFdAnchoring !== true) try {
83
+ parentHandle = await open(parentRealPath, constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants));
84
+ anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`;
85
+ } catch (err) {
86
+ if (hasErrorCode(err, "ELOOP") || hasErrorCode(err, "ENOTDIR")) throw new Error("unsafe local destination path: parent is not a directory");
87
+ throw err;
88
+ }
89
+ /* v8 ignore stop */
90
+ const finalName = path.basename(destPath);
91
+ const finalWritePath = path.join(anchoredParentPath ?? parentRealPath, finalName);
92
+ let publishMode;
93
+ try {
94
+ publishMode = await replacementFileMode(finalPath);
95
+ } catch (err) {
96
+ /* v8 ignore next -- best-effort close during setup failure */
97
+ await parentHandle?.close().catch(() => {});
98
+ throw err;
99
+ }
100
+ let stagingDirectory;
101
+ try {
102
+ stagingDirectory = await createDownloadStagingDirectory(rootRealPath, path, randomUUID, statForDeviceCheck, localFileIoTestHooks.beforeStagingMarkerWrite);
103
+ } catch (err) {
104
+ /* v8 ignore next -- best-effort close during setup failure */
105
+ await parentHandle?.close().catch(() => {});
106
+ throw err;
107
+ }
108
+ const tmpPath = path.join(stagingDirectory, `.b2sdk-${randomUUID()}.partial`);
109
+ let handle;
110
+ try {
111
+ handle = await open(tmpPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollowFlag(constants), PRIVATE_DOWNLOAD_FILE_MODE);
112
+ /* v8 ignore next -- best-effort chmod */
113
+ await handle.chmod(PRIVATE_DOWNLOAD_FILE_MODE).catch(() => {});
114
+ await localFileIoTestHooks.afterTempFileCreated?.(tmpPath, stagingDirectory);
115
+ } catch (err) {
116
+ await parentHandle?.close().catch(() => {});
117
+ await rm(stagingDirectory, {
118
+ recursive: true,
119
+ force: true
120
+ }).catch(() => {});
121
+ throw err;
122
+ }
123
+ /* v8 ignore stop */
124
+ try {
125
+ const tmpRealPath = await realpath(tmpPath);
126
+ assertPathInsideRoot(stagingDirectory, tmpRealPath, path);
127
+ } catch (err) {
128
+ /* v8 ignore next -- best-effort cleanup */
129
+ await handle?.close().catch(() => {});
130
+ /* v8 ignore next -- best-effort cleanup */
131
+ await rm(tmpPath, { force: true }).catch(() => {});
132
+ /* v8 ignore next -- best-effort cleanup */
133
+ await rm(stagingDirectory, {
134
+ recursive: true,
135
+ force: true
136
+ }).catch(() => {});
137
+ /* v8 ignore next -- best-effort cleanup */
138
+ await parentHandle?.close().catch(() => {});
139
+ throw err;
140
+ }
141
+ const writeHandle = handle;
142
+ const reader = body.getReader();
143
+ let completed = false;
144
+ try {
145
+ let bytesWritten = 0;
146
+ while (true) {
147
+ const { done, value } = await readStreamChunkWithTimeout(reader, options.idleTimeoutMillis, `download read stalled for ${options.idleTimeoutMillis} ms`, options.signal);
148
+ if (done) break;
149
+ if (bytesWritten + value.byteLength > options.expectedBytes) throw new Error(`download read exceeded ${options.expectedBytes} byte limit`);
150
+ await writeAll(writeHandle, value, bytesWritten);
151
+ bytesWritten += value.byteLength;
152
+ }
153
+ if (bytesWritten !== options.expectedBytes) throw new Error(`download read ended after ${bytesWritten} bytes, expected ${options.expectedBytes}`);
154
+ if (publishMode !== PRIVATE_DOWNLOAD_FILE_MODE)
155
+ /* v8 ignore next -- best-effort mode preservation */
156
+ await writeHandle.chmod(publishMode).catch(() => {});
157
+ await writeHandle.close();
158
+ handle = void 0;
159
+ const [parentRealPathBeforeRename, parentStatsBeforeRename] = await Promise.all([realpath(path.dirname(destPath)), stat(path.dirname(destPath))]);
160
+ assertPathInsideRoot(rootRealPath, path.join(parentRealPathBeforeRename, path.basename(destPath)), path);
161
+ await localFileIoTestHooks.beforeFinalRename?.(parentRealPathBeforeRename);
162
+ let publishPath = finalWritePath;
163
+ if (anchoredParentPath === void 0) {
164
+ const [parentRealPathAfterHook, parentStatsAfterHook] = await Promise.all([realpath(path.dirname(destPath)), stat(path.dirname(destPath))]);
165
+ if (parentRealPathAfterHook !== parentRealPathBeforeRename || !sameParentIdentity(parentStatsAfterHook, parentStatsBeforeRename)) throw new Error("unsafe local destination path: parent changed before final publish");
166
+ publishPath = path.join(parentRealPathAfterHook, path.basename(destPath));
167
+ assertPathInsideRoot(rootRealPath, publishPath, path);
168
+ }
169
+ await publishDownload(lstat, link, path, randomUUID, rename, rm, tmpPath, publishPath, options.expectedDestination);
170
+ completed = true;
171
+ } catch (err) {
172
+ /* v8 ignore next -- best-effort cleanup */
173
+ reader.cancel(err).catch(() => {});
174
+ throw err;
175
+ } finally {
176
+ reader.releaseLock();
177
+ if (!completed) {
178
+ /* v8 ignore next -- best-effort cleanup */
179
+ await handle?.close().catch(() => {});
180
+ /* v8 ignore next -- best-effort cleanup */
181
+ await rm(tmpPath, { force: true }).catch(() => {});
182
+ }
183
+ /* v8 ignore next -- best-effort cleanup */
184
+ await rm(stagingDirectory, {
185
+ recursive: true,
186
+ force: true
187
+ }).catch(() => {});
188
+ /* v8 ignore next -- best-effort cleanup */
189
+ await parentHandle?.close().catch(() => {});
190
+ }
191
+ }
192
+ function assertValidExpectedBytes(expectedBytes) {
193
+ if (!Number.isSafeInteger(expectedBytes) || expectedBytes < 0) throw new Error("download expectedBytes must be a non-negative safe integer");
194
+ }
195
+ async function assertExpectedDownloadDestination(lstat, finalPath, expectedDestination) {
196
+ if (expectedDestination === void 0) return;
197
+ try {
198
+ const stats = await lstat(finalPath);
199
+ if (expectedDestination === null) throw new Error("local destination changed before download: file was created");
200
+ assertSameScannedRegularFile(stats, {
201
+ ...expectedDestination,
202
+ absolutePath: finalPath
203
+ }, "download", { platform: localFileIoTestHooks.platform });
204
+ } catch (err) {
205
+ if (hasErrorCode(err, "ENOENT")) {
206
+ if (expectedDestination === null) return;
207
+ throw new Error("local file changed before download: file missing");
208
+ }
209
+ throw err;
210
+ }
211
+ }
212
+ async function publishDownload(lstat, link, path, randomUUID, rename, rm, tmpPath, publishPath, expectedDestination) {
213
+ await assertExpectedDownloadDestination(lstat, publishPath, expectedDestination);
214
+ await localFileIoTestHooks.beforeDownloadPublish?.(publishPath);
215
+ if (expectedDestination === void 0) {
216
+ await rename(tmpPath, publishPath);
217
+ return;
218
+ }
219
+ if (expectedDestination === null) {
220
+ await linkDownloadNoOverwrite(link, tmpPath, publishPath, "local destination changed before download: file was created");
221
+ /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */
222
+ await rm(tmpPath, { force: true }).catch(() => {});
223
+ return;
224
+ }
225
+ const backupPath = path.join(path.dirname(publishPath), makeReservedSyncTempFileName(path.basename(publishPath), randomUUID()));
226
+ let backupExists = false;
227
+ let removeBackup = false;
228
+ try {
229
+ try {
230
+ await rename(publishPath, backupPath);
231
+ backupExists = true;
232
+ await localFileIoTestHooks.afterDownloadBackupRename?.(backupPath);
233
+ } catch (err) {
234
+ if (hasErrorCode(err, "ENOENT")) throw new Error("local file changed before download: file missing");
235
+ throw err;
236
+ }
237
+ assertSameScannedRegularFile(await lstat(backupPath), {
238
+ ...expectedDestination,
239
+ absolutePath: backupPath
240
+ }, "download", {
241
+ compareChangeTime: false,
242
+ platform: localFileIoTestHooks.platform
243
+ });
244
+ await linkDownloadNoOverwrite(link, tmpPath, publishPath, "local destination changed before download: file was created");
245
+ removeBackup = true;
246
+ /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */
247
+ await rm(tmpPath, { force: true }).catch(() => {});
248
+ } catch (err) {
249
+ if (backupExists && !removeBackup) await restoreBackupWithoutOverwrite(link, rm, backupPath, publishPath).catch(() => {});
250
+ throw err;
251
+ } finally {
252
+ if (removeBackup)
253
+ /* v8 ignore next -- old destination cleanup is best-effort after publish succeeds. */
254
+ await rm(backupPath, { force: true }).catch(() => {});
255
+ }
256
+ }
257
+ async function linkDownloadNoOverwrite(link, sourcePath, destPath, message) {
258
+ try {
259
+ await link(sourcePath, destPath);
260
+ } catch (err) {
261
+ if (hasErrorCode(err, "EEXIST")) throw new Error(message);
262
+ throw err;
263
+ }
264
+ }
265
+ async function restoreBackupWithoutOverwrite(link, rm, backupPath, publishPath) {
266
+ try {
267
+ await link(backupPath, publishPath);
268
+ await rm(backupPath, { force: true });
269
+ } catch (err) {
270
+ if (hasErrorCode(err, "EEXIST")) return;
271
+ throw err;
272
+ }
273
+ }
274
+ var PRIVATE_DOWNLOAD_FILE_MODE = 384;
275
+ async function replacementFileMode(filePath) {
276
+ const { lstat } = await import("node:fs/promises");
277
+ try {
278
+ const stats = await lstat(filePath);
279
+ return stats.isFile() ? stats.mode & 511 : PRIVATE_DOWNLOAD_FILE_MODE;
280
+ } catch (err) {
281
+ if (hasErrorCode(err, "ENOENT")) return PRIVATE_DOWNLOAD_FILE_MODE;
282
+ throw err;
283
+ }
284
+ }
285
+ /**
286
+ * Deletes a scanned local file under a sync root without re-resolving attacker-controlled parents.
287
+ *
288
+ * @param root - Local sync root.
289
+ * @param scannedPath - Previously scanned local file metadata.
290
+ *
291
+ * @internal
292
+ */
293
+ async function deleteLocalFileInsideRoot(root, scannedPath) {
294
+ if (root === "") throw new Error("Local sync root required for filesystem mutation");
295
+ const { constants } = await import("node:fs");
296
+ const { lstat, open, realpath, stat, unlink } = await import("node:fs/promises");
297
+ const path = await import("node:path");
298
+ const segments = safeRelativePathSegments(scannedPath.relativePath);
299
+ const safeRoot = path.resolve(root);
300
+ const rootStats = await lstat(safeRoot);
301
+ if (rootStats.isSymbolicLink()) throw new Error(`Refusing to access sync root through symlink: ${scannedPath.relativePath}`);
302
+ if (!rootStats.isDirectory()) throw new Error(`Local sync root is not a directory: ${scannedPath.relativePath}`);
303
+ const rootRealPath = await realpath(safeRoot);
304
+ const expectedPath = path.join(rootRealPath, ...segments);
305
+ assertPathInsideRoot(rootRealPath, expectedPath, path);
306
+ if (path.resolve(scannedPath.absolutePath) !== expectedPath) throw new Error(`Refusing to delete outside sync root: ${scannedPath.relativePath}`);
307
+ const [parentRealPath, parentStats] = await Promise.all([realpath(path.dirname(expectedPath)), stat(path.dirname(expectedPath))]);
308
+ const finalPath = path.join(parentRealPath, path.basename(expectedPath));
309
+ assertPathInsideRoot(rootRealPath, finalPath, path);
310
+ const platform = globalThis.process?.platform;
311
+ let parentHandle;
312
+ let anchoredParentPath;
313
+ /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */
314
+ if (platform === "linux" && constants.O_DIRECTORY !== void 0 && localFileIoTestHooks.disableProcFdAnchoring !== true) try {
315
+ await localFileIoTestHooks.beforeLocalDeleteOpenParent?.(parentRealPath);
316
+ parentHandle = await open(parentRealPath, constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants));
317
+ anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`;
318
+ } catch (err) {
319
+ if (hasErrorCode(err, "ELOOP") || hasErrorCode(err, "ENOTDIR")) throw new Error("unsafe local delete path: parent is not a directory");
320
+ throw err;
321
+ }
322
+ /* v8 ignore stop */
323
+ try {
324
+ const unlinkPath = anchoredParentPath === void 0 ? finalPath : path.join(anchoredParentPath, path.basename(expectedPath));
325
+ assertSameScannedRegularFile(await lstat(unlinkPath), {
326
+ ...scannedPath,
327
+ absolutePath: unlinkPath
328
+ }, "delete", { platform: localFileIoTestHooks.platform });
329
+ await localFileIoTestHooks.beforeLocalDeleteUnlink?.(parentRealPath);
330
+ if (anchoredParentPath === void 0 && localFileIoTestHooks.disableProcFdAnchoring === true && parentRealPath !== rootRealPath) throw new Error("unsafe local delete path: stable parent handle unavailable for unlink");
331
+ if (anchoredParentPath === void 0) {
332
+ const [parentRealPathBeforeUnlink, parentStatsBeforeUnlink] = await Promise.all([realpath(path.dirname(expectedPath)), stat(path.dirname(expectedPath))]);
333
+ if (parentRealPathBeforeUnlink !== parentRealPath || !sameParentIdentity(parentStatsBeforeUnlink, parentStats)) throw new Error("unsafe local delete path: parent changed before unlink");
334
+ }
335
+ assertSameScannedRegularFile(await lstat(unlinkPath), {
336
+ ...scannedPath,
337
+ absolutePath: unlinkPath
338
+ }, "delete", { platform: localFileIoTestHooks.platform });
339
+ await unlink(unlinkPath);
340
+ } finally {
341
+ /* v8 ignore next -- best-effort cleanup */
342
+ await parentHandle?.close().catch(() => {});
343
+ }
344
+ }
345
+ function sameParentIdentity(current, expected) {
346
+ return current.dev === expected.dev && current.ino === expected.ino;
347
+ }
348
+ async function writeAll(handle, data, position) {
349
+ let offset = 0;
350
+ while (offset < data.byteLength) {
351
+ const { bytesWritten } = await handle.write(data, offset, data.byteLength - offset, position + offset);
352
+ /* v8 ignore next -- defensive: FileHandle.write should progress for non-empty chunks. */
353
+ if (bytesWritten <= 0) throw new Error("download write made no progress");
354
+ offset += bytesWritten;
355
+ }
356
+ }
357
+ //#endregion
358
+ export { DOWNLOAD_STAGING_DIRECTORY_NAME, deleteLocalFileInsideRoot, localFileIoTestHooks, validateScannedLocalFile, writeLocalStreamInsideRoot };
359
+
360
+ //# sourceMappingURL=local-file-io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-file-io.js","names":[],"sources":["../../src/sync/local-file-io.ts"],"sourcesContent":["import { sanitizeErrorReason } from '../util/error-reason.ts'\nimport { readStreamChunkWithTimeout } from './b2-sha1-reader.ts'\nimport {\n assertDownloadPathSameDevice,\n createDownloadStagingDirectory,\n DOWNLOAD_STAGING_DIRECTORY_NAME,\n isDownloadStagingDirectorySegment,\n} from './download-staging.ts'\nimport { assertSameScannedRegularFile } from './local-file-identity.ts'\nimport {\n assertPathInsideRoot,\n hasErrorCode,\n makeReservedSyncTempFileName,\n noFollowFlag,\n safeRelativePathSegments,\n} from './path-safety.ts'\nimport { DEFAULT_SHA1_IDLE_TIMEOUT_MILLIS } from './sha1-options.ts'\nimport type { LocalSyncPath } from './types.ts'\n\ntype DeviceStat = { readonly dev: number | bigint }\ntype DeviceStatFn = (path: string) => Promise<DeviceStat>\n\nexport { DOWNLOAD_STAGING_DIRECTORY_NAME }\n\n/** @internal */\nexport const localFileIoTestHooks: {\n afterParentDirectoryValidated?: (path: string) => Promise<void> | void\n afterDownloadBackupRename?: (path: string) => Promise<void> | void\n afterTempFileCreated?: (path: string, stagingDirectory: string) => Promise<void> | void\n beforeDownloadPublish?: (path: string) => Promise<void> | void\n beforeFinalRename?: (path: string) => Promise<void> | void\n beforeLocalDeleteOpenParent?: (path: string) => Promise<void> | void\n beforeLocalDeleteUnlink?: (path: string) => Promise<void> | void\n beforeStagingMarkerWrite?: (path: string) => Promise<void> | void\n disableProcFdAnchoring?: boolean\n platform?: string\n statForDeviceCheck?: DeviceStatFn\n} = {}\n\ninterface ScannedLocalFileHandle {\n stat(): Promise<{\n isFile(): boolean\n readonly dev: number\n readonly ino: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n readonly size: number\n }>\n readFile(): Promise<Uint8Array>\n close(): Promise<void>\n}\n\n/**\n * Verifies that a previously scanned local file still points at the same regular file.\n *\n * @param path - Scanned local path and file identity.\n *\n * @internal\n */\nexport async function validateScannedLocalFile(path: LocalSyncPath): Promise<void> {\n const handle = await openValidatedScannedLocalFile(path)\n await handle.close()\n}\n\n/**\n * Reads a previously scanned local file while rejecting symlink swaps and metadata drift.\n *\n * @param path - Scanned local path and file identity.\n *\n * @returns The file bytes.\n *\n * @internal\n */\nexport async function readScannedLocalFile(path: LocalSyncPath): Promise<Uint8Array> {\n const handle = await openValidatedScannedLocalFile(path)\n try {\n const data = await handle.readFile()\n if (data.byteLength !== path.size) {\n throw new Error('local file changed before upload: size changed while reading')\n }\n return data\n } finally {\n await handle.close()\n }\n}\n\nasync function openValidatedScannedLocalFile(path: LocalSyncPath): Promise<ScannedLocalFileHandle> {\n const { constants } = await import('node:fs')\n const { open } = await import('node:fs/promises')\n const flags =\n constants.O_RDONLY |\n noFollowFlag(constants) |\n ((constants as { O_NONBLOCK?: number }).O_NONBLOCK ?? 0)\n const handle = await open(path.absolutePath, flags).catch((err: unknown) => {\n if (hasErrorCode(err, 'ELOOP')) {\n throw new Error('local file changed before upload: not a regular file')\n }\n throw new Error(\n `local file changed before upload: could not open scanned file: ${sanitizeErrorReason(err)}`,\n )\n })\n try {\n const stats = await handle.stat()\n assertSameScannedRegularFile(stats, path, 'upload', {\n platform: localFileIoTestHooks.platform,\n })\n return handle\n } catch (err) {\n await handle.close().catch(() => {})\n throw err\n }\n}\n\n/**\n * Writes bytes under a local sync root after path containment checks.\n *\n * @param root - Local sync root.\n * @param relPath - Destination path relative to the root.\n * @param data - Bytes to write.\n *\n * @internal\n */\nexport async function writeLocalFileInsideRoot(\n root: string,\n relPath: string,\n data: Uint8Array,\n): Promise<void> {\n await writeLocalStreamInsideRoot(\n root,\n relPath,\n new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(data)\n controller.close()\n },\n }),\n {\n expectedBytes: data.byteLength,\n idleTimeoutMillis: DEFAULT_SHA1_IDLE_TIMEOUT_MILLIS,\n },\n )\n}\n\n/**\n * Streams a B2 download under a local sync root with path, timeout, and size checks.\n *\n * @param root - Local sync root.\n * @param relPath - Destination path relative to the root.\n * @param body - Download body stream.\n * @param options - Expected byte count, idle timeout, and optional abort signal.\n *\n * @internal\n */\nexport async function writeLocalStreamInsideRoot(\n root: string,\n relPath: string,\n body: ReadableStream<Uint8Array>,\n options: {\n readonly expectedBytes: number\n readonly expectedDestination?: LocalSyncPath | null\n readonly idleTimeoutMillis: number\n readonly signal?: AbortSignal\n },\n): Promise<void> {\n const { constants } = await import('node:fs')\n const { link, lstat, mkdir, open, realpath, rename, rm, stat } = await import('node:fs/promises')\n const path = await import('node:path')\n const { randomUUID } = await import('node:crypto')\n assertValidExpectedBytes(options.expectedBytes)\n const segments = safeRelativePathSegments(relPath)\n if (isDownloadStagingDirectorySegment(segments[0])) {\n throw new Error(\n `unsafe local destination path: ${DOWNLOAD_STAGING_DIRECTORY_NAME} is reserved for SDK download staging`,\n )\n }\n const rootRealPath = await realpath(root)\n\n let current = rootRealPath\n for (const segment of segments.slice(0, -1)) {\n current = path.join(current, segment)\n try {\n await mkdir(current)\n } catch (err) {\n if (!hasErrorCode(err, 'EEXIST')) throw err\n }\n const stats = await lstat(current)\n if (!stats.isDirectory()) {\n throw new Error('unsafe local destination path: parent is not a directory')\n }\n await localFileIoTestHooks.afterParentDirectoryValidated?.(current)\n }\n\n const destPath = path.join(rootRealPath, ...segments)\n assertPathInsideRoot(rootRealPath, destPath, path)\n\n const parentRealPath = await realpath(path.dirname(destPath))\n const finalPath = path.join(parentRealPath, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, finalPath, path)\n try {\n const targetStats = await lstat(finalPath)\n if (targetStats.isSymbolicLink()) {\n throw new Error('unsafe local destination path: target is a symbolic link')\n }\n if (targetStats.isFile() && targetStats.nlink > 1) {\n throw new Error('unsafe local destination path: target has multiple hard links')\n }\n } catch (err) {\n if (!hasErrorCode(err, 'ENOENT')) throw err\n }\n const statForDeviceCheck = localFileIoTestHooks.statForDeviceCheck ?? stat\n await assertDownloadPathSameDevice(\n rootRealPath,\n parentRealPath,\n statForDeviceCheck,\n 'unsafe local destination path: cannot publish download across filesystems',\n )\n\n let parentHandle: Awaited<ReturnType<typeof open>> | undefined\n let anchoredParentPath: string | undefined\n const platform = (globalThis as { process?: { platform?: string } }).process?.platform\n /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */\n if (\n platform === 'linux' &&\n constants.O_DIRECTORY !== undefined &&\n localFileIoTestHooks.disableProcFdAnchoring !== true\n ) {\n try {\n parentHandle = await open(\n parentRealPath,\n constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants),\n )\n anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`\n } catch (err) {\n if (hasErrorCode(err, 'ELOOP') || hasErrorCode(err, 'ENOTDIR')) {\n throw new Error('unsafe local destination path: parent is not a directory')\n }\n throw err\n }\n }\n /* v8 ignore stop */\n const finalName = path.basename(destPath)\n const finalWritePath = path.join(anchoredParentPath ?? parentRealPath, finalName)\n let publishMode: number\n try {\n publishMode = await replacementFileMode(finalPath)\n } catch (err) {\n /* v8 ignore next -- best-effort close during setup failure */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n let stagingDirectory: string\n try {\n stagingDirectory = await createDownloadStagingDirectory(\n rootRealPath,\n path,\n randomUUID,\n statForDeviceCheck,\n localFileIoTestHooks.beforeStagingMarkerWrite,\n )\n } catch (err) {\n /* v8 ignore next -- best-effort close during setup failure */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n const tmpPath = path.join(stagingDirectory, `.b2sdk-${randomUUID()}.partial`)\n let handle: Awaited<ReturnType<typeof open>> | undefined\n try {\n handle = await open(\n tmpPath,\n constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollowFlag(constants),\n PRIVATE_DOWNLOAD_FILE_MODE,\n )\n /* v8 ignore next -- best-effort chmod */\n await handle.chmod(PRIVATE_DOWNLOAD_FILE_MODE).catch(() => {})\n await localFileIoTestHooks.afterTempFileCreated?.(tmpPath, stagingDirectory)\n /* v8 ignore start -- defensive cleanup before the main write try/finally exists */\n } catch (err) {\n await parentHandle?.close().catch(() => {})\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n throw err\n }\n /* v8 ignore stop */\n try {\n const tmpRealPath = await realpath(tmpPath)\n assertPathInsideRoot(stagingDirectory, tmpRealPath, path)\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n await handle?.close().catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(tmpPath, { force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n throw err\n }\n const writeHandle = handle\n const reader = body.getReader()\n let completed = false\n try {\n let bytesWritten = 0\n while (true) {\n const { done, value } = await readStreamChunkWithTimeout(\n reader,\n options.idleTimeoutMillis,\n `download read stalled for ${options.idleTimeoutMillis} ms`,\n options.signal,\n )\n if (done) break\n if (bytesWritten + value.byteLength > options.expectedBytes) {\n throw new Error(`download read exceeded ${options.expectedBytes} byte limit`)\n }\n await writeAll(writeHandle, value, bytesWritten)\n bytesWritten += value.byteLength\n }\n if (bytesWritten !== options.expectedBytes) {\n throw new Error(\n `download read ended after ${bytesWritten} bytes, expected ${options.expectedBytes}`,\n )\n }\n if (publishMode !== PRIVATE_DOWNLOAD_FILE_MODE) {\n /* v8 ignore next -- best-effort mode preservation */\n await writeHandle.chmod(publishMode).catch(() => {})\n }\n await writeHandle.close()\n handle = undefined\n\n const [parentRealPathBeforeRename, parentStatsBeforeRename] = await Promise.all([\n realpath(path.dirname(destPath)),\n stat(path.dirname(destPath)),\n ])\n const finalPathBeforeRename = path.join(parentRealPathBeforeRename, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, finalPathBeforeRename, path)\n await localFileIoTestHooks.beforeFinalRename?.(parentRealPathBeforeRename)\n let publishPath = finalWritePath\n if (anchoredParentPath === undefined) {\n const [parentRealPathAfterHook, parentStatsAfterHook] = await Promise.all([\n realpath(path.dirname(destPath)),\n stat(path.dirname(destPath)),\n ])\n if (\n parentRealPathAfterHook !== parentRealPathBeforeRename ||\n !sameParentIdentity(parentStatsAfterHook, parentStatsBeforeRename)\n ) {\n throw new Error('unsafe local destination path: parent changed before final publish')\n }\n publishPath = path.join(parentRealPathAfterHook, path.basename(destPath))\n assertPathInsideRoot(rootRealPath, publishPath, path)\n }\n await publishDownload(\n lstat,\n link,\n path,\n randomUUID,\n rename,\n rm,\n tmpPath,\n publishPath,\n options.expectedDestination,\n )\n completed = true\n } catch (err) {\n /* v8 ignore next -- best-effort cleanup */\n void reader.cancel(err).catch(() => {})\n throw err\n } finally {\n reader.releaseLock()\n if (!completed) {\n /* v8 ignore next -- best-effort cleanup */\n await handle?.close().catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await rm(tmpPath, { force: true }).catch(() => {})\n }\n /* v8 ignore next -- best-effort cleanup */\n await rm(stagingDirectory, { recursive: true, force: true }).catch(() => {})\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n }\n}\n\nfunction assertValidExpectedBytes(expectedBytes: number): void {\n if (!Number.isSafeInteger(expectedBytes) || expectedBytes < 0) {\n throw new Error('download expectedBytes must be a non-negative safe integer')\n }\n}\n\nasync function assertExpectedDownloadDestination(\n lstat: typeof import('node:fs/promises')['lstat'],\n finalPath: string,\n expectedDestination: LocalSyncPath | null | undefined,\n): Promise<void> {\n if (expectedDestination === undefined) return\n\n try {\n const stats = await lstat(finalPath)\n if (expectedDestination === null) {\n throw new Error('local destination changed before download: file was created')\n }\n assertSameScannedRegularFile(\n stats,\n { ...expectedDestination, absolutePath: finalPath },\n 'download',\n { platform: localFileIoTestHooks.platform },\n )\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) {\n if (expectedDestination === null) return\n throw new Error('local file changed before download: file missing')\n }\n throw err\n }\n}\n\nasync function publishDownload(\n lstat: typeof import('node:fs/promises')['lstat'],\n link: typeof import('node:fs/promises')['link'],\n path: typeof import('node:path'),\n randomUUID: () => string,\n rename: typeof import('node:fs/promises')['rename'],\n rm: typeof import('node:fs/promises')['rm'],\n tmpPath: string,\n publishPath: string,\n expectedDestination: LocalSyncPath | null | undefined,\n): Promise<void> {\n await assertExpectedDownloadDestination(lstat, publishPath, expectedDestination)\n await localFileIoTestHooks.beforeDownloadPublish?.(publishPath)\n\n if (expectedDestination === undefined) {\n await rename(tmpPath, publishPath)\n return\n }\n\n if (expectedDestination === null) {\n await linkDownloadNoOverwrite(\n link,\n tmpPath,\n publishPath,\n 'local destination changed before download: file was created',\n )\n /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */\n await rm(tmpPath, { force: true }).catch(() => {})\n return\n }\n\n const backupPath = path.join(\n path.dirname(publishPath),\n makeReservedSyncTempFileName(path.basename(publishPath), randomUUID()),\n )\n let backupExists = false\n let removeBackup = false\n\n try {\n try {\n await rename(publishPath, backupPath)\n backupExists = true\n await localFileIoTestHooks.afterDownloadBackupRename?.(backupPath)\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) {\n throw new Error('local file changed before download: file missing')\n }\n throw err\n }\n\n const backupStats = await lstat(backupPath)\n assertSameScannedRegularFile(\n backupStats,\n { ...expectedDestination, absolutePath: backupPath },\n 'download',\n { compareChangeTime: false, platform: localFileIoTestHooks.platform },\n )\n await linkDownloadNoOverwrite(\n link,\n tmpPath,\n publishPath,\n 'local destination changed before download: file was created',\n )\n removeBackup = true\n /* v8 ignore next -- staging cleanup is best-effort after a guarded publish succeeds. */\n await rm(tmpPath, { force: true }).catch(() => {})\n } catch (err) {\n if (backupExists && !removeBackup) {\n await restoreBackupWithoutOverwrite(link, rm, backupPath, publishPath).catch(() => {})\n }\n throw err\n } finally {\n if (removeBackup) {\n /* v8 ignore next -- old destination cleanup is best-effort after publish succeeds. */\n await rm(backupPath, { force: true }).catch(() => {})\n }\n }\n}\n\nasync function linkDownloadNoOverwrite(\n link: typeof import('node:fs/promises')['link'],\n sourcePath: string,\n destPath: string,\n message: string,\n): Promise<void> {\n try {\n await link(sourcePath, destPath)\n } catch (err) {\n if (hasErrorCode(err, 'EEXIST')) throw new Error(message)\n throw err\n }\n}\n\nasync function restoreBackupWithoutOverwrite(\n link: typeof import('node:fs/promises')['link'],\n rm: typeof import('node:fs/promises')['rm'],\n backupPath: string,\n publishPath: string,\n): Promise<void> {\n try {\n await link(backupPath, publishPath)\n await rm(backupPath, { force: true })\n } catch (err) {\n if (hasErrorCode(err, 'EEXIST')) return\n throw err\n }\n}\n\nconst PRIVATE_DOWNLOAD_FILE_MODE = 0o600\nasync function replacementFileMode(filePath: string): Promise<number> {\n const { lstat } = await import('node:fs/promises')\n try {\n const stats = await lstat(filePath)\n return stats.isFile() ? stats.mode & 0o777 : PRIVATE_DOWNLOAD_FILE_MODE\n } catch (err) {\n if (hasErrorCode(err, 'ENOENT')) return PRIVATE_DOWNLOAD_FILE_MODE\n throw err\n }\n}\n\n/**\n * Deletes a scanned local file under a sync root without re-resolving attacker-controlled parents.\n *\n * @param root - Local sync root.\n * @param scannedPath - Previously scanned local file metadata.\n *\n * @internal\n */\nexport async function deleteLocalFileInsideRoot(\n root: string,\n scannedPath: LocalSyncPath,\n): Promise<void> {\n if (root === '') {\n throw new Error('Local sync root required for filesystem mutation')\n }\n\n const { constants } = await import('node:fs')\n const { lstat, open, realpath, stat, unlink } = await import('node:fs/promises')\n const path = await import('node:path')\n const segments = safeRelativePathSegments(scannedPath.relativePath)\n const safeRoot = path.resolve(root)\n const rootStats = await lstat(safeRoot)\n if (rootStats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${scannedPath.relativePath}`)\n }\n if (!rootStats.isDirectory()) {\n throw new Error(`Local sync root is not a directory: ${scannedPath.relativePath}`)\n }\n const rootRealPath = await realpath(safeRoot)\n const expectedPath = path.join(rootRealPath, ...segments)\n assertPathInsideRoot(rootRealPath, expectedPath, path)\n\n const scannerPath = path.resolve(scannedPath.absolutePath)\n if (scannerPath !== expectedPath) {\n throw new Error(`Refusing to delete outside sync root: ${scannedPath.relativePath}`)\n }\n\n const [parentRealPath, parentStats] = await Promise.all([\n realpath(path.dirname(expectedPath)),\n stat(path.dirname(expectedPath)),\n ])\n const finalPath = path.join(parentRealPath, path.basename(expectedPath))\n assertPathInsideRoot(rootRealPath, finalPath, path)\n\n const platform = (globalThis as { process?: { platform?: string } }).process?.platform\n let parentHandle: Awaited<ReturnType<typeof open>> | undefined\n let anchoredParentPath: string | undefined\n /* v8 ignore start -- Linux-only fd-relative path support is covered by Linux CI */\n if (\n platform === 'linux' &&\n constants.O_DIRECTORY !== undefined &&\n localFileIoTestHooks.disableProcFdAnchoring !== true\n ) {\n try {\n await localFileIoTestHooks.beforeLocalDeleteOpenParent?.(parentRealPath)\n parentHandle = await open(\n parentRealPath,\n constants.O_RDONLY | constants.O_DIRECTORY | noFollowFlag(constants),\n )\n anchoredParentPath = `/proc/self/fd/${parentHandle.fd}`\n } catch (err) {\n if (hasErrorCode(err, 'ELOOP') || hasErrorCode(err, 'ENOTDIR')) {\n throw new Error('unsafe local delete path: parent is not a directory')\n }\n throw err\n }\n }\n /* v8 ignore stop */\n\n try {\n const unlinkPath =\n anchoredParentPath === undefined\n ? finalPath\n : path.join(anchoredParentPath, path.basename(expectedPath))\n const stats = await lstat(unlinkPath)\n assertSameScannedRegularFile(stats, { ...scannedPath, absolutePath: unlinkPath }, 'delete', {\n platform: localFileIoTestHooks.platform,\n })\n await localFileIoTestHooks.beforeLocalDeleteUnlink?.(parentRealPath)\n if (\n anchoredParentPath === undefined &&\n localFileIoTestHooks.disableProcFdAnchoring === true &&\n parentRealPath !== rootRealPath\n ) {\n throw new Error('unsafe local delete path: stable parent handle unavailable for unlink')\n }\n\n if (anchoredParentPath === undefined) {\n const [parentRealPathBeforeUnlink, parentStatsBeforeUnlink] = await Promise.all([\n realpath(path.dirname(expectedPath)),\n stat(path.dirname(expectedPath)),\n ])\n if (\n parentRealPathBeforeUnlink !== parentRealPath ||\n !sameParentIdentity(parentStatsBeforeUnlink, parentStats)\n ) {\n throw new Error('unsafe local delete path: parent changed before unlink')\n }\n }\n const finalStats = await lstat(unlinkPath)\n assertSameScannedRegularFile(\n finalStats,\n { ...scannedPath, absolutePath: unlinkPath },\n 'delete',\n { platform: localFileIoTestHooks.platform },\n )\n await unlink(unlinkPath)\n } finally {\n /* v8 ignore next -- best-effort cleanup */\n await parentHandle?.close().catch(() => {})\n }\n}\n\nfunction sameParentIdentity(\n current: { readonly dev: number | bigint; readonly ino: number | bigint },\n expected: { readonly dev: number | bigint; readonly ino: number | bigint },\n): boolean {\n return current.dev === expected.dev && current.ino === expected.ino\n}\n\nasync function writeAll(\n handle: {\n write(\n buffer: Uint8Array,\n offset: number,\n length: number,\n position: number,\n ): Promise<{ readonly bytesWritten: number }>\n },\n data: Uint8Array,\n position: number,\n): Promise<void> {\n let offset = 0\n while (offset < data.byteLength) {\n const { bytesWritten } = await handle.write(\n data,\n offset,\n data.byteLength - offset,\n position + offset,\n )\n /* v8 ignore next -- defensive: FileHandle.write should progress for non-empty chunks. */\n if (bytesWritten <= 0) throw new Error('download write made no progress')\n offset += bytesWritten\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ //#region src/sync/local-filesystem-root.ts
2
+ var localFilesystemRoots = /* @__PURE__ */ new WeakSet();
3
+ /**
4
+ * Privately marks SDK local folders that are backed by the filesystem.
5
+ * @param folder - Local folder instance to mark.
6
+ *
7
+ * @internal
8
+ */
9
+ function registerLocalFilesystemRoot(folder) {
10
+ localFilesystemRoots.add(folder);
11
+ }
12
+ /**
13
+ * Returns true for SDK local folders backed by the filesystem.
14
+ * @param folder - Sync folder to inspect.
15
+ *
16
+ * @returns True when the folder was registered as an SDK filesystem root.
17
+ *
18
+ * @internal
19
+ */
20
+ function isLocalFilesystemRoot(folder) {
21
+ return localFilesystemRoots.has(folder);
22
+ }
23
+ //#endregion
24
+ exports.isLocalFilesystemRoot = isLocalFilesystemRoot;
25
+ exports.registerLocalFilesystemRoot = registerLocalFilesystemRoot;
26
+
27
+ //# sourceMappingURL=local-filesystem-root.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-filesystem-root.cjs","names":[],"sources":["../../src/sync/local-filesystem-root.ts"],"sourcesContent":["import type { SyncFolder } from './types.ts'\n\nconst localFilesystemRoots = new WeakSet<SyncFolder>()\n\n/**\n * Privately marks SDK local folders that are backed by the filesystem.\n * @param folder - Local folder instance to mark.\n *\n * @internal\n */\nexport function registerLocalFilesystemRoot(folder: SyncFolder): void {\n localFilesystemRoots.add(folder)\n}\n\n/**\n * Returns true for SDK local folders backed by the filesystem.\n * @param folder - Sync folder to inspect.\n *\n * @returns True when the folder was registered as an SDK filesystem root.\n *\n * @internal\n */\nexport function isLocalFilesystemRoot(folder: SyncFolder): boolean {\n return localFilesystemRoots.has(folder)\n}\n"],"mappings":";AAEA,IAAM,uCAAuB,IAAI,QAAoB;;;;;;;AAQrD,SAAgB,4BAA4B,QAA0B;CACpE,qBAAqB,IAAI,MAAM;AACjC;;;;;;;;;AAUA,SAAgB,sBAAsB,QAA6B;CACjE,OAAO,qBAAqB,IAAI,MAAM;AACxC"}
@@ -0,0 +1,18 @@
1
+ import { SyncFolder } from './types.js';
2
+ /**
3
+ * Privately marks SDK local folders that are backed by the filesystem.
4
+ * @param folder - Local folder instance to mark.
5
+ *
6
+ * @internal
7
+ */
8
+ export declare function registerLocalFilesystemRoot(folder: SyncFolder): void;
9
+ /**
10
+ * Returns true for SDK local folders backed by the filesystem.
11
+ * @param folder - Sync folder to inspect.
12
+ *
13
+ * @returns True when the folder was registered as an SDK filesystem root.
14
+ *
15
+ * @internal
16
+ */
17
+ export declare function isLocalFilesystemRoot(folder: SyncFolder): boolean;
18
+ //# sourceMappingURL=local-filesystem-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-filesystem-root.d.ts","sourceRoot":"","sources":["../../src/sync/local-filesystem-root.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAI5C;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,18 @@
1
+ import { SyncFolder } from './types.js';
2
+ /**
3
+ * Privately marks SDK local folders that are backed by the filesystem.
4
+ * @param folder - Local folder instance to mark.
5
+ *
6
+ * @internal
7
+ */
8
+ export declare function registerLocalFilesystemRoot(folder: SyncFolder): void;
9
+ /**
10
+ * Returns true for SDK local folders backed by the filesystem.
11
+ * @param folder - Sync folder to inspect.
12
+ *
13
+ * @returns True when the folder was registered as an SDK filesystem root.
14
+ *
15
+ * @internal
16
+ */
17
+ export declare function isLocalFilesystemRoot(folder: SyncFolder): boolean;
18
+ //# sourceMappingURL=local-filesystem-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-filesystem-root.d.ts","sourceRoot":"","sources":["../../src/sync/local-filesystem-root.js"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAI5C;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAEpE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,26 @@
1
+ //#region src/sync/local-filesystem-root.ts
2
+ var localFilesystemRoots = /* @__PURE__ */ new WeakSet();
3
+ /**
4
+ * Privately marks SDK local folders that are backed by the filesystem.
5
+ * @param folder - Local folder instance to mark.
6
+ *
7
+ * @internal
8
+ */
9
+ function registerLocalFilesystemRoot(folder) {
10
+ localFilesystemRoots.add(folder);
11
+ }
12
+ /**
13
+ * Returns true for SDK local folders backed by the filesystem.
14
+ * @param folder - Sync folder to inspect.
15
+ *
16
+ * @returns True when the folder was registered as an SDK filesystem root.
17
+ *
18
+ * @internal
19
+ */
20
+ function isLocalFilesystemRoot(folder) {
21
+ return localFilesystemRoots.has(folder);
22
+ }
23
+ //#endregion
24
+ export { isLocalFilesystemRoot, registerLocalFilesystemRoot };
25
+
26
+ //# sourceMappingURL=local-filesystem-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-filesystem-root.js","names":[],"sources":["../../src/sync/local-filesystem-root.ts"],"sourcesContent":["import type { SyncFolder } from './types.ts'\n\nconst localFilesystemRoots = new WeakSet<SyncFolder>()\n\n/**\n * Privately marks SDK local folders that are backed by the filesystem.\n * @param folder - Local folder instance to mark.\n *\n * @internal\n */\nexport function registerLocalFilesystemRoot(folder: SyncFolder): void {\n localFilesystemRoots.add(folder)\n}\n\n/**\n * Returns true for SDK local folders backed by the filesystem.\n * @param folder - Sync folder to inspect.\n *\n * @returns True when the folder was registered as an SDK filesystem root.\n *\n * @internal\n */\nexport function isLocalFilesystemRoot(folder: SyncFolder): boolean {\n return localFilesystemRoots.has(folder)\n}\n"],"mappings":";AAEA,IAAM,uCAAuB,IAAI,QAAoB;;;;;;;AAQrD,SAAgB,4BAA4B,QAA0B;CACpE,qBAAqB,IAAI,MAAM;AACjC;;;;;;;;;AAUA,SAAgB,sBAAsB,QAA6B;CACjE,OAAO,qBAAqB,IAAI,MAAM;AACxC"}