@backblaze-labs/b2-sdk 0.2.0 → 0.3.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 (460) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/README.md +20 -2
  3. package/dist/_virtual/_b2-sdk-version-json.cjs +1 -1
  4. package/dist/_virtual/_b2-sdk-version-json.js +1 -1
  5. package/dist/auth/file.cjs.map +1 -1
  6. package/dist/backup/client.cjs +206 -0
  7. package/dist/backup/client.cjs.map +1 -0
  8. package/dist/backup/client.d.cts +238 -0
  9. package/dist/backup/client.d.cts.map +1 -0
  10. package/dist/backup/client.d.ts +238 -0
  11. package/dist/backup/client.d.ts.map +1 -0
  12. package/dist/backup/client.js +206 -0
  13. package/dist/backup/client.js.map +1 -0
  14. package/dist/backup/index.cjs +15 -0
  15. package/dist/backup/index.d.cts +22 -0
  16. package/dist/backup/index.d.cts.map +1 -0
  17. package/dist/backup/index.d.ts +22 -0
  18. package/dist/backup/index.d.ts.map +1 -0
  19. package/dist/backup/index.js +7 -0
  20. package/dist/backup/raw.cjs +146 -0
  21. package/dist/backup/raw.cjs.map +1 -0
  22. package/dist/backup/raw.d.cts +122 -0
  23. package/dist/backup/raw.d.cts.map +1 -0
  24. package/dist/backup/raw.d.ts +122 -0
  25. package/dist/backup/raw.d.ts.map +1 -0
  26. package/dist/backup/raw.js +146 -0
  27. package/dist/backup/raw.js.map +1 -0
  28. package/dist/bucket.cjs +2 -1
  29. package/dist/bucket.cjs.map +1 -1
  30. package/dist/bucket.js +2 -1
  31. package/dist/bucket.js.map +1 -1
  32. package/dist/copy/large.cjs +5 -3
  33. package/dist/copy/large.cjs.map +1 -1
  34. package/dist/copy/large.d.cts.map +1 -1
  35. package/dist/copy/large.d.ts.map +1 -1
  36. package/dist/copy/large.js +5 -3
  37. package/dist/copy/large.js.map +1 -1
  38. package/dist/download/checksum.cjs +6 -2
  39. package/dist/download/checksum.cjs.map +1 -1
  40. package/dist/download/checksum.js +6 -2
  41. package/dist/download/checksum.js.map +1 -1
  42. package/dist/download/parallel.cjs +52 -15
  43. package/dist/download/parallel.cjs.map +1 -1
  44. package/dist/download/parallel.d.cts +8 -1
  45. package/dist/download/parallel.d.cts.map +1 -1
  46. package/dist/download/parallel.d.ts +8 -1
  47. package/dist/download/parallel.d.ts.map +1 -1
  48. package/dist/download/parallel.js +52 -15
  49. package/dist/download/parallel.js.map +1 -1
  50. package/dist/download/single.cjs +141 -6
  51. package/dist/download/single.cjs.map +1 -1
  52. package/dist/download/single.d.cts.map +1 -1
  53. package/dist/download/single.d.ts.map +1 -1
  54. package/dist/download/single.js +141 -6
  55. package/dist/download/single.js.map +1 -1
  56. package/dist/errors/index.cjs +173 -3
  57. package/dist/errors/index.cjs.map +1 -1
  58. package/dist/errors/index.d.cts +119 -0
  59. package/dist/errors/index.d.cts.map +1 -1
  60. package/dist/errors/index.d.ts +119 -0
  61. package/dist/errors/index.d.ts.map +1 -1
  62. package/dist/errors/index.js +161 -4
  63. package/dist/errors/index.js.map +1 -1
  64. package/dist/http/retry.cjs +1 -1
  65. package/dist/http/retry.cjs.map +1 -1
  66. package/dist/http/retry.js +1 -1
  67. package/dist/http/retry.js.map +1 -1
  68. package/dist/http/transport.cjs +61 -23
  69. package/dist/http/transport.cjs.map +1 -1
  70. package/dist/http/transport.d.cts +25 -3
  71. package/dist/http/transport.d.cts.map +1 -1
  72. package/dist/http/transport.d.ts +25 -3
  73. package/dist/http/transport.d.ts.map +1 -1
  74. package/dist/http/transport.js +59 -22
  75. package/dist/http/transport.js.map +1 -1
  76. package/dist/index.cjs +25 -2
  77. package/dist/index.d.cts +6 -4
  78. package/dist/index.d.cts.map +1 -1
  79. package/dist/index.d.ts +6 -4
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +8 -6
  82. package/dist/object.cjs +1 -1
  83. package/dist/object.cjs.map +1 -1
  84. package/dist/object.d.cts +4 -2
  85. package/dist/object.d.cts.map +1 -1
  86. package/dist/object.d.ts +4 -2
  87. package/dist/object.d.ts.map +1 -1
  88. package/dist/object.js +1 -1
  89. package/dist/object.js.map +1 -1
  90. package/dist/partner/account-info.d.cts +43 -0
  91. package/dist/partner/account-info.d.cts.map +1 -0
  92. package/dist/partner/account-info.d.ts +43 -0
  93. package/dist/partner/account-info.d.ts.map +1 -0
  94. package/dist/partner/auth-clone.cjs +64 -0
  95. package/dist/partner/auth-clone.cjs.map +1 -0
  96. package/dist/partner/auth-clone.d.cts +27 -0
  97. package/dist/partner/auth-clone.d.cts.map +1 -0
  98. package/dist/partner/auth-clone.d.ts +27 -0
  99. package/dist/partner/auth-clone.d.ts.map +1 -0
  100. package/dist/partner/auth-clone.js +63 -0
  101. package/dist/partner/auth-clone.js.map +1 -0
  102. package/dist/partner/auth-core.cjs +175 -0
  103. package/dist/partner/auth-core.cjs.map +1 -0
  104. package/dist/partner/auth-core.d.cts +111 -0
  105. package/dist/partner/auth-core.d.cts.map +1 -0
  106. package/dist/partner/auth-core.d.ts +111 -0
  107. package/dist/partner/auth-core.d.ts.map +1 -0
  108. package/dist/partner/auth-core.js +175 -0
  109. package/dist/partner/auth-core.js.map +1 -0
  110. package/dist/partner/auth-shape.cjs +36 -0
  111. package/dist/partner/auth-shape.cjs.map +1 -0
  112. package/dist/partner/auth-shape.d.cts +12 -0
  113. package/dist/partner/auth-shape.d.cts.map +1 -0
  114. package/dist/partner/auth-shape.d.ts +12 -0
  115. package/dist/partner/auth-shape.d.ts.map +1 -0
  116. package/dist/partner/auth-shape.js +36 -0
  117. package/dist/partner/auth-shape.js.map +1 -0
  118. package/dist/partner/client.cjs +255 -0
  119. package/dist/partner/client.cjs.map +1 -0
  120. package/dist/partner/client.d.cts +335 -0
  121. package/dist/partner/client.d.cts.map +1 -0
  122. package/dist/partner/client.d.ts +335 -0
  123. package/dist/partner/client.d.ts.map +1 -0
  124. package/dist/partner/client.js +255 -0
  125. package/dist/partner/client.js.map +1 -0
  126. package/dist/partner/endpoint-url.cjs +108 -0
  127. package/dist/partner/endpoint-url.cjs.map +1 -0
  128. package/dist/partner/endpoint-url.d.cts +72 -0
  129. package/dist/partner/endpoint-url.d.cts.map +1 -0
  130. package/dist/partner/endpoint-url.d.ts +72 -0
  131. package/dist/partner/endpoint-url.d.ts.map +1 -0
  132. package/dist/partner/endpoint-url.js +105 -0
  133. package/dist/partner/endpoint-url.js.map +1 -0
  134. package/dist/partner/in-memory.cjs +135 -0
  135. package/dist/partner/in-memory.cjs.map +1 -0
  136. package/dist/partner/in-memory.d.cts +99 -0
  137. package/dist/partner/in-memory.d.cts.map +1 -0
  138. package/dist/partner/in-memory.d.ts +99 -0
  139. package/dist/partner/in-memory.d.ts.map +1 -0
  140. package/dist/partner/in-memory.js +135 -0
  141. package/dist/partner/in-memory.js.map +1 -0
  142. package/dist/partner/index.cjs +18 -0
  143. package/dist/partner/index.d.cts +23 -0
  144. package/dist/partner/index.d.cts.map +1 -0
  145. package/dist/partner/index.d.ts +23 -0
  146. package/dist/partner/index.d.ts.map +1 -0
  147. package/dist/partner/index.js +7 -0
  148. package/dist/partner/raw.cjs +392 -0
  149. package/dist/partner/raw.cjs.map +1 -0
  150. package/dist/partner/raw.d.cts +223 -0
  151. package/dist/partner/raw.d.cts.map +1 -0
  152. package/dist/partner/raw.d.ts +223 -0
  153. package/dist/partner/raw.d.ts.map +1 -0
  154. package/dist/partner/raw.js +390 -0
  155. package/dist/partner/raw.js.map +1 -0
  156. package/dist/partner/redaction-placeholders.cjs +10 -0
  157. package/dist/partner/redaction-placeholders.cjs.map +1 -0
  158. package/dist/partner/redaction-placeholders.d.cts +5 -0
  159. package/dist/partner/redaction-placeholders.d.cts.map +1 -0
  160. package/dist/partner/redaction-placeholders.d.ts +5 -0
  161. package/dist/partner/redaction-placeholders.d.ts.map +1 -0
  162. package/dist/partner/redaction-placeholders.js +9 -0
  163. package/dist/partner/redaction-placeholders.js.map +1 -0
  164. package/dist/partner/redaction.cjs +267 -0
  165. package/dist/partner/redaction.cjs.map +1 -0
  166. package/dist/partner/redaction.d.cts +124 -0
  167. package/dist/partner/redaction.d.cts.map +1 -0
  168. package/dist/partner/redaction.d.ts +124 -0
  169. package/dist/partner/redaction.d.ts.map +1 -0
  170. package/dist/partner/redaction.js +257 -0
  171. package/dist/partner/redaction.js.map +1 -0
  172. package/dist/raw/index.cjs +81 -19
  173. package/dist/raw/index.cjs.map +1 -1
  174. package/dist/raw/index.d.cts +6 -2
  175. package/dist/raw/index.d.cts.map +1 -1
  176. package/dist/raw/index.d.ts +6 -2
  177. package/dist/raw/index.d.ts.map +1 -1
  178. package/dist/raw/index.js +81 -19
  179. package/dist/raw/index.js.map +1 -1
  180. package/dist/raw/url.cjs +111 -0
  181. package/dist/raw/url.cjs.map +1 -0
  182. package/dist/raw/url.d.cts +41 -0
  183. package/dist/raw/url.d.cts.map +1 -0
  184. package/dist/raw/url.d.ts +41 -0
  185. package/dist/raw/url.d.ts.map +1 -0
  186. package/dist/raw/url.js +110 -0
  187. package/dist/raw/url.js.map +1 -0
  188. package/dist/s3/sigv4.cjs +15 -10
  189. package/dist/s3/sigv4.cjs.map +1 -1
  190. package/dist/s3/sigv4.js +15 -10
  191. package/dist/s3/sigv4.js.map +1 -1
  192. package/dist/simulator/index.cjs +1197 -206
  193. package/dist/simulator/index.cjs.map +1 -1
  194. package/dist/simulator/index.d.cts +109 -11
  195. package/dist/simulator/index.d.cts.map +1 -1
  196. package/dist/simulator/index.d.ts +109 -11
  197. package/dist/simulator/index.d.ts.map +1 -1
  198. package/dist/simulator/index.js +1196 -209
  199. package/dist/simulator/index.js.map +1 -1
  200. package/dist/simulator/partner.cjs +719 -0
  201. package/dist/simulator/partner.cjs.map +1 -0
  202. package/dist/simulator/partner.d.cts +183 -0
  203. package/dist/simulator/partner.d.cts.map +1 -0
  204. package/dist/simulator/partner.d.ts +183 -0
  205. package/dist/simulator/partner.d.ts.map +1 -0
  206. package/dist/simulator/partner.js +718 -0
  207. package/dist/simulator/partner.js.map +1 -0
  208. package/dist/simulator/validation.cjs +577 -0
  209. package/dist/simulator/validation.cjs.map +1 -1
  210. package/dist/simulator/validation.d.cts +117 -0
  211. package/dist/simulator/validation.d.cts.map +1 -1
  212. package/dist/simulator/validation.d.ts +117 -0
  213. package/dist/simulator/validation.d.ts.map +1 -1
  214. package/dist/simulator/validation.js +563 -1
  215. package/dist/simulator/validation.js.map +1 -1
  216. package/dist/streams/file-source.cjs +1 -1
  217. package/dist/streams/file-source.cjs.map +1 -1
  218. package/dist/streams/file-source.js +1 -1
  219. package/dist/streams/file-source.js.map +1 -1
  220. package/dist/streams/index.cjs +1 -1
  221. package/dist/streams/index.js +1 -1
  222. package/dist/streams/source.cjs +2 -1
  223. package/dist/streams/source.cjs.map +1 -1
  224. package/dist/streams/source.js +2 -1
  225. package/dist/streams/source.js.map +1 -1
  226. package/dist/sync/b2-sha1-reader.cjs +6 -2
  227. package/dist/sync/b2-sha1-reader.cjs.map +1 -1
  228. package/dist/sync/b2-sha1-reader.d.cts.map +1 -1
  229. package/dist/sync/b2-sha1-reader.d.ts.map +1 -1
  230. package/dist/sync/b2-sha1-reader.js +6 -2
  231. package/dist/sync/b2-sha1-reader.js.map +1 -1
  232. package/dist/sync/download-staging.cjs +11 -10
  233. package/dist/sync/download-staging.cjs.map +1 -1
  234. package/dist/sync/download-staging.d.cts +2 -1
  235. package/dist/sync/download-staging.d.cts.map +1 -1
  236. package/dist/sync/download-staging.d.ts +2 -1
  237. package/dist/sync/download-staging.d.ts.map +1 -1
  238. package/dist/sync/download-staging.js +11 -10
  239. package/dist/sync/download-staging.js.map +1 -1
  240. package/dist/sync/filters.cjs.map +1 -1
  241. package/dist/sync/filters.js.map +1 -1
  242. package/dist/sync/local-file-io.cjs +11 -6
  243. package/dist/sync/local-file-io.cjs.map +1 -1
  244. package/dist/sync/local-file-io.d.cts +1 -0
  245. package/dist/sync/local-file-io.d.cts.map +1 -1
  246. package/dist/sync/local-file-io.d.ts +1 -0
  247. package/dist/sync/local-file-io.d.ts.map +1 -1
  248. package/dist/sync/local-file-io.js +11 -6
  249. package/dist/sync/local-file-io.js.map +1 -1
  250. package/dist/sync/local-sha1.cjs +6 -15
  251. package/dist/sync/local-sha1.cjs.map +1 -1
  252. package/dist/sync/local-sha1.d.cts +0 -8
  253. package/dist/sync/local-sha1.d.cts.map +1 -1
  254. package/dist/sync/local-sha1.d.ts +0 -8
  255. package/dist/sync/local-sha1.d.ts.map +1 -1
  256. package/dist/sync/local-sha1.js +7 -15
  257. package/dist/sync/local-sha1.js.map +1 -1
  258. package/dist/sync/policies/compare.cjs +5 -3
  259. package/dist/sync/policies/compare.cjs.map +1 -1
  260. package/dist/sync/policies/compare.d.cts.map +1 -1
  261. package/dist/sync/policies/compare.d.ts.map +1 -1
  262. package/dist/sync/policies/compare.js +6 -4
  263. package/dist/sync/policies/compare.js.map +1 -1
  264. package/dist/sync/policies/index.cjs +4 -10
  265. package/dist/sync/policies/index.cjs.map +1 -1
  266. package/dist/sync/policies/index.js +4 -10
  267. package/dist/sync/policies/index.js.map +1 -1
  268. package/dist/sync/scanners/b2.cjs +2 -2
  269. package/dist/sync/scanners/b2.cjs.map +1 -1
  270. package/dist/sync/scanners/b2.js +2 -2
  271. package/dist/sync/scanners/b2.js.map +1 -1
  272. package/dist/sync/scanners/local.cjs.map +1 -1
  273. package/dist/sync/scanners/local.js.map +1 -1
  274. package/dist/sync/synchronizer.cjs +3 -2
  275. package/dist/sync/synchronizer.cjs.map +1 -1
  276. package/dist/sync/synchronizer.js +3 -2
  277. package/dist/sync/synchronizer.js.map +1 -1
  278. package/dist/types/auth.cjs +4 -0
  279. package/dist/types/auth.cjs.map +1 -1
  280. package/dist/types/auth.d.cts +4 -0
  281. package/dist/types/auth.d.cts.map +1 -1
  282. package/dist/types/auth.d.ts +4 -0
  283. package/dist/types/auth.d.ts.map +1 -1
  284. package/dist/types/auth.js +4 -0
  285. package/dist/types/auth.js.map +1 -1
  286. package/dist/types/backup.d.cts +80 -0
  287. package/dist/types/backup.d.cts.map +1 -0
  288. package/dist/types/backup.d.ts +80 -0
  289. package/dist/types/backup.d.ts.map +1 -0
  290. package/dist/types/bucket.cjs.map +1 -1
  291. package/dist/types/bucket.d.cts +4 -2
  292. package/dist/types/bucket.d.cts.map +1 -1
  293. package/dist/types/bucket.d.ts +4 -2
  294. package/dist/types/bucket.d.ts.map +1 -1
  295. package/dist/types/bucket.js.map +1 -1
  296. package/dist/types/download.cjs +62 -0
  297. package/dist/types/download.cjs.map +1 -0
  298. package/dist/types/download.d.cts +98 -0
  299. package/dist/types/download.d.cts.map +1 -1
  300. package/dist/types/download.d.ts +98 -0
  301. package/dist/types/download.d.ts.map +1 -1
  302. package/dist/types/download.js +61 -0
  303. package/dist/types/download.js.map +1 -0
  304. package/dist/types/encryption.cjs +58 -229
  305. package/dist/types/encryption.cjs.map +1 -1
  306. package/dist/types/encryption.d.cts +32 -0
  307. package/dist/types/encryption.d.cts.map +1 -1
  308. package/dist/types/encryption.d.ts +32 -0
  309. package/dist/types/encryption.d.ts.map +1 -1
  310. package/dist/types/encryption.js +56 -229
  311. package/dist/types/encryption.js.map +1 -1
  312. package/dist/types/errors.cjs +9 -0
  313. package/dist/types/errors.cjs.map +1 -1
  314. package/dist/types/errors.d.cts +1 -1
  315. package/dist/types/errors.d.cts.map +1 -1
  316. package/dist/types/errors.d.ts +1 -1
  317. package/dist/types/errors.d.ts.map +1 -1
  318. package/dist/types/errors.js +9 -0
  319. package/dist/types/errors.js.map +1 -1
  320. package/dist/types/file.cjs.map +1 -1
  321. package/dist/types/file.d.cts +15 -2
  322. package/dist/types/file.d.cts.map +1 -1
  323. package/dist/types/file.d.ts +15 -2
  324. package/dist/types/file.d.ts.map +1 -1
  325. package/dist/types/file.js.map +1 -1
  326. package/dist/types/ids.cjs +30 -0
  327. package/dist/types/ids.cjs.map +1 -1
  328. package/dist/types/ids.d.cts +32 -0
  329. package/dist/types/ids.d.cts.map +1 -1
  330. package/dist/types/ids.d.ts +32 -0
  331. package/dist/types/ids.d.ts.map +1 -1
  332. package/dist/types/ids.js +28 -1
  333. package/dist/types/ids.js.map +1 -1
  334. package/dist/types/index.d.cts +10 -6
  335. package/dist/types/index.d.cts.map +1 -1
  336. package/dist/types/index.d.ts +10 -6
  337. package/dist/types/index.d.ts.map +1 -1
  338. package/dist/types/lock.cjs.map +1 -1
  339. package/dist/types/lock.d.cts +14 -0
  340. package/dist/types/lock.d.cts.map +1 -1
  341. package/dist/types/lock.d.ts +14 -0
  342. package/dist/types/lock.d.ts.map +1 -1
  343. package/dist/types/lock.js.map +1 -1
  344. package/dist/types/notifications.cjs +1 -1
  345. package/dist/types/notifications.cjs.map +1 -1
  346. package/dist/types/notifications.d.cts +5 -3
  347. package/dist/types/notifications.d.cts.map +1 -1
  348. package/dist/types/notifications.d.ts +5 -3
  349. package/dist/types/notifications.d.ts.map +1 -1
  350. package/dist/types/notifications.js +1 -1
  351. package/dist/types/notifications.js.map +1 -1
  352. package/dist/types/partner.cjs +32 -0
  353. package/dist/types/partner.cjs.map +1 -0
  354. package/dist/types/partner.d.cts +394 -0
  355. package/dist/types/partner.d.cts.map +1 -0
  356. package/dist/types/partner.d.ts +394 -0
  357. package/dist/types/partner.d.ts.map +1 -0
  358. package/dist/types/partner.js +29 -0
  359. package/dist/types/partner.js.map +1 -0
  360. package/dist/types/upload.d.cts +31 -18
  361. package/dist/types/upload.d.cts.map +1 -1
  362. package/dist/types/upload.d.ts +31 -18
  363. package/dist/types/upload.d.ts.map +1 -1
  364. package/dist/upload/abort-scope.cjs +0 -45
  365. package/dist/upload/abort-scope.cjs.map +1 -1
  366. package/dist/upload/abort-scope.d.cts +0 -22
  367. package/dist/upload/abort-scope.d.cts.map +1 -1
  368. package/dist/upload/abort-scope.d.ts +0 -22
  369. package/dist/upload/abort-scope.d.ts.map +1 -1
  370. package/dist/upload/abort-scope.js +1 -44
  371. package/dist/upload/abort-scope.js.map +1 -1
  372. package/dist/upload/finish.cjs +3 -8
  373. package/dist/upload/finish.cjs.map +1 -1
  374. package/dist/upload/finish.d.cts.map +1 -1
  375. package/dist/upload/finish.d.ts.map +1 -1
  376. package/dist/upload/finish.js +1 -6
  377. package/dist/upload/finish.js.map +1 -1
  378. package/dist/upload/large.cjs +5 -1
  379. package/dist/upload/large.cjs.map +1 -1
  380. package/dist/upload/large.d.cts +16 -2
  381. package/dist/upload/large.d.cts.map +1 -1
  382. package/dist/upload/large.d.ts +16 -2
  383. package/dist/upload/large.d.ts.map +1 -1
  384. package/dist/upload/large.js +5 -1
  385. package/dist/upload/large.js.map +1 -1
  386. package/dist/upload/options.cjs.map +1 -1
  387. package/dist/upload/options.d.cts +12 -0
  388. package/dist/upload/options.d.cts.map +1 -1
  389. package/dist/upload/options.d.ts +12 -0
  390. package/dist/upload/options.d.ts.map +1 -1
  391. package/dist/upload/options.js.map +1 -1
  392. package/dist/upload/resume.cjs +2 -1
  393. package/dist/upload/resume.cjs.map +1 -1
  394. package/dist/upload/resume.d.cts +3 -1
  395. package/dist/upload/resume.d.cts.map +1 -1
  396. package/dist/upload/resume.d.ts +3 -1
  397. package/dist/upload/resume.d.ts.map +1 -1
  398. package/dist/upload/resume.js +2 -1
  399. package/dist/upload/resume.js.map +1 -1
  400. package/dist/upload/retry.cjs +8 -3
  401. package/dist/upload/retry.cjs.map +1 -1
  402. package/dist/upload/retry.js +8 -3
  403. package/dist/upload/retry.js.map +1 -1
  404. package/dist/upload/single.cjs +2 -1
  405. package/dist/upload/single.cjs.map +1 -1
  406. package/dist/upload/single.d.cts +6 -0
  407. package/dist/upload/single.d.cts.map +1 -1
  408. package/dist/upload/single.d.ts +6 -0
  409. package/dist/upload/single.d.ts.map +1 -1
  410. package/dist/upload/single.js +2 -1
  411. package/dist/upload/single.js.map +1 -1
  412. package/dist/upload/stream.cjs +6 -4
  413. package/dist/upload/stream.cjs.map +1 -1
  414. package/dist/upload/stream.d.cts +6 -0
  415. package/dist/upload/stream.d.cts.map +1 -1
  416. package/dist/upload/stream.d.ts +6 -0
  417. package/dist/upload/stream.d.ts.map +1 -1
  418. package/dist/upload/stream.js +3 -1
  419. package/dist/upload/stream.js.map +1 -1
  420. package/dist/util/abort.cjs +114 -0
  421. package/dist/util/abort.cjs.map +1 -0
  422. package/dist/util/abort.d.cts +65 -0
  423. package/dist/util/abort.d.cts.map +1 -0
  424. package/dist/util/abort.d.ts +65 -0
  425. package/dist/util/abort.d.ts.map +1 -0
  426. package/dist/util/abort.js +109 -0
  427. package/dist/util/abort.js.map +1 -0
  428. package/dist/util/defaults.cjs +3 -0
  429. package/dist/util/defaults.cjs.map +1 -1
  430. package/dist/util/defaults.d.cts +2 -0
  431. package/dist/util/defaults.d.cts.map +1 -1
  432. package/dist/util/defaults.d.ts +2 -0
  433. package/dist/util/defaults.d.ts.map +1 -1
  434. package/dist/util/defaults.js +3 -1
  435. package/dist/util/defaults.js.map +1 -1
  436. package/dist/util/md5.cjs +211 -0
  437. package/dist/util/md5.cjs.map +1 -0
  438. package/dist/util/md5.d.cts +43 -0
  439. package/dist/util/md5.d.cts.map +1 -0
  440. package/dist/util/md5.d.ts +43 -0
  441. package/dist/util/md5.d.ts.map +1 -0
  442. package/dist/util/md5.js +209 -0
  443. package/dist/util/md5.js.map +1 -0
  444. package/dist/util/normalize.cjs +5 -5
  445. package/dist/util/normalize.cjs.map +1 -1
  446. package/dist/util/normalize.d.cts +7 -7
  447. package/dist/util/normalize.d.cts.map +1 -1
  448. package/dist/util/normalize.d.ts +7 -7
  449. package/dist/util/normalize.d.ts.map +1 -1
  450. package/dist/util/normalize.js +5 -5
  451. package/dist/util/normalize.js.map +1 -1
  452. package/dist/util/paginator.cjs +7 -1
  453. package/dist/util/paginator.cjs.map +1 -1
  454. package/dist/util/paginator.d.cts +3 -1
  455. package/dist/util/paginator.d.cts.map +1 -1
  456. package/dist/util/paginator.d.ts +3 -1
  457. package/dist/util/paginator.d.ts.map +1 -1
  458. package/dist/util/paginator.js +7 -1
  459. package/dist/util/paginator.js.map +1 -1
  460. package/package.json +38 -16
@@ -1 +1 @@
1
- {"version":3,"file":"local.cjs","names":[],"sources":["../../../src/sync/scanners/local.ts"],"sourcesContent":["import {\n isDownloadStagingDirectorySegment,\n isManagedDownloadStagingRoot,\n} from '../download-staging.ts'\nimport { localFilesystemErrorReason } from '../filesystem-errors.ts'\nimport {\n directoryMayContainSyncPaths,\n pathPassesSyncFilters,\n pathSkippedByRegExpInputLimit,\n} from '../filters.ts'\nimport { localFileIdentityFromStats } from '../local-file-identity.ts'\nimport { registerLocalFilesystemRoot } from '../local-filesystem-root.ts'\nimport { compareSyncRelativePaths } from '../path-order.ts'\nimport { isReservedSyncTempFileName } from '../path-safety.ts'\nimport { validateSyncFilters } from '../regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from '../scan-events.ts'\nimport { assertScanEntryLimit, scanEntryLimit } from '../scan-limit.ts'\nimport type { LocalSyncPath, SyncErrorEvent, SyncFolder, SyncScanOptions } from '../types.ts'\n\ntype LocalDirent = {\n readonly name: string\n isDirectory(): boolean\n isFile(): boolean\n}\n\ntype LocalStats = {\n readonly dev: number\n readonly ino: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n readonly size: number\n isFile(): boolean\n}\n\ntype LocalNodeDeps = {\n readdir(path: string, options: { readonly withFileTypes: true }): Promise<LocalDirent[]>\n lstat(path: string): Promise<LocalStats>\n join(...paths: string[]): string\n relative(from: string, to: string): string\n resolve(...paths: string[]): string\n sep: string\n}\n\n/**\n * Scans a local directory tree and yields {@link LocalSyncPath} entries sorted by relative path.\n * A root directory read failure aborts the scan with an error diagnostic. Per-entry file or\n * directory failures are reported through `onError` and the scan continues over readable siblings.\n * SDK-managed partial download file names are skipped so unfinished internal\n * temp files are not synchronized.\n * The current implementation collects matching entries before sorting, so memory usage is\n * proportional to the number of matched files.\n */\nexport class LocalFolder implements SyncFolder {\n readonly type = 'local' as const\n readonly appliesScanFilters = true as const\n readonly appliesScanSorting = true as const\n /** Resolved absolute path to the local root directory. */\n readonly root: string\n\n /**\n * Creates a new LocalFolder for the given root directory.\n * @param root - Absolute or relative path to the local directory to scan.\n */\n constructor(root: string) {\n this.root = resolvePathAtConstruction(root)\n registerLocalFilesystemRoot(this)\n }\n\n /**\n * Recursively walks the directory and yields files in sync path order.\n * @param options - Optional scan controls.\n */\n async *scan(options: SyncScanOptions = {}): AsyncGenerator<LocalSyncPath> {\n validateSyncFilters(options)\n const nodeDeps = await loadLocalNodeDeps()\n const root = nodeDeps.resolve(this.root)\n const collected: LocalSyncPath[] = []\n await this.walk(root, root, collected, options, scanEntryLimit(options), nodeDeps)\n collected.sort((a, b) => compareSyncRelativePaths(a.relativePath, b.relativePath))\n for (const entry of collected) {\n throwIfScanAborted(options)\n yield entry\n }\n }\n\n /**\n * Recursively collects files from {@link dir} into {@link out}.\n * @param root - Resolved scan root used for relative path calculation.\n * @param dir - Absolute path of the directory to scan.\n * @param out - Accumulator array that receives discovered file entries.\n * @param options - Optional scan controls.\n * @param maxScanEntries - Maximum number of entries to retain before failing.\n * @param nodeDeps - Lazy-loaded Node filesystem and path helpers.\n */\n private async walk(\n root: string,\n dir: string,\n out: LocalSyncPath[],\n options: SyncScanOptions,\n maxScanEntries: number,\n nodeDeps: LocalNodeDeps,\n ): Promise<void> {\n throwIfScanAborted(options)\n let entries: LocalDirent[]\n try {\n entries = await nodeDeps.readdir(dir, { withFileTypes: true })\n } catch (err) {\n const error = this.emitScanError(\n options,\n relativePathFromRoot(root, dir, nodeDeps),\n 'directory',\n err,\n )\n if (dir === root) throw error\n return\n }\n\n for (const entry of entries) {\n throwIfScanAborted(options)\n const fullPath = nodeDeps.join(dir, entry.name)\n const rel = relativePathFromRoot(root, fullPath, nodeDeps)\n if (\n isDownloadStagingDirectorySegment(rel) &&\n entry.isDirectory() &&\n isDownloadStagingDirectorySegment(entry.name) &&\n (await isManagedDownloadStagingRoot(fullPath))\n ) {\n continue\n }\n if (rel.includes('\\\\')) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'unsafe-name',\n message: `Skipped local path ${JSON.stringify(rel)}: backslashes are not safe sync path characters`,\n })\n continue\n }\n if (isReservedSyncTempFileName(entry.name)) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'stale-download-partial',\n message: `Skipped local path ${JSON.stringify(rel)}: reserved SDK partial download file`,\n })\n continue\n }\n // Symlinks, FIFOs, sockets, and device nodes are not syncable files.\n // Ignore them without poisoning delete-mode orphan handling for unrelated paths.\n if (entry.isDirectory()) {\n if (directoryMayContainSyncPaths(rel, options)) {\n await this.walk(root, fullPath, out, options, maxScanEntries, nodeDeps)\n }\n } else if (entry.isFile()) {\n if (!pathPassesSyncFilters(rel, options)) {\n if (pathSkippedByRegExpInputLimit(rel, options)) {\n emitScannerSkip(options, regexpInputTooLongSkip(rel))\n }\n continue\n }\n let s: LocalStats\n try {\n s = await nodeDeps.lstat(fullPath)\n /* v8 ignore start -- lstat race after a Dirent file result is not deterministic */\n if (!s.isFile()) {\n this.emitScanError(options, rel, 'file', new Error('not a regular file'))\n continue\n }\n /* v8 ignore stop */\n } catch (err) {\n /* v8 ignore next -- stat TOCTOU failures are not deterministic to trigger */\n this.emitScanError(options, relativePathFromRoot(root, fullPath, nodeDeps), 'file', err)\n continue\n }\n assertScanEntryLimit(out.length + 1, maxScanEntries)\n out.push({\n relativePath: rel,\n absolutePath: fullPath,\n modTimeMillis: Math.floor(s.mtimeMs),\n size: s.size,\n fileIdentity: localFileIdentityFromStats(s),\n })\n }\n }\n }\n\n private emitScanError(\n options: SyncScanOptions,\n path: string,\n kind: 'directory' | 'file',\n err: unknown,\n ): Error {\n const event: SyncErrorEvent = {\n type: 'error',\n path,\n size: 0,\n message: `failed to scan local ${kind}: ${localFilesystemErrorReason(err)}`,\n }\n options.onError?.(event)\n return new Error(event.message)\n }\n}\n\nasync function loadLocalNodeDeps(): Promise<LocalNodeDeps> {\n const [fsPromises, path] = await Promise.all([import('node:fs/promises'), import('node:path')])\n return {\n readdir: fsPromises.readdir as LocalNodeDeps['readdir'],\n lstat: fsPromises.lstat as LocalNodeDeps['lstat'],\n join: path.join,\n relative: path.relative,\n resolve: path.resolve,\n sep: path.sep,\n }\n}\n\nfunction resolvePathAtConstruction(root: string): string {\n const processLike = (\n globalThis as {\n process?: { cwd?: () => string; platform?: string }\n }\n ).process\n if (typeof processLike?.cwd !== 'function') return root\n\n const cwd = processLike.cwd()\n if (processLike.platform === 'win32') return resolveWindowsPath(cwd, root)\n return resolvePosixPath(cwd, root)\n}\n\nfunction resolvePosixPath(cwd: string, root: string): string {\n const joined = root.startsWith('/') ? root : `${cwd}/${root}`\n const resolved = normalizePathSegments(joined.split('/'), '/')\n return resolved === '' ? '/' : `/${resolved}`\n}\n\nfunction resolveWindowsPath(cwd: string, root: string): string {\n const normalizedRoot = root.replaceAll('/', '\\\\')\n const normalizedCwd = cwd.replaceAll('/', '\\\\')\n const drive = /^[A-Za-z]:/.exec(normalizedCwd)?.[0] ?? ''\n const cwdUnc = splitUncPath(normalizedCwd)\n if (/^\\\\\\\\/.test(normalizedRoot)) return normalizeUncPath(normalizedRoot)\n if (/^[A-Za-z]:\\\\/.test(normalizedRoot)) {\n const prefix = normalizedRoot.slice(0, 2)\n const rest = normalizedRoot.slice(3).split('\\\\')\n return joinWindowsRoot(prefix, normalizePathSegments(rest, '\\\\'))\n }\n if (/^[A-Za-z]:/.test(normalizedRoot)) {\n throw new Error('LocalFolder root must not be a drive-relative Windows path')\n }\n if (normalizedRoot.startsWith('\\\\')) {\n const rest = normalizePathSegments(normalizedRoot.slice(1).split('\\\\'), '\\\\')\n return joinWindowsRoot(cwdUnc?.prefix ?? drive, rest)\n }\n if (cwdUnc !== undefined) {\n const resolved = normalizePathSegments([...cwdUnc.rest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(cwdUnc.prefix, resolved)\n }\n const base = /^[A-Za-z]:\\\\/.test(normalizedCwd) ? normalizedCwd : `${drive}\\\\`\n const prefix = /^[A-Za-z]:/.exec(base)?.[0] ?? drive\n const baseRest = base.slice(prefix.length).replace(/^\\\\/, '').split('\\\\')\n const resolved = normalizePathSegments([...baseRest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(prefix, resolved)\n}\n\nfunction normalizeUncPath(path: string): string {\n const unc = splitUncPath(path)\n if (unc === undefined) return path\n return joinWindowsRoot(unc.prefix, normalizePathSegments(unc.rest, '\\\\'))\n}\n\nfunction splitUncPath(\n path: string,\n): { readonly prefix: string; readonly rest: string[] } | undefined {\n if (!path.startsWith('\\\\\\\\')) return undefined\n const parts = path.split('\\\\').filter((part) => part !== '')\n const [server, share, ...rest] = parts\n if (server === undefined || share === undefined) return undefined\n return { prefix: `\\\\\\\\${server}\\\\${share}`, rest }\n}\n\nfunction joinWindowsRoot(prefix: string, rest: string): string {\n return rest === '' ? `${prefix}\\\\` : `${prefix}\\\\${rest}`\n}\n\nfunction normalizePathSegments(segments: readonly string[], separator: '/' | '\\\\'): string {\n const out: string[] = []\n for (const segment of segments) {\n if (segment === '' || segment === '.') continue\n if (segment === '..') {\n out.pop()\n continue\n }\n out.push(segment)\n }\n return out.join(separator)\n}\n\nfunction relativePathFromRoot(root: string, path: string, nodeDeps: LocalNodeDeps): string {\n return nodeDeps.relative(root, path).split(nodeDeps.sep).join('/')\n}\n\nfunction throwIfScanAborted(options: SyncScanOptions): void {\n if (options.signal?.aborted === true) {\n throw options.signal.reason ?? new DOMException('Aborted', 'AbortError')\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"local.cjs","names":[],"sources":["../../../src/sync/scanners/local.ts"],"sourcesContent":["import {\n isDownloadStagingDirectorySegment,\n isManagedDownloadStagingRoot,\n} from '../download-staging.ts'\nimport { localFilesystemErrorReason } from '../filesystem-errors.ts'\nimport {\n directoryMayContainSyncPaths,\n pathPassesSyncFilters,\n pathSkippedByRegExpInputLimit,\n} from '../filters.ts'\nimport { localFileIdentityFromStats } from '../local-file-identity.ts'\nimport { registerLocalFilesystemRoot } from '../local-filesystem-root.ts'\nimport { compareSyncRelativePaths } from '../path-order.ts'\nimport { isReservedSyncTempFileName } from '../path-safety.ts'\nimport { validateSyncFilters } from '../regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from '../scan-events.ts'\nimport { assertScanEntryLimit, scanEntryLimit } from '../scan-limit.ts'\nimport type { LocalSyncPath, SyncErrorEvent, SyncFolder, SyncScanOptions } from '../types.ts'\n\ntype LocalDirent = {\n readonly name: string\n isDirectory(): boolean\n isFile(): boolean\n}\n\ntype LocalStats = {\n readonly dev: number\n readonly ino: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n readonly size: number\n isFile(): boolean\n}\n\ntype LocalNodeDeps = {\n readdir(path: string, options: { readonly withFileTypes: true }): Promise<LocalDirent[]>\n lstat(path: string): Promise<LocalStats>\n join(...paths: string[]): string\n relative(from: string, to: string): string\n resolve(...paths: string[]): string\n sep: string\n}\n\n/**\n * Scans a local directory tree and yields {@link LocalSyncPath} entries sorted by relative path.\n * A root directory read failure aborts the scan with an error diagnostic. Per-entry file or\n * directory failures are reported through `onError` and the scan continues over readable siblings.\n * SDK-managed partial download file names are skipped so unfinished internal\n * temp files are not synchronized.\n * The current implementation collects matching entries before sorting, so memory usage is\n * proportional to the number of matched files.\n */\nexport class LocalFolder implements SyncFolder {\n readonly type = 'local' as const\n readonly appliesScanFilters = true as const\n readonly appliesScanSorting = true as const\n /** Resolved absolute path to the local root directory. */\n readonly root: string\n\n /**\n * Creates a new LocalFolder for the given root directory.\n * @param root - Absolute or relative path to the local directory to scan.\n */\n constructor(root: string) {\n this.root = resolvePathAtConstruction(root)\n registerLocalFilesystemRoot(this)\n }\n\n /**\n * Recursively walks the directory and yields files in sync path order.\n * @param options - Optional scan controls.\n */\n async *scan(options: SyncScanOptions = {}): AsyncGenerator<LocalSyncPath> {\n validateSyncFilters(options)\n const nodeDeps = await loadLocalNodeDeps()\n const root = nodeDeps.resolve(this.root)\n const collected: LocalSyncPath[] = []\n await this.walk(root, root, collected, options, scanEntryLimit(options), nodeDeps)\n collected.sort((a, b) => compareSyncRelativePaths(a.relativePath, b.relativePath))\n for (const entry of collected) {\n throwIfScanAborted(options)\n yield entry\n }\n }\n\n /**\n * Recursively collects files from {@link dir} into {@link out}.\n * @param root - Resolved scan root used for relative path calculation.\n * @param dir - Absolute path of the directory to scan.\n * @param out - Accumulator array that receives discovered file entries.\n * @param options - Optional scan controls.\n * @param maxScanEntries - Maximum number of entries to retain before failing.\n * @param nodeDeps - Lazy-loaded Node filesystem and path helpers.\n */\n private async walk(\n root: string,\n dir: string,\n out: LocalSyncPath[],\n options: SyncScanOptions,\n maxScanEntries: number,\n nodeDeps: LocalNodeDeps,\n ): Promise<void> {\n throwIfScanAborted(options)\n let entries: LocalDirent[]\n try {\n entries = await nodeDeps.readdir(dir, { withFileTypes: true })\n } catch (err) {\n const error = this.emitScanError(\n options,\n relativePathFromRoot(root, dir, nodeDeps),\n 'directory',\n err,\n )\n if (dir === root) throw error\n return\n }\n\n for (const entry of entries) {\n throwIfScanAborted(options)\n const fullPath = nodeDeps.join(dir, entry.name)\n const rel = relativePathFromRoot(root, fullPath, nodeDeps)\n if (\n isDownloadStagingDirectorySegment(rel) &&\n entry.isDirectory() &&\n isDownloadStagingDirectorySegment(entry.name) &&\n (await isManagedDownloadStagingRoot(fullPath))\n ) {\n continue\n }\n if (rel.includes('\\\\')) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'unsafe-name',\n message: `Skipped local path ${JSON.stringify(rel)}: backslashes are not safe sync path characters`,\n })\n continue\n }\n if (isReservedSyncTempFileName(entry.name)) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'stale-download-partial',\n message: `Skipped local path ${JSON.stringify(rel)}: reserved SDK partial download file`,\n })\n continue\n }\n // Symlinks, FIFOs, sockets, and device nodes are not syncable files.\n // Ignore them without poisoning delete-mode orphan handling for unrelated paths.\n if (entry.isDirectory()) {\n if (directoryMayContainSyncPaths(rel, options)) {\n await this.walk(root, fullPath, out, options, maxScanEntries, nodeDeps)\n }\n } else if (entry.isFile()) {\n if (!pathPassesSyncFilters(rel, options)) {\n if (pathSkippedByRegExpInputLimit(rel, options)) {\n emitScannerSkip(options, regexpInputTooLongSkip(rel))\n }\n continue\n }\n let s: LocalStats\n try {\n s = await nodeDeps.lstat(fullPath)\n /* v8 ignore start -- lstat race after a Dirent file result is not deterministic */\n if (!s.isFile()) {\n this.emitScanError(options, rel, 'file', new Error('not a regular file'))\n continue\n }\n /* v8 ignore stop */\n } catch (err) {\n /* v8 ignore next -- stat TOCTOU failures are not deterministic to trigger */\n this.emitScanError(options, relativePathFromRoot(root, fullPath, nodeDeps), 'file', err)\n continue\n }\n assertScanEntryLimit(out.length + 1, maxScanEntries)\n out.push({\n relativePath: rel,\n absolutePath: fullPath,\n modTimeMillis: Math.floor(s.mtimeMs),\n size: s.size,\n fileIdentity: localFileIdentityFromStats(s),\n })\n }\n }\n }\n\n private emitScanError(\n options: SyncScanOptions,\n path: string,\n kind: 'directory' | 'file',\n err: unknown,\n ): Error {\n const event: SyncErrorEvent = {\n type: 'error',\n path,\n size: 0,\n message: `failed to scan local ${kind}: ${localFilesystemErrorReason(err)}`,\n }\n options.onError?.(event)\n return new Error(event.message)\n }\n}\n\nasync function loadLocalNodeDeps(): Promise<LocalNodeDeps> {\n const [fsPromises, path] = await Promise.all([import('node:fs/promises'), import('node:path')])\n return {\n readdir: fsPromises.readdir as LocalNodeDeps['readdir'],\n lstat: fsPromises.lstat as LocalNodeDeps['lstat'],\n join: path.join,\n relative: path.relative,\n resolve: path.resolve,\n sep: path.sep,\n }\n}\n\nfunction resolvePathAtConstruction(root: string): string {\n const processLike = (\n globalThis as {\n process?: { cwd?: () => string; platform?: string }\n }\n ).process\n if (typeof processLike?.cwd !== 'function') return root\n\n const cwd = processLike.cwd()\n if (processLike.platform === 'win32') return resolveWindowsPath(cwd, root)\n return resolvePosixPath(cwd, root)\n}\n\nfunction resolvePosixPath(cwd: string, root: string): string {\n const joined = root.startsWith('/') ? root : `${cwd}/${root}`\n const resolved = normalizePathSegments(joined.split('/'), '/')\n return resolved === '' ? '/' : `/${resolved}`\n}\n\nfunction resolveWindowsPath(cwd: string, root: string): string {\n const normalizedRoot = root.replaceAll('/', '\\\\')\n const normalizedCwd = cwd.replaceAll('/', '\\\\')\n const drive = /^[A-Za-z]:/.exec(normalizedCwd)?.[0] ?? ''\n const cwdUnc = splitUncPath(normalizedCwd)\n if (/^\\\\\\\\/.test(normalizedRoot)) return normalizeUncPath(normalizedRoot)\n if (/^[A-Za-z]:\\\\/.test(normalizedRoot)) {\n const prefix = normalizedRoot.slice(0, 2)\n const rest = normalizedRoot.slice(3).split('\\\\')\n return joinWindowsRoot(prefix, normalizePathSegments(rest, '\\\\'))\n }\n if (/^[A-Za-z]:/.test(normalizedRoot)) {\n throw new Error('LocalFolder root must not be a drive-relative Windows path')\n }\n if (normalizedRoot.startsWith('\\\\')) {\n const rest = normalizePathSegments(normalizedRoot.slice(1).split('\\\\'), '\\\\')\n return joinWindowsRoot(cwdUnc?.prefix ?? drive, rest)\n }\n if (cwdUnc !== undefined) {\n const resolved = normalizePathSegments([...cwdUnc.rest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(cwdUnc.prefix, resolved)\n }\n const base = /^[A-Za-z]:\\\\/.test(normalizedCwd) ? normalizedCwd : `${drive}\\\\`\n const prefix = /^[A-Za-z]:/.exec(base)?.[0] ?? drive\n const baseRest = base.slice(prefix.length).replace(/^\\\\/, '').split('\\\\')\n const resolved = normalizePathSegments([...baseRest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(prefix, resolved)\n}\n\nfunction normalizeUncPath(path: string): string {\n const unc = splitUncPath(path)\n if (unc === undefined) return path\n return joinWindowsRoot(unc.prefix, normalizePathSegments(unc.rest, '\\\\'))\n}\n\nfunction splitUncPath(\n path: string,\n): { readonly prefix: string; readonly rest: string[] } | undefined {\n if (!path.startsWith('\\\\\\\\')) return undefined\n const parts = path.split('\\\\').filter((part) => part !== '')\n const [server, share, ...rest] = parts\n if (server === undefined || share === undefined) return undefined\n return { prefix: `\\\\\\\\${server}\\\\${share}`, rest }\n}\n\nfunction joinWindowsRoot(prefix: string, rest: string): string {\n return rest === '' ? `${prefix}\\\\` : `${prefix}\\\\${rest}`\n}\n\nfunction normalizePathSegments(segments: readonly string[], separator: '/' | '\\\\'): string {\n const out: string[] = []\n for (const segment of segments) {\n if (segment === '' || segment === '.') continue\n if (segment === '..') {\n out.pop()\n continue\n }\n out.push(segment)\n }\n return out.join(separator)\n}\n\nfunction relativePathFromRoot(root: string, path: string, nodeDeps: LocalNodeDeps): string {\n return nodeDeps.relative(root, path).split(nodeDeps.sep).join('/')\n}\n\nfunction throwIfScanAborted(options: SyncScanOptions): void {\n if (options.signal?.aborted === true) {\n throw options.signal.reason ?? new DOMException('Aborted', 'AbortError')\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"local.js","names":[],"sources":["../../../src/sync/scanners/local.ts"],"sourcesContent":["import {\n isDownloadStagingDirectorySegment,\n isManagedDownloadStagingRoot,\n} from '../download-staging.ts'\nimport { localFilesystemErrorReason } from '../filesystem-errors.ts'\nimport {\n directoryMayContainSyncPaths,\n pathPassesSyncFilters,\n pathSkippedByRegExpInputLimit,\n} from '../filters.ts'\nimport { localFileIdentityFromStats } from '../local-file-identity.ts'\nimport { registerLocalFilesystemRoot } from '../local-filesystem-root.ts'\nimport { compareSyncRelativePaths } from '../path-order.ts'\nimport { isReservedSyncTempFileName } from '../path-safety.ts'\nimport { validateSyncFilters } from '../regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from '../scan-events.ts'\nimport { assertScanEntryLimit, scanEntryLimit } from '../scan-limit.ts'\nimport type { LocalSyncPath, SyncErrorEvent, SyncFolder, SyncScanOptions } from '../types.ts'\n\ntype LocalDirent = {\n readonly name: string\n isDirectory(): boolean\n isFile(): boolean\n}\n\ntype LocalStats = {\n readonly dev: number\n readonly ino: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n readonly size: number\n isFile(): boolean\n}\n\ntype LocalNodeDeps = {\n readdir(path: string, options: { readonly withFileTypes: true }): Promise<LocalDirent[]>\n lstat(path: string): Promise<LocalStats>\n join(...paths: string[]): string\n relative(from: string, to: string): string\n resolve(...paths: string[]): string\n sep: string\n}\n\n/**\n * Scans a local directory tree and yields {@link LocalSyncPath} entries sorted by relative path.\n * A root directory read failure aborts the scan with an error diagnostic. Per-entry file or\n * directory failures are reported through `onError` and the scan continues over readable siblings.\n * SDK-managed partial download file names are skipped so unfinished internal\n * temp files are not synchronized.\n * The current implementation collects matching entries before sorting, so memory usage is\n * proportional to the number of matched files.\n */\nexport class LocalFolder implements SyncFolder {\n readonly type = 'local' as const\n readonly appliesScanFilters = true as const\n readonly appliesScanSorting = true as const\n /** Resolved absolute path to the local root directory. */\n readonly root: string\n\n /**\n * Creates a new LocalFolder for the given root directory.\n * @param root - Absolute or relative path to the local directory to scan.\n */\n constructor(root: string) {\n this.root = resolvePathAtConstruction(root)\n registerLocalFilesystemRoot(this)\n }\n\n /**\n * Recursively walks the directory and yields files in sync path order.\n * @param options - Optional scan controls.\n */\n async *scan(options: SyncScanOptions = {}): AsyncGenerator<LocalSyncPath> {\n validateSyncFilters(options)\n const nodeDeps = await loadLocalNodeDeps()\n const root = nodeDeps.resolve(this.root)\n const collected: LocalSyncPath[] = []\n await this.walk(root, root, collected, options, scanEntryLimit(options), nodeDeps)\n collected.sort((a, b) => compareSyncRelativePaths(a.relativePath, b.relativePath))\n for (const entry of collected) {\n throwIfScanAborted(options)\n yield entry\n }\n }\n\n /**\n * Recursively collects files from {@link dir} into {@link out}.\n * @param root - Resolved scan root used for relative path calculation.\n * @param dir - Absolute path of the directory to scan.\n * @param out - Accumulator array that receives discovered file entries.\n * @param options - Optional scan controls.\n * @param maxScanEntries - Maximum number of entries to retain before failing.\n * @param nodeDeps - Lazy-loaded Node filesystem and path helpers.\n */\n private async walk(\n root: string,\n dir: string,\n out: LocalSyncPath[],\n options: SyncScanOptions,\n maxScanEntries: number,\n nodeDeps: LocalNodeDeps,\n ): Promise<void> {\n throwIfScanAborted(options)\n let entries: LocalDirent[]\n try {\n entries = await nodeDeps.readdir(dir, { withFileTypes: true })\n } catch (err) {\n const error = this.emitScanError(\n options,\n relativePathFromRoot(root, dir, nodeDeps),\n 'directory',\n err,\n )\n if (dir === root) throw error\n return\n }\n\n for (const entry of entries) {\n throwIfScanAborted(options)\n const fullPath = nodeDeps.join(dir, entry.name)\n const rel = relativePathFromRoot(root, fullPath, nodeDeps)\n if (\n isDownloadStagingDirectorySegment(rel) &&\n entry.isDirectory() &&\n isDownloadStagingDirectorySegment(entry.name) &&\n (await isManagedDownloadStagingRoot(fullPath))\n ) {\n continue\n }\n if (rel.includes('\\\\')) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'unsafe-name',\n message: `Skipped local path ${JSON.stringify(rel)}: backslashes are not safe sync path characters`,\n })\n continue\n }\n if (isReservedSyncTempFileName(entry.name)) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'stale-download-partial',\n message: `Skipped local path ${JSON.stringify(rel)}: reserved SDK partial download file`,\n })\n continue\n }\n // Symlinks, FIFOs, sockets, and device nodes are not syncable files.\n // Ignore them without poisoning delete-mode orphan handling for unrelated paths.\n if (entry.isDirectory()) {\n if (directoryMayContainSyncPaths(rel, options)) {\n await this.walk(root, fullPath, out, options, maxScanEntries, nodeDeps)\n }\n } else if (entry.isFile()) {\n if (!pathPassesSyncFilters(rel, options)) {\n if (pathSkippedByRegExpInputLimit(rel, options)) {\n emitScannerSkip(options, regexpInputTooLongSkip(rel))\n }\n continue\n }\n let s: LocalStats\n try {\n s = await nodeDeps.lstat(fullPath)\n /* v8 ignore start -- lstat race after a Dirent file result is not deterministic */\n if (!s.isFile()) {\n this.emitScanError(options, rel, 'file', new Error('not a regular file'))\n continue\n }\n /* v8 ignore stop */\n } catch (err) {\n /* v8 ignore next -- stat TOCTOU failures are not deterministic to trigger */\n this.emitScanError(options, relativePathFromRoot(root, fullPath, nodeDeps), 'file', err)\n continue\n }\n assertScanEntryLimit(out.length + 1, maxScanEntries)\n out.push({\n relativePath: rel,\n absolutePath: fullPath,\n modTimeMillis: Math.floor(s.mtimeMs),\n size: s.size,\n fileIdentity: localFileIdentityFromStats(s),\n })\n }\n }\n }\n\n private emitScanError(\n options: SyncScanOptions,\n path: string,\n kind: 'directory' | 'file',\n err: unknown,\n ): Error {\n const event: SyncErrorEvent = {\n type: 'error',\n path,\n size: 0,\n message: `failed to scan local ${kind}: ${localFilesystemErrorReason(err)}`,\n }\n options.onError?.(event)\n return new Error(event.message)\n }\n}\n\nasync function loadLocalNodeDeps(): Promise<LocalNodeDeps> {\n const [fsPromises, path] = await Promise.all([import('node:fs/promises'), import('node:path')])\n return {\n readdir: fsPromises.readdir as LocalNodeDeps['readdir'],\n lstat: fsPromises.lstat as LocalNodeDeps['lstat'],\n join: path.join,\n relative: path.relative,\n resolve: path.resolve,\n sep: path.sep,\n }\n}\n\nfunction resolvePathAtConstruction(root: string): string {\n const processLike = (\n globalThis as {\n process?: { cwd?: () => string; platform?: string }\n }\n ).process\n if (typeof processLike?.cwd !== 'function') return root\n\n const cwd = processLike.cwd()\n if (processLike.platform === 'win32') return resolveWindowsPath(cwd, root)\n return resolvePosixPath(cwd, root)\n}\n\nfunction resolvePosixPath(cwd: string, root: string): string {\n const joined = root.startsWith('/') ? root : `${cwd}/${root}`\n const resolved = normalizePathSegments(joined.split('/'), '/')\n return resolved === '' ? '/' : `/${resolved}`\n}\n\nfunction resolveWindowsPath(cwd: string, root: string): string {\n const normalizedRoot = root.replaceAll('/', '\\\\')\n const normalizedCwd = cwd.replaceAll('/', '\\\\')\n const drive = /^[A-Za-z]:/.exec(normalizedCwd)?.[0] ?? ''\n const cwdUnc = splitUncPath(normalizedCwd)\n if (/^\\\\\\\\/.test(normalizedRoot)) return normalizeUncPath(normalizedRoot)\n if (/^[A-Za-z]:\\\\/.test(normalizedRoot)) {\n const prefix = normalizedRoot.slice(0, 2)\n const rest = normalizedRoot.slice(3).split('\\\\')\n return joinWindowsRoot(prefix, normalizePathSegments(rest, '\\\\'))\n }\n if (/^[A-Za-z]:/.test(normalizedRoot)) {\n throw new Error('LocalFolder root must not be a drive-relative Windows path')\n }\n if (normalizedRoot.startsWith('\\\\')) {\n const rest = normalizePathSegments(normalizedRoot.slice(1).split('\\\\'), '\\\\')\n return joinWindowsRoot(cwdUnc?.prefix ?? drive, rest)\n }\n if (cwdUnc !== undefined) {\n const resolved = normalizePathSegments([...cwdUnc.rest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(cwdUnc.prefix, resolved)\n }\n const base = /^[A-Za-z]:\\\\/.test(normalizedCwd) ? normalizedCwd : `${drive}\\\\`\n const prefix = /^[A-Za-z]:/.exec(base)?.[0] ?? drive\n const baseRest = base.slice(prefix.length).replace(/^\\\\/, '').split('\\\\')\n const resolved = normalizePathSegments([...baseRest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(prefix, resolved)\n}\n\nfunction normalizeUncPath(path: string): string {\n const unc = splitUncPath(path)\n if (unc === undefined) return path\n return joinWindowsRoot(unc.prefix, normalizePathSegments(unc.rest, '\\\\'))\n}\n\nfunction splitUncPath(\n path: string,\n): { readonly prefix: string; readonly rest: string[] } | undefined {\n if (!path.startsWith('\\\\\\\\')) return undefined\n const parts = path.split('\\\\').filter((part) => part !== '')\n const [server, share, ...rest] = parts\n if (server === undefined || share === undefined) return undefined\n return { prefix: `\\\\\\\\${server}\\\\${share}`, rest }\n}\n\nfunction joinWindowsRoot(prefix: string, rest: string): string {\n return rest === '' ? `${prefix}\\\\` : `${prefix}\\\\${rest}`\n}\n\nfunction normalizePathSegments(segments: readonly string[], separator: '/' | '\\\\'): string {\n const out: string[] = []\n for (const segment of segments) {\n if (segment === '' || segment === '.') continue\n if (segment === '..') {\n out.pop()\n continue\n }\n out.push(segment)\n }\n return out.join(separator)\n}\n\nfunction relativePathFromRoot(root: string, path: string, nodeDeps: LocalNodeDeps): string {\n return nodeDeps.relative(root, path).split(nodeDeps.sep).join('/')\n}\n\nfunction throwIfScanAborted(options: SyncScanOptions): void {\n if (options.signal?.aborted === true) {\n throw options.signal.reason ?? new DOMException('Aborted', 'AbortError')\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"local.js","names":[],"sources":["../../../src/sync/scanners/local.ts"],"sourcesContent":["import {\n isDownloadStagingDirectorySegment,\n isManagedDownloadStagingRoot,\n} from '../download-staging.ts'\nimport { localFilesystemErrorReason } from '../filesystem-errors.ts'\nimport {\n directoryMayContainSyncPaths,\n pathPassesSyncFilters,\n pathSkippedByRegExpInputLimit,\n} from '../filters.ts'\nimport { localFileIdentityFromStats } from '../local-file-identity.ts'\nimport { registerLocalFilesystemRoot } from '../local-filesystem-root.ts'\nimport { compareSyncRelativePaths } from '../path-order.ts'\nimport { isReservedSyncTempFileName } from '../path-safety.ts'\nimport { validateSyncFilters } from '../regexp-safety.ts'\nimport { emitScannerSkip, regexpInputTooLongSkip } from '../scan-events.ts'\nimport { assertScanEntryLimit, scanEntryLimit } from '../scan-limit.ts'\nimport type { LocalSyncPath, SyncErrorEvent, SyncFolder, SyncScanOptions } from '../types.ts'\n\ntype LocalDirent = {\n readonly name: string\n isDirectory(): boolean\n isFile(): boolean\n}\n\ntype LocalStats = {\n readonly dev: number\n readonly ino: number\n readonly mtimeMs: number\n readonly ctimeMs: number\n readonly size: number\n isFile(): boolean\n}\n\ntype LocalNodeDeps = {\n readdir(path: string, options: { readonly withFileTypes: true }): Promise<LocalDirent[]>\n lstat(path: string): Promise<LocalStats>\n join(...paths: string[]): string\n relative(from: string, to: string): string\n resolve(...paths: string[]): string\n sep: string\n}\n\n/**\n * Scans a local directory tree and yields {@link LocalSyncPath} entries sorted by relative path.\n * A root directory read failure aborts the scan with an error diagnostic. Per-entry file or\n * directory failures are reported through `onError` and the scan continues over readable siblings.\n * SDK-managed partial download file names are skipped so unfinished internal\n * temp files are not synchronized.\n * The current implementation collects matching entries before sorting, so memory usage is\n * proportional to the number of matched files.\n */\nexport class LocalFolder implements SyncFolder {\n readonly type = 'local' as const\n readonly appliesScanFilters = true as const\n readonly appliesScanSorting = true as const\n /** Resolved absolute path to the local root directory. */\n readonly root: string\n\n /**\n * Creates a new LocalFolder for the given root directory.\n * @param root - Absolute or relative path to the local directory to scan.\n */\n constructor(root: string) {\n this.root = resolvePathAtConstruction(root)\n registerLocalFilesystemRoot(this)\n }\n\n /**\n * Recursively walks the directory and yields files in sync path order.\n * @param options - Optional scan controls.\n */\n async *scan(options: SyncScanOptions = {}): AsyncGenerator<LocalSyncPath> {\n validateSyncFilters(options)\n const nodeDeps = await loadLocalNodeDeps()\n const root = nodeDeps.resolve(this.root)\n const collected: LocalSyncPath[] = []\n await this.walk(root, root, collected, options, scanEntryLimit(options), nodeDeps)\n collected.sort((a, b) => compareSyncRelativePaths(a.relativePath, b.relativePath))\n for (const entry of collected) {\n throwIfScanAborted(options)\n yield entry\n }\n }\n\n /**\n * Recursively collects files from {@link dir} into {@link out}.\n * @param root - Resolved scan root used for relative path calculation.\n * @param dir - Absolute path of the directory to scan.\n * @param out - Accumulator array that receives discovered file entries.\n * @param options - Optional scan controls.\n * @param maxScanEntries - Maximum number of entries to retain before failing.\n * @param nodeDeps - Lazy-loaded Node filesystem and path helpers.\n */\n private async walk(\n root: string,\n dir: string,\n out: LocalSyncPath[],\n options: SyncScanOptions,\n maxScanEntries: number,\n nodeDeps: LocalNodeDeps,\n ): Promise<void> {\n throwIfScanAborted(options)\n let entries: LocalDirent[]\n try {\n entries = await nodeDeps.readdir(dir, { withFileTypes: true })\n } catch (err) {\n const error = this.emitScanError(\n options,\n relativePathFromRoot(root, dir, nodeDeps),\n 'directory',\n err,\n )\n if (dir === root) throw error\n return\n }\n\n for (const entry of entries) {\n throwIfScanAborted(options)\n const fullPath = nodeDeps.join(dir, entry.name)\n const rel = relativePathFromRoot(root, fullPath, nodeDeps)\n if (\n isDownloadStagingDirectorySegment(rel) &&\n entry.isDirectory() &&\n isDownloadStagingDirectorySegment(entry.name) &&\n (await isManagedDownloadStagingRoot(fullPath))\n ) {\n continue\n }\n if (rel.includes('\\\\')) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'unsafe-name',\n message: `Skipped local path ${JSON.stringify(rel)}: backslashes are not safe sync path characters`,\n })\n continue\n }\n if (isReservedSyncTempFileName(entry.name)) {\n emitScannerSkip(options, {\n type: 'skip',\n path: rel,\n size: 0,\n reason: 'stale-download-partial',\n message: `Skipped local path ${JSON.stringify(rel)}: reserved SDK partial download file`,\n })\n continue\n }\n // Symlinks, FIFOs, sockets, and device nodes are not syncable files.\n // Ignore them without poisoning delete-mode orphan handling for unrelated paths.\n if (entry.isDirectory()) {\n if (directoryMayContainSyncPaths(rel, options)) {\n await this.walk(root, fullPath, out, options, maxScanEntries, nodeDeps)\n }\n } else if (entry.isFile()) {\n if (!pathPassesSyncFilters(rel, options)) {\n if (pathSkippedByRegExpInputLimit(rel, options)) {\n emitScannerSkip(options, regexpInputTooLongSkip(rel))\n }\n continue\n }\n let s: LocalStats\n try {\n s = await nodeDeps.lstat(fullPath)\n /* v8 ignore start -- lstat race after a Dirent file result is not deterministic */\n if (!s.isFile()) {\n this.emitScanError(options, rel, 'file', new Error('not a regular file'))\n continue\n }\n /* v8 ignore stop */\n } catch (err) {\n /* v8 ignore next -- stat TOCTOU failures are not deterministic to trigger */\n this.emitScanError(options, relativePathFromRoot(root, fullPath, nodeDeps), 'file', err)\n continue\n }\n assertScanEntryLimit(out.length + 1, maxScanEntries)\n out.push({\n relativePath: rel,\n absolutePath: fullPath,\n modTimeMillis: Math.floor(s.mtimeMs),\n size: s.size,\n fileIdentity: localFileIdentityFromStats(s),\n })\n }\n }\n }\n\n private emitScanError(\n options: SyncScanOptions,\n path: string,\n kind: 'directory' | 'file',\n err: unknown,\n ): Error {\n const event: SyncErrorEvent = {\n type: 'error',\n path,\n size: 0,\n message: `failed to scan local ${kind}: ${localFilesystemErrorReason(err)}`,\n }\n options.onError?.(event)\n return new Error(event.message)\n }\n}\n\nasync function loadLocalNodeDeps(): Promise<LocalNodeDeps> {\n const [fsPromises, path] = await Promise.all([import('node:fs/promises'), import('node:path')])\n return {\n readdir: fsPromises.readdir as LocalNodeDeps['readdir'],\n lstat: fsPromises.lstat as LocalNodeDeps['lstat'],\n join: path.join,\n relative: path.relative,\n resolve: path.resolve,\n sep: path.sep,\n }\n}\n\nfunction resolvePathAtConstruction(root: string): string {\n const processLike = (\n globalThis as {\n process?: { cwd?: () => string; platform?: string }\n }\n ).process\n if (typeof processLike?.cwd !== 'function') return root\n\n const cwd = processLike.cwd()\n if (processLike.platform === 'win32') return resolveWindowsPath(cwd, root)\n return resolvePosixPath(cwd, root)\n}\n\nfunction resolvePosixPath(cwd: string, root: string): string {\n const joined = root.startsWith('/') ? root : `${cwd}/${root}`\n const resolved = normalizePathSegments(joined.split('/'), '/')\n return resolved === '' ? '/' : `/${resolved}`\n}\n\nfunction resolveWindowsPath(cwd: string, root: string): string {\n const normalizedRoot = root.replaceAll('/', '\\\\')\n const normalizedCwd = cwd.replaceAll('/', '\\\\')\n const drive = /^[A-Za-z]:/.exec(normalizedCwd)?.[0] ?? ''\n const cwdUnc = splitUncPath(normalizedCwd)\n if (/^\\\\\\\\/.test(normalizedRoot)) return normalizeUncPath(normalizedRoot)\n if (/^[A-Za-z]:\\\\/.test(normalizedRoot)) {\n const prefix = normalizedRoot.slice(0, 2)\n const rest = normalizedRoot.slice(3).split('\\\\')\n return joinWindowsRoot(prefix, normalizePathSegments(rest, '\\\\'))\n }\n if (/^[A-Za-z]:/.test(normalizedRoot)) {\n throw new Error('LocalFolder root must not be a drive-relative Windows path')\n }\n if (normalizedRoot.startsWith('\\\\')) {\n const rest = normalizePathSegments(normalizedRoot.slice(1).split('\\\\'), '\\\\')\n return joinWindowsRoot(cwdUnc?.prefix ?? drive, rest)\n }\n if (cwdUnc !== undefined) {\n const resolved = normalizePathSegments([...cwdUnc.rest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(cwdUnc.prefix, resolved)\n }\n const base = /^[A-Za-z]:\\\\/.test(normalizedCwd) ? normalizedCwd : `${drive}\\\\`\n const prefix = /^[A-Za-z]:/.exec(base)?.[0] ?? drive\n const baseRest = base.slice(prefix.length).replace(/^\\\\/, '').split('\\\\')\n const resolved = normalizePathSegments([...baseRest, ...normalizedRoot.split('\\\\')], '\\\\')\n return joinWindowsRoot(prefix, resolved)\n}\n\nfunction normalizeUncPath(path: string): string {\n const unc = splitUncPath(path)\n if (unc === undefined) return path\n return joinWindowsRoot(unc.prefix, normalizePathSegments(unc.rest, '\\\\'))\n}\n\nfunction splitUncPath(\n path: string,\n): { readonly prefix: string; readonly rest: string[] } | undefined {\n if (!path.startsWith('\\\\\\\\')) return undefined\n const parts = path.split('\\\\').filter((part) => part !== '')\n const [server, share, ...rest] = parts\n if (server === undefined || share === undefined) return undefined\n return { prefix: `\\\\\\\\${server}\\\\${share}`, rest }\n}\n\nfunction joinWindowsRoot(prefix: string, rest: string): string {\n return rest === '' ? `${prefix}\\\\` : `${prefix}\\\\${rest}`\n}\n\nfunction normalizePathSegments(segments: readonly string[], separator: '/' | '\\\\'): string {\n const out: string[] = []\n for (const segment of segments) {\n if (segment === '' || segment === '.') continue\n if (segment === '..') {\n out.pop()\n continue\n }\n out.push(segment)\n }\n return out.join(separator)\n}\n\nfunction relativePathFromRoot(root: string, path: string, nodeDeps: LocalNodeDeps): string {\n return nodeDeps.relative(root, path).split(nodeDeps.sep).join('/')\n}\n\nfunction throwIfScanAborted(options: SyncScanOptions): void {\n if (options.signal?.aborted === true) {\n throw options.signal.reason ?? new DOMException('Aborted', 'AbortError')\n }\n}\n"],"mappings":"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"}
@@ -328,10 +328,11 @@ function createB2Sha1Reader(config) {
328
328
  if (maxBytes < expectedBytes) throw new Error(`sha1 B2 verification skipped because contentLength ${expectedBytes} exceeds ${maxBytes} byte verification budget`);
329
329
  const serverSideEncryption = toSseCDownloadKey(config.options.encryptionProvider?.getSettingForDownload(path.selectedVersion));
330
330
  const fileName = validateB2SyncPathInAnyPrefix(readablePrefixes, path, "read");
331
- const verified = await require_b2_sha1_reader.hashReadableStreamSha1((await bucket.file(fileName).downloadById(path.selectedVersion.fileId, {
331
+ const result = await bucket.file(fileName).downloadById(path.selectedVersion.fileId, {
332
332
  ...serverSideEncryption !== void 0 ? { serverSideEncryption } : {},
333
333
  signal: deadlineSignal
334
- })).body, deadlineSignal, {
334
+ });
335
+ const verified = await require_b2_sha1_reader.hashReadableStreamSha1(result.body, deadlineSignal, {
335
336
  idleTimeoutMillis,
336
337
  maxBytes,
337
338
  expectedBytes
@@ -1 +1 @@
1
- {"version":3,"file":"synchronizer.cjs","names":[],"sources":["../../src/sync/synchronizer.ts"],"sourcesContent":["import type { Bucket } from '../bucket.ts'\nimport type { SseCDownloadKey } from '../raw/index.ts'\nimport { type ContentSource, FileSource } from '../streams/source.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport { fileId as fileIdOf } from '../types/ids.ts'\nimport { DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { sanitizeErrorReason } from '../util/error-reason.ts'\nimport type { SyncAction } from './actions/index.ts'\nimport {\n CopyAction,\n DeleteLocalAction,\n DeleteRemoteAction,\n DownloadAction,\n HideAction,\n SkipAction,\n UploadAction,\n} from './actions/index.ts'\nimport {\n hashReadableStreamSha1,\n normalizeSha1VerificationMaxBytes,\n withSha1VerificationDeadline,\n} from './b2-sha1-reader.ts'\nimport { localFilesystemErrorReason } from './filesystem-errors.ts'\nimport {\n deleteLocalFileInsideRoot,\n validateScannedLocalFile,\n writeLocalStreamInsideRoot,\n} from './local-file-io.ts'\nimport { isLocalFilesystemRoot } from './local-filesystem-root.ts'\nimport { readLocalSha1File } from './local-sha1.ts'\nimport { type SyncPair, zipFolders } from './pairing.ts'\nimport { safeRelativePathSegments } from './path-safety.ts'\nimport {\n assertSupportedCompareMode,\n type B2Sha1Reader,\n type ComparePreparationResult,\n preparePairsForCompare,\n readyComparePair,\n} from './policies/compare.ts'\nimport type { ActionFactory } from './policies/index.ts'\nimport { generateActions } from './policies/index.ts'\nimport { asRawB2KeyPrefix, b2KeyToRelativePathUnderPrefix } from './prefix.ts'\nimport {\n DEFAULT_SHA1_VERIFICATION_TIMEOUT_MILLIS,\n normalizeSha1TimeoutMillis,\n} from './sha1-options.ts'\nimport type {\n B2SyncPath,\n LocalSyncPath,\n SyncDirection,\n SyncEvent,\n SyncFolder,\n SyncOptions,\n SyncPath,\n SyncScanOptions,\n SyncSkipEvent,\n} from './types.ts'\n\nconst MAX_BUFFERED_SCAN_EVENTS = 100\nconst MAX_AGGREGATE_FAILED_PATHS = 100\nconst DEFAULT_DOWNLOAD_IDLE_TIMEOUT_MILLIS = 60_000\n\ninterface ScanEventBuffer {\n readonly events: SyncEvent[]\n fatalFilesystemErrorMessage?: string\n sourceInventoryIncompleteMessage?: string\n dropped: number\n}\n\n/**\n * Test hooks for bounded planning behavior.\n *\n * @internal\n */\nexport const synchronizerTestHooks: {\n afterNonSha1PlanBatch?: (batchSize: number) => void\n} = {}\n\n/** Base configuration for a sync operation. */\nexport interface SynchronizerConfig {\n /** The folder to read files from. */\n readonly source: SyncFolder\n /** The folder to write files to. */\n readonly dest: SyncFolder\n /** Options controlling comparison, deletion policy, and concurrency. */\n readonly options: SyncOptions\n}\n\n/** A sync folder constrained to the local filesystem. */\nexport interface LocalSyncFolder extends SyncFolder {\n /** Discriminant identifying a local folder. */\n readonly type: 'local'\n /** Absolute filesystem path to the local root directory. */\n readonly root: string\n}\n\n/** A sync folder constrained to a B2 bucket prefix. */\nexport interface B2SyncFolder extends SyncFolder {\n /** Discriminant identifying a B2 folder. */\n readonly type: 'b2'\n /** Raw B2 key prefix represented by this folder when known. */\n readonly rawPrefix?: string\n}\n\n/** Configuration for a local-to-B2 sync (upload direction). */\nexport interface SynchronizerUpConfig extends SynchronizerConfig {\n /** Local source folder. */\n readonly source: LocalSyncFolder\n /** B2 destination folder. */\n readonly dest: B2SyncFolder\n /** The target B2 bucket. */\n readonly bucket: Bucket\n /**\n * Raw B2 key prefix for uploaded files in the bucket.\n * Backslashes are preserved as raw B2 key characters.\n */\n readonly prefix: string\n}\n\n/** Configuration for a B2-to-local sync (download direction). */\nexport interface SynchronizerDownConfig extends SynchronizerConfig {\n /** B2 source folder. */\n readonly source: B2SyncFolder\n /** Local destination folder. */\n readonly dest: LocalSyncFolder\n /** The source B2 bucket. */\n readonly bucket: Bucket\n}\n\n/** Configuration for a B2-to-B2 sync (copy direction). */\nexport interface SynchronizerB2Config extends SynchronizerConfig {\n /** B2 source folder. */\n readonly source: B2SyncFolder\n /** B2 destination folder. */\n readonly dest: B2SyncFolder\n /** The B2 bucket used for copy, hide, and delete operations. */\n readonly bucket: Bucket\n /**\n * Key prefix for destination-side copy, hide, and delete mutations.\n *\n * When omitted, the synchronizer uses `dest.rawPrefix` when the destination\n * folder exposes one. Set this explicitly for custom B2 folders that do not\n * expose their raw prefix.\n */\n readonly prefix?: string\n}\n\n/** Concrete sync configurations supported by {@link synchronize}. */\nexport type SupportedSynchronizerConfig =\n | SynchronizerUpConfig\n | SynchronizerDownConfig\n | SynchronizerB2Config\n\ninterface PreparedActionPlan {\n readonly event: SyncEvent\n readonly actions: readonly SyncAction[]\n}\n\ninterface LocalRootIdentity {\n readonly deviceId: number | bigint\n readonly inode: number | bigint\n}\n\ninterface LocalRootContext {\n readonly root: string\n readonly realPath: string\n readonly identity?: LocalRootIdentity\n}\n\ninterface LocalRootContexts {\n readonly source?: LocalRootContext\n readonly dest?: LocalRootContext\n}\n\n/**\n * Infers the sync direction from the source and destination folder types.\n * @param source - The folder to read files from.\n * @param dest - The folder to write files to.\n *\n * @returns The resolved sync direction based on folder types.\n *\n * @throws When the source and destination folder types form an unsupported combination.\n */\nfunction resolveDirection(source: SyncFolder, dest: SyncFolder): SyncDirection {\n if (source.type === 'local' && dest.type === 'b2') return 'local-to-b2'\n if (source.type === 'b2' && dest.type === 'local') return 'b2-to-local'\n if (source.type === 'b2' && dest.type === 'b2') return 'b2-to-b2'\n throw new Error(`Unsupported sync direction: ${source.type} to ${dest.type}`)\n}\n\n/**\n * Runs a full sync operation: scans both folders, pairs files, generates actions,\n * and executes them with bounded concurrency. Yields {@link SyncEvent} entries for\n * each comparison and action result.\n *\n * @param config - The synchronizer configuration (source, dest, options, and optional bucket).\n *\n * @returns An async generator that yields comparison and action events.\n */\nexport function synchronize(config: SupportedSynchronizerConfig): AsyncGenerator<SyncEvent>\n/**\n * Legacy overload for broad custom synchronizer configurations.\n *\n * @param config - The synchronizer configuration.\n *\n * @returns An async generator that yields comparison and action events.\n *\n * @deprecated Use {@link SupportedSynchronizerConfig}. This legacy overload\n * accepts broad custom configurations for source compatibility, but the\n * supported union is the public contract for built-in sync directions.\n */\nexport function synchronize(config: SynchronizerConfig): AsyncGenerator<SyncEvent>\nexport async function* synchronize(config: SynchronizerConfig): AsyncGenerator<SyncEvent> {\n const { source, dest, options } = config\n assertSupportedCompareMode(options.compareMode)\n const direction = resolveDirection(source, dest)\n const dryRun = options.dryRun ?? false\n const concurrency = normalizeSyncConcurrency(options.concurrency)\n const keepDays = options.keepDays ?? 0\n const compareThreshold = options.compareThreshold ?? 0\n const nowMillis = Date.now()\n const localRootContexts = await resolveLocalRootContexts(config)\n const queuedEvents: SyncEvent[] = []\n const failedPaths: string[] = []\n const failedPathSet = new Set<string>()\n let errorCount = 0\n let failedPathOmittedCount = 0\n let scanHadError = false\n const runningActions = new Set<Promise<void>>()\n const scanEvents: ScanEventBuffer = { events: [], dropped: 0 }\n const scanOptions: SyncScanOptions = {\n ...(options.include !== undefined ? { include: options.include } : {}),\n ...(options.exclude !== undefined ? { exclude: options.exclude } : {}),\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.maxScanEntries !== undefined ? { maxScanEntries: options.maxScanEntries } : {}),\n ...(direction === 'b2-to-local' ? { requireLocalSafePaths: true } : {}),\n onError: (event) => {\n scanHadError = true\n recordSyncError(event)\n queueEvent(event)\n },\n }\n\n const factory = createActionFactory(config, localRootContexts)\n const readB2Sha1 = dryRun ? undefined : createB2Sha1Reader(config)\n const actionAbortController = new AbortController()\n const removeAbortForwarder = forwardAbortSignal(options.signal, actionAbortController)\n let completed = false\n\n async function* finishAfterAbort(): AsyncGenerator<SyncEvent> {\n await drainActions()\n yield* emitQueuedEvents()\n }\n\n try {\n let pairs: SyncPair[]\n try {\n pairs = await collectPairs()\n } catch (err) {\n await drainActions()\n if (options.signal?.aborted) {\n yield* finishAfterAbort()\n return\n }\n if (!scanHadError) {\n yield* emitQueuedEvents()\n throw err\n }\n\n yield* emitQueuedEvents()\n yield aggregateErrorEvent()\n completed = true\n return\n }\n\n yield* emitQueuedEvents()\n const filesystemError = scanFilesystemError(scanEvents)\n if (filesystemError !== undefined) throw filesystemError\n\n if (options.signal?.aborted) {\n yield* finishAfterAbort()\n return\n }\n\n if (scanHadError) {\n if (errorCount > 0) yield aggregateErrorEvent()\n completed = true\n return\n }\n\n if (options.compareMode === 'sha1') {\n const compareBatchSize = concurrency\n for (let index = 0; index < pairs.length; index += compareBatchSize) {\n if (yield* emitSha1Batch(pairs.slice(index, index + compareBatchSize))) return\n }\n } else {\n for (let index = 0; index < pairs.length; index += concurrency) {\n const items = pairs\n .slice(index, index + concurrency)\n .map((pair) => planPreparedPair(pair, readyComparePair(pair)))\n synchronizerTestHooks.afterNonSha1PlanBatch?.(items.length)\n if (yield* emitPreparedItems(items)) return\n }\n }\n\n await drainActions()\n yield* emitQueuedEvents()\n\n if (errorCount > 0) {\n yield aggregateErrorEvent()\n }\n completed = true\n } finally {\n if (!completed) {\n abortActionController(\n actionAbortController,\n new DOMException('Sync iterator closed', 'AbortError'),\n )\n }\n removeAbortForwarder()\n await drainActions()\n }\n\n async function collectPairs(): Promise<SyncPair[]> {\n const pairs: SyncPair[] = []\n for await (const pair of zipFolders(source, dest, scanOptions, {\n onSourceSkip(event) {\n bufferScanEvent(scanEvents, event, direction, 'source')\n },\n onDestSkip(event) {\n bufferScanEvent(scanEvents, event, direction, 'dest')\n },\n })) {\n if (options.signal?.aborted) return pairs\n validateB2SourcePairPrefix(pair, config)\n pairs.push(pair)\n }\n return pairs\n }\n\n async function* emitSha1Batch(batch: readonly SyncPair[]): AsyncGenerator<SyncEvent, boolean> {\n if (batch.length === 0) return false\n\n // Keep SHA-1 hashing / B2 verification and transfer actions under one effective\n // concurrency ceiling. This intentionally creates a batch barrier instead of\n // overlapping compare reads with prior transfers.\n await drainActions()\n yield* emitQueuedEvents()\n\n const preparedBatch = await processPreparedBatch(batch)\n if (yield* emitPreparedItems(preparedBatch.items)) return true\n if (preparedBatch.aborted || options.signal?.aborted) {\n yield* finishAfterAbort()\n return true\n }\n return false\n }\n\n async function* emitPreparedItems(\n items: readonly PreparedActionPlan[],\n ): AsyncGenerator<SyncEvent, boolean> {\n for (const item of items) {\n yield* emitQueuedEvents()\n yield item.event\n yield* emitQueuedEvents()\n /* v8 ignore next -- abort between compare yield and scheduling is timing-dependent */\n if (options.signal?.aborted) {\n yield* finishAfterAbort()\n return true\n }\n for (const action of item.actions) {\n await scheduleAction(action)\n yield* emitQueuedEvents()\n }\n }\n return false\n }\n\n async function processPreparedBatch(\n batch: readonly SyncPair[],\n ): Promise<{ readonly items: readonly PreparedActionPlan[]; readonly aborted: boolean }> {\n if (batch.length === 0) return { items: [], aborted: false }\n const preparedPairs = await preparePairsForCompare(batch, 'sha1', {\n concurrency,\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.sha1ReadTimeoutMillis !== undefined\n ? { sha1ReadTimeoutMillis: options.sha1ReadTimeoutMillis }\n : {}),\n readLocalSha1: readLocalSha1File,\n ...(readB2Sha1 !== undefined ? { readB2Sha1 } : {}),\n })\n\n const items: PreparedActionPlan[] = []\n for (const { originalPair, prepared } of preparedPairs) {\n if (prepared.aborted || options.signal?.aborted) return { items, aborted: true }\n items.push(planPreparedPair(originalPair, prepared))\n }\n return { items, aborted: false }\n }\n\n function planPreparedPair(\n pair: SyncPair,\n prepared: ComparePreparationResult,\n ): PreparedActionPlan {\n const event: SyncEvent = {\n type: 'compare',\n path: (pair[0] ?? pair[1])?.relativePath ?? '',\n size: 0,\n bytesHashed: prepared.bytesHashed,\n ...(prepared.bytesVerified > 0 ? { bytesVerified: prepared.bytesVerified } : {}),\n }\n\n let preparedErrorEventCount = 0\n for (const preparedEvent of prepared.events) {\n queueEvent(preparedEvent)\n if (preparedEvent.type === 'error') {\n preparedErrorEventCount++\n recordFailurePath(preparedEvent.path)\n }\n }\n errorCount += prepared.errors.length\n for (let index = preparedErrorEventCount; index < prepared.errors.length; index++) {\n recordFailurePath(event.path)\n }\n if (prepared.skipActionGeneration) return { event, actions: [] }\n if (\n (scanHadError || scanHadFilesystemError(scanEvents)) &&\n prepared.pair[0] === null &&\n prepared.pair[1] !== null\n ) {\n return {\n event,\n actions: [\n new SkipAction(prepared.pair[1].relativePath, 'not removed because scan errors occurred'),\n ],\n }\n }\n if (\n sourceInventoryIncomplete(scanEvents) &&\n prepared.pair[0] === null &&\n prepared.pair[1] !== null\n ) {\n return {\n event,\n actions: [\n new SkipAction(\n prepared.pair[1].relativePath,\n scanEvents.sourceInventoryIncompleteMessage ??\n 'not removed because the source scan skipped unsafe B2 names',\n ),\n ],\n }\n }\n\n const actions = [\n ...generateActions(\n prepared.pair,\n direction,\n options.compareMode,\n options.keepMode,\n keepDays,\n nowMillis,\n factory,\n compareThreshold,\n ),\n ]\n\n return { event, actions }\n }\n\n async function scheduleAction(action: SyncAction): Promise<void> {\n const task = executeAction(action).finally(() => {\n runningActions.delete(task)\n })\n runningActions.add(task)\n if (runningActions.size >= concurrency) await Promise.race(runningActions)\n }\n\n async function executeAction(action: SyncAction): Promise<void> {\n try {\n if (actionAbortController.signal.aborted) return\n const event = await action.execute(dryRun, actionAbortController.signal)\n queueEvent(event)\n } catch (err) {\n const event: SyncEvent = {\n type: 'error',\n path: action.relativePath,\n size: 0,\n message: sanitizeErrorReason(err),\n }\n recordSyncError(event)\n queueEvent(event)\n }\n }\n\n function recordSyncError(event: SyncEvent): void {\n errorCount += 1\n recordFailurePath(event.path)\n }\n\n function recordFailurePath(path: string): void {\n if (path === '') return\n if (failedPathSet.has(path)) return\n failedPathSet.add(path)\n if (failedPaths.length < MAX_AGGREGATE_FAILED_PATHS) {\n failedPaths.push(path)\n } else {\n failedPathOmittedCount++\n }\n }\n\n function aggregateErrorEvent(): SyncEvent {\n return {\n type: 'error',\n path: '',\n size: 0,\n message: `${errorCount} sync error(s) occurred`,\n failureCount: errorCount,\n failedPaths: [...failedPaths],\n ...(failedPathOmittedCount > 0 ? { failedPathOmittedCount } : {}),\n }\n }\n\n function queueEvent(event: SyncEvent): void {\n queuedEvents.push(event)\n }\n\n async function* emitQueuedEvents(): AsyncGenerator<SyncEvent> {\n yield* drainScanEvents(scanEvents)\n for (const event of queuedEvents.splice(0)) yield event\n }\n\n async function drainActions(): Promise<void> {\n while (runningActions.size > 0) {\n await Promise.race(runningActions)\n }\n }\n}\n\n/**\n * Normalizes user-provided sync concurrency before it controls compare batches and transfers.\n *\n * @param value - Optional concurrency value from sync options.\n *\n * @returns A positive integer concurrency value.\n *\n * @throws When the configured concurrency is not a positive integer.\n */\nfunction normalizeSyncConcurrency(value: number | undefined): number {\n const candidate = value ?? DEFAULT_TRANSFER_CONCURRENCY\n if (!Number.isInteger(candidate) || candidate < 1) {\n throw new RangeError('Sync concurrency must be a positive integer')\n }\n return candidate\n}\n\nfunction normalizeDownloadIdleTimeoutMillis(value: number | undefined): number {\n if (value === undefined) return DEFAULT_DOWNLOAD_IDLE_TIMEOUT_MILLIS\n if (value === Number.POSITIVE_INFINITY) return value\n if (!Number.isFinite(value) || value < 1) {\n throw new RangeError('downloadIdleTimeoutMillis must be a positive finite number or Infinity')\n }\n return Math.floor(value)\n}\n\nfunction assertValidB2ContentLength(contentLength: number): number {\n if (!Number.isSafeInteger(contentLength) || contentLength < 0) {\n throw new Error('B2 contentLength must be a non-negative safe integer')\n }\n return contentLength\n}\n\nfunction createB2Sha1Reader(config: SynchronizerConfig): B2Sha1Reader | undefined {\n const upConfig = config as Partial<SynchronizerUpConfig>\n const downConfig = config as Partial<SynchronizerDownConfig>\n const bucket = upConfig.bucket ?? downConfig.bucket\n if (bucket === undefined) return undefined\n const readablePrefixes = b2ReadableRawPrefixes(config)\n const idleTimeoutMillis = normalizeSha1TimeoutMillis(config.options.sha1ReadTimeoutMillis)\n const verificationTimeoutMillis = normalizeSha1TimeoutMillis(\n config.options.sha1VerificationTimeoutMillis,\n DEFAULT_SHA1_VERIFICATION_TIMEOUT_MILLIS,\n )\n\n return async (path, signal) => {\n const expectedBytes = assertValidB2ContentLength(path.selectedVersion.contentLength)\n const maxBytes = normalizeSha1VerificationMaxBytes(\n expectedBytes,\n config.options.sha1VerificationMaxBytes,\n )\n return withSha1VerificationDeadline(\n signal,\n verificationTimeoutMillis,\n async (deadlineSignal) => {\n deadlineSignal.throwIfAborted()\n if (maxBytes < expectedBytes) {\n throw new Error(\n `sha1 B2 verification skipped because contentLength ${expectedBytes} exceeds ${maxBytes} byte verification budget`,\n )\n }\n const serverSideEncryption = toSseCDownloadKey(\n config.options.encryptionProvider?.getSettingForDownload(path.selectedVersion),\n )\n const fileName = validateB2SyncPathInAnyPrefix(readablePrefixes, path, 'read')\n const result = await bucket.file(fileName).downloadById(path.selectedVersion.fileId, {\n ...(serverSideEncryption !== undefined ? { serverSideEncryption } : {}),\n signal: deadlineSignal,\n })\n const verified = await hashReadableStreamSha1(result.body, deadlineSignal, {\n idleTimeoutMillis,\n maxBytes,\n expectedBytes,\n })\n return { contentSha1: verified.contentSha1, bytesRead: verified.bytesRead }\n },\n )\n }\n}\n\nfunction forwardAbortSignal(\n source: AbortSignal | undefined,\n controller: AbortController,\n): () => void {\n if (source === undefined) return () => undefined\n if (source.aborted) {\n abortActionController(controller, source.reason)\n return () => undefined\n }\n\n const abort = () => abortActionController(controller, source.reason)\n source.addEventListener('abort', abort, { once: true })\n return () => source.removeEventListener('abort', abort)\n}\n\nfunction abortActionController(controller: AbortController, reason: unknown): void {\n if (!controller.signal.aborted) {\n controller.abort(reason)\n }\n}\n\n/**\n * Narrowing assertion that a `Bucket` is present for an action that requires\n * it. Throws with a consistent, context-tagged message when the configured\n * direction did not supply one (e.g. `b2-to-local` direction asking for an\n * upload action).\n *\n * Uses TypeScript's `asserts` signature so call-site flow narrows\n * `bucket` from `Bucket | undefined` to `Bucket` after the check, without\n * requiring a separate `if (!bucket) throw ...` line per action factory.\n *\n * @param bucket - The (possibly missing) bucket reference.\n * @param context - Short verb describing the action being constructed\n * (e.g. `'upload'`, `'download'`). Surfaced in the error message.\n *\n * @throws `Error` when `bucket` is `undefined` or `null`.\n */\nfunction assertBucket(bucket: Bucket | undefined, context: string): asserts bucket is Bucket {\n if (!bucket) throw new Error(`Bucket required for ${context} actions`)\n}\n\n/**\n * Returns the root for a local sync folder required by an action.\n *\n * @param folder - The configured folder to validate.\n * @param role - Whether the local folder is the source or destination.\n * @param context - Short verb describing the action being constructed.\n *\n * @returns The local filesystem root.\n *\n * @throws `Error` when the folder is not local or has no root.\n */\nfunction requireLocalRoot(\n folder: SyncFolder | undefined,\n role: 'source' | 'destination',\n context: string,\n): string {\n const root = folder?.type === 'local' ? (folder as Partial<LocalSyncFolder>).root : undefined\n if (typeof root !== 'string' || root === '') {\n throw new Error(`Local ${role} root required for ${context} actions`)\n }\n return root\n}\n\nasync function resolveLocalRootContexts(config: SynchronizerConfig): Promise<LocalRootContexts> {\n const sourceIsLocalFilesystem = isLocalFilesystemFolder(config.source)\n const destIsLocalFilesystem = isLocalFilesystemFolder(config.dest)\n if (!sourceIsLocalFilesystem && !destIsLocalFilesystem) return {}\n\n const sourceContext = config.dest.type === 'b2' ? 'upload' : 'sync'\n const destContext = config.source.type === 'b2' ? 'download' : 'sync'\n return {\n ...(sourceIsLocalFilesystem\n ? {\n source: await resolveLocalRootContext(\n requireLocalRoot(config.source, 'source', sourceContext),\n ),\n }\n : {}),\n ...(destIsLocalFilesystem\n ? {\n dest: await resolveLocalRootContext(\n requireLocalRoot(config.dest, 'destination', destContext),\n ),\n }\n : {}),\n }\n}\n\nasync function resolveLocalRootContext(root: string): Promise<LocalRootContext> {\n const { realpath, stat } = await import('node:fs/promises')\n const { resolve } = await import('node:path')\n const safeRoot = resolve(root)\n const realPath = await realpath(safeRoot).catch((err: unknown) => {\n if (isNotFoundError(err)) return safeRoot\n throw err\n })\n const stats = await stat(realPath).catch((err: unknown) => {\n if (isNotFoundError(err)) return undefined\n throw err\n })\n if (stats !== undefined && !stats.isDirectory()) {\n throw new Error('Local sync root is not a directory')\n }\n return {\n root: safeRoot,\n realPath,\n ...(stats === undefined\n ? {}\n : { identity: { deviceId: stats.dev, inode: stats.ino } satisfies LocalRootIdentity }),\n }\n}\n\nfunction isLocalFilesystemFolder(folder: SyncFolder | undefined): folder is LocalSyncFolder {\n return folder?.type === 'local' && isLocalFilesystemRoot(folder)\n}\n/**\n * Narrows a setting to SSE-C; non-SSE-C source settings need no key on read.\n *\n * @param setting - Provider-supplied encryption setting, or undefined.\n *\n * @returns The SSE-C setting when one is provided; otherwise undefined.\n */\nfunction toSseCEncryptionSetting(\n setting: EncryptionSetting | undefined,\n): Extract<EncryptionSetting, { readonly mode: 'SSE-C' }> | undefined {\n if (setting?.mode !== 'SSE-C') return undefined\n return setting\n}\n\n/**\n * Returns a download key from SSE-C settings; non-SSE-C downloads need no key.\n *\n * @param setting - Provider-supplied encryption setting, or undefined.\n *\n * @returns A download key for SSE-C files; otherwise undefined.\n */\nfunction toSseCDownloadKey(setting: EncryptionSetting | undefined): SseCDownloadKey | undefined {\n return toSseCEncryptionSetting(setting)\n}\n\n/**\n * Creates a configured sync engine wired to the bucket and paths in the given config.\n *\n * For sync operations that may need to remove destination-only files (the\n * `keepMode: 'delete'` policy), the factory reads the destination\n * bucket's cached `fileLockConfiguration` once so `removeOrphan` can\n * dispatch to either `hide` (locked buckets) or `deleteFileVersion`\n * (vanilla buckets) without a per-file branch. The cache is whatever\n * `client.listBuckets()` or `client.createBucket()` returned — callers\n * who flipped lock state mid-sync (rare) should refresh before\n * synchronize().\n *\n * @param config - Synchronizer configuration containing source, destination, and options.\n * @param localRootContexts - Resolved filesystem roots captured before action creation.\n *\n * @returns An action factory bound to the provided configuration.\n */\nfunction createActionFactory(\n config: SynchronizerConfig,\n localRootContexts: LocalRootContexts,\n): ActionFactory {\n const upConfig = config as Partial<SynchronizerUpConfig>\n const downConfig = config as Partial<SynchronizerDownConfig>\n const uploadPrefix = asRawB2KeyPrefix(upConfig.prefix ?? b2FolderRawPrefix(config.dest) ?? '')\n const sourceB2Prefix = b2FolderRawPrefix(config.source)\n\n const destBucket = upConfig.bucket ?? downConfig.bucket\n // Defensive optional chain on `info`: synchronizer tests use Bucket\n // mocks that don't populate this field. Real `Bucket` instances always\n // do (constructor parameter).\n const bucketIsLocked = destBucket?.info?.fileLockConfiguration?.value?.isFileLockEnabled ?? false\n\n const factory: ActionFactory = {\n upload(source: LocalSyncPath, dest?: B2SyncPath): SyncAction {\n const bucket = upConfig.bucket\n assertBucket(bucket, 'upload')\n\n return new UploadAction(\n source.relativePath,\n source.absolutePath,\n source.size,\n async (absPath, relPath, signal) => {\n const rootContext = localRootContexts.source\n const root = rootContext?.root ?? (upConfig.source as Partial<LocalSyncFolder>).root ?? ''\n const fileName =\n dest !== undefined\n ? validateB2SyncPathInPrefix(uploadPrefix, dest)\n : `${uploadPrefix}${relPath}`\n if (rootContext !== undefined) {\n await assertLocalRootContextCurrent(rootContext, relPath, { allowSymlinkRoot: true })\n }\n const targetPath =\n rootContext === undefined\n ? await resolveContainedLocalPath(root, source.relativePath, absPath)\n : await resolveContainedLocalPath(rootContext.realPath, source.relativePath)\n throwIfAborted(signal)\n // FileSource avoids whole-file buffering and rejects path swaps on a\n // best-effort basis. On Windows, callers that need tamper-resistant\n // same-size rewrite detection should use compareMode: 'sha1' or an\n // independent digest because restored modification times can hide\n // local rewrites.\n const fileSource = await createValidatedUploadFileSource(source, targetPath)\n throwIfAborted(signal)\n const serverSideEncryption = config.options.encryptionProvider?.getSettingForUpload(\n fileName,\n fileSource.size,\n )\n await bucket.upload({\n fileName,\n source: fileSource,\n ...(serverSideEncryption !== undefined ? { serverSideEncryption } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n },\n )\n },\n\n download(source: B2SyncPath, scannedDest?: LocalSyncPath | null): SyncAction {\n const bucket = downConfig.bucket\n assertBucket(bucket, 'download')\n\n return new DownloadAction(source.relativePath, source.size, async (relPath, signal) => {\n const rootContext = localRootContexts.dest\n const root = rootContext?.root ?? (downConfig.dest as Partial<LocalSyncFolder>).root ?? ''\n safeRelativePathSegments(relPath)\n const b2FileName =\n sourceB2Prefix === undefined\n ? source.selectedVersion.fileName\n : validateB2SyncPathInPrefix(sourceB2Prefix, source, 'read')\n const idleTimeoutMillis = normalizeDownloadIdleTimeoutMillis(\n config.options.downloadIdleTimeoutMillis,\n )\n const expectedBytes = assertValidB2ContentLength(source.selectedVersion.contentLength)\n if (rootContext !== undefined) {\n await assertLocalRootContextCurrent(rootContext, relPath, { allowSymlinkRoot: false })\n }\n await ensureLocalSyncRootDirectory(root, relPath)\n const serverSideEncryption = toSseCDownloadKey(\n config.options.encryptionProvider?.getSettingForDownload(source.selectedVersion),\n )\n const result = await bucket.file(b2FileName).downloadById(source.selectedVersion.fileId, {\n ...(serverSideEncryption !== undefined ? { serverSideEncryption } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n try {\n await writeLocalStreamInsideRoot(root, relPath, result.body, {\n expectedBytes,\n ...(scannedDest !== undefined ? { expectedDestination: scannedDest } : {}),\n idleTimeoutMillis,\n ...(signal !== undefined ? { signal } : {}),\n })\n } catch (err) {\n await cancelReadableStreamBody(result.body, err)\n throw err\n }\n })\n },\n\n copy(source: B2SyncPath, destRelativePath: string): SyncAction {\n return copyToB2Key(source, `${uploadPrefix}${destRelativePath}`)\n },\n\n copyB2Path(source: B2SyncPath, dest: B2SyncPath): SyncAction {\n return copyToB2Key(source, validateB2SyncPathInPrefix(uploadPrefix, dest))\n },\n\n hide(path: string): SyncAction {\n const bucket = upConfig.bucket ?? downConfig.bucket\n assertBucket(bucket, 'hide')\n\n return new HideAction(path, async (_relPath, signal) => {\n await bucket.hideFile(\n `${uploadPrefix}${path}`,\n signal === undefined ? undefined : { signal },\n )\n })\n },\n\n hideB2Path(path: B2SyncPath): SyncAction {\n const bucket = upConfig.bucket ?? downConfig.bucket\n assertBucket(bucket, 'hide')\n const b2FileName = validateB2SyncPathInPrefix(uploadPrefix, path)\n\n return new HideAction(path.relativePath, async (_relPath, signal) => {\n await bucket.hideFile(b2FileName, signal === undefined ? undefined : { signal })\n })\n },\n\n deleteRemote(path: B2SyncPath): SyncAction {\n const bucket = upConfig.bucket ?? downConfig.bucket\n assertBucket(bucket, 'delete')\n\n // Bug fix: `DeleteRemoteAction` invokes the closure with\n // `relativePath` (the scanner-stripped name relative to the sync\n // prefix), but B2 stores files under the FULL name including the\n // prefix. Use the FileVersion's authoritative `fileName` — the\n // actual B2 key — rather than reconstructing it via prefix\n // concat. Without this, syncs with a non-empty destination\n // prefix (e.g. `'site/'`) failed orphan deletion with\n // `file_not_present`.\n const b2FileName = validateB2SyncPathInPrefix(uploadPrefix, path)\n return new DeleteRemoteAction(\n path.relativePath,\n path.selectedVersion.fileId as string,\n async (fileId, _fileName, signal) => {\n await bucket.deleteFileVersion(\n b2FileName,\n fileIdOf(fileId),\n signal === undefined ? undefined : { signal },\n )\n },\n )\n },\n\n deleteLocal(path: LocalSyncPath): SyncAction {\n const rootContext = localRootContexts.dest\n const root = rootContext?.root ?? localSyncRoot(downConfig.dest)\n return new DeleteLocalAction(\n path.relativePath,\n path.absolutePath,\n async (absPath, signal) => {\n signal?.throwIfAborted()\n if (absPath !== path.absolutePath) {\n throw new Error(`Refusing to delete outside sync root: ${path.relativePath}`)\n }\n signal?.throwIfAborted()\n try {\n if (rootContext !== undefined) {\n await assertLocalRootContextCurrent(rootContext, path.relativePath, {\n allowSymlinkRoot: false,\n })\n }\n await deleteLocalFileInsideRoot(root, path)\n } catch (err) {\n if (isLocalDeleteSafetyError(err)) throw err\n throw new Error(`failed to delete local file: ${localFilesystemErrorReason(err)}`)\n }\n },\n )\n },\n\n removeOrphan(dest: B2SyncPath): SyncAction {\n // Locked buckets: `b2_delete_file_version` is blocked or stacks\n // hide markers under retention; a hide is the safe choice.\n // Vanilla buckets: plain delete is the right move — hide markers\n // would just litter the version history.\n return bucketIsLocked\n ? (factory.hideB2Path?.(dest) ?? factory.hide(dest.relativePath))\n : factory.deleteRemote(dest)\n },\n }\n\n function copyToB2Key(source: B2SyncPath, targetPath: string): SyncAction {\n const bucket = upConfig.bucket\n assertBucket(bucket, 'copy')\n\n return new CopyAction(source.relativePath, source.size, async (_relPath, signal) => {\n if (sourceB2Prefix !== undefined) validateB2SyncPathInPrefix(sourceB2Prefix, source, 'read')\n const destinationServerSideEncryption =\n config.options.encryptionProvider?.getSettingForUpload(targetPath, source.size)\n const sourceServerSideEncryption = toSseCEncryptionSetting(\n config.options.encryptionProvider?.getSettingForDownload(source.selectedVersion),\n )\n await bucket.copyFile({\n sourceFileId: source.selectedVersion.fileId,\n fileName: targetPath,\n ...(destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption }\n : {}),\n ...(sourceServerSideEncryption !== undefined ? { sourceServerSideEncryption } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n })\n }\n\n return factory\n}\n\nasync function createValidatedUploadFileSource(\n source: LocalSyncPath,\n absolutePath: string,\n): Promise<ContentSource> {\n try {\n const fileSource = await FileSource.fromPath(absolutePath)\n await validateScannedLocalFile({ ...source, absolutePath })\n return fileSource\n } catch (err) {\n throw normalizeLocalUploadSourceError(err)\n }\n}\n\nfunction normalizeLocalUploadSourceError(err: unknown): Error {\n if (err instanceof Error && err.message.startsWith('local file changed before upload')) {\n return err\n }\n const message = err instanceof Error ? err.message : String(err)\n if (message.includes('not a regular file')) {\n return new Error('local file changed before upload: not a regular file')\n }\n if (message.includes('changed') || message.includes('modified')) {\n return new Error('local file changed before upload')\n }\n return new Error(`local file changed before upload: ${sanitizeErrorReason(err)}`)\n}\n\nfunction localSyncRoot(folder: LocalSyncFolder | undefined): string {\n return folder?.type === 'local' ? folder.root : ''\n}\n\nfunction b2FolderRawPrefix(folder: SyncFolder | undefined): string | undefined {\n if (folder?.type !== 'b2') return undefined\n const rawPrefix = (folder as B2SyncFolder).rawPrefix\n return typeof rawPrefix === 'string' ? rawPrefix : undefined\n}\n\nfunction validateB2SourcePairPrefix(pair: SyncPair, config: SynchronizerConfig): void {\n const [source] = pair\n if (config.source.type !== 'b2' || source === null || !isB2SyncPath(source)) return\n const sourcePrefix = b2FolderRawPrefix(config.source)\n if (sourcePrefix === undefined) return\n validateB2SyncPathInPrefix(sourcePrefix, source, 'read')\n}\n\nfunction b2ReadableRawPrefixes(config: SynchronizerConfig): readonly string[] {\n const prefixes: string[] = []\n const sourcePrefix = b2FolderRawPrefix(config.source)\n if (config.source.type === 'b2') prefixes.push(sourcePrefix ?? '')\n\n const upConfig = config as Partial<SynchronizerUpConfig>\n if (config.dest.type === 'b2') {\n prefixes.push(upConfig.prefix ?? b2FolderRawPrefix(config.dest) ?? '')\n }\n\n return [...new Set(prefixes.map((prefix) => asRawB2KeyPrefix(prefix)))]\n}\n\nfunction validateB2SyncPathInAnyPrefix(\n prefixes: readonly string[],\n path: B2SyncPath,\n operation: string,\n): string {\n let firstError = new Error(`Refusing to ${operation} B2 key: ${path.relativePath}`)\n for (const prefix of prefixes) {\n try {\n return validateB2SyncPathInPrefix(prefix, path, operation)\n } catch (err) {\n if (err instanceof Error) firstError = err\n }\n }\n throw firstError\n}\n\nfunction validateB2SyncPathInPrefix(\n prefix: string,\n path: B2SyncPath,\n operation = 'mutate',\n): string {\n const fileName = path.selectedVersion.fileName\n if (!fileName.startsWith(prefix)) {\n throw new Error(\n `Refusing to ${operation} B2 key outside configured prefix: ${path.relativePath}`,\n )\n }\n\n const relativePath = b2KeyToRelativePathUnderPrefix(prefix, fileName)\n if (relativePath !== path.relativePath) {\n throw new Error(\n `Refusing to ${operation} mismatched B2 key for sync path: ${path.relativePath}`,\n )\n }\n\n return fileName\n}\n\nfunction isB2SyncPath(path: SyncPath): path is B2SyncPath {\n return 'selectedVersion' in path\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted === true) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError')\n }\n}\n\nasync function cancelReadableStreamBody(\n body: ReadableStream<Uint8Array>,\n reason: unknown,\n): Promise<void> {\n if (body.locked) return\n try {\n await body.cancel(reason)\n } catch {\n // Best-effort response cleanup must not mask the setup or write failure.\n }\n}\n\nasync function assertLocalRootContextCurrent(\n context: LocalRootContext,\n relativePath: string,\n options: { readonly allowSymlinkRoot: boolean },\n): Promise<void> {\n if (context.identity === undefined) return\n\n const { lstat, realpath, stat } = await import('node:fs/promises')\n let currentRealPath: string\n try {\n const rootLinkStats = await lstat(context.root)\n if (!options.allowSymlinkRoot && rootLinkStats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${relativePath}`)\n }\n currentRealPath = await realpath(context.root)\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Refusing to access sync root')) throw err\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n\n if (currentRealPath !== context.realPath) {\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n\n let currentStats: Awaited<ReturnType<typeof stat>>\n try {\n currentStats = await stat(context.realPath)\n } catch {\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n\n if (\n !currentStats.isDirectory() ||\n currentStats.dev !== context.identity.deviceId ||\n currentStats.ino !== context.identity.inode\n ) {\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n}\n\nasync function ensureLocalSyncRootDirectory(root: string, relativePath: string): Promise<void> {\n if (root === '') {\n throw new Error('Local sync root required for filesystem mutation')\n }\n\n const { lstat, mkdir } = await import('node:fs/promises')\n const { resolve } = await import('node:path')\n const safeRoot = resolve(root)\n\n try {\n const stats = await lstat(safeRoot)\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${relativePath}`)\n }\n if (!stats.isDirectory()) {\n throw new Error(`Local sync root is not a directory: ${relativePath}`)\n }\n return\n } catch (error) {\n if (!isNotFoundError(error)) throw error\n }\n\n await mkdir(safeRoot, { recursive: true })\n await assertLocalRootHasNoSymlink(safeRoot, relativePath)\n}\n\nfunction bufferScanEvent(\n buffer: ScanEventBuffer,\n event: SyncEvent,\n direction: SyncDirection,\n scanSide: 'source' | 'dest',\n): void {\n if (event.type === 'skip' && event.reason === 'filesystem-error') {\n buffer.fatalFilesystemErrorMessage ??= event.message\n }\n if (sourceSkipMakesInventoryIncomplete(event, direction, scanSide)) {\n buffer.sourceInventoryIncompleteMessage ??= sourceInventoryIncompleteMessage(direction)\n }\n if (buffer.events.length < MAX_BUFFERED_SCAN_EVENTS) {\n buffer.events.push(event)\n } else {\n buffer.dropped++\n }\n}\n\nfunction* drainScanEvents(buffer: ScanEventBuffer): Generator<SyncEvent> {\n while (buffer.events.length > 0) {\n const event = buffer.events.shift()\n if (event) yield event\n }\n\n if (buffer.dropped > 0) {\n yield {\n type: 'skip',\n path: '',\n size: 0,\n reason: 'scan-skip-overflow',\n message: `${buffer.dropped} scanner skip event(s) were omitted after ${MAX_BUFFERED_SCAN_EVENTS} buffered diagnostics`,\n }\n buffer.dropped = 0\n }\n}\n\nfunction scanHadFilesystemError(scanEvents: ScanEventBuffer): boolean {\n return scanEvents.fatalFilesystemErrorMessage !== undefined\n}\n\nfunction sourceInventoryIncomplete(scanEvents: ScanEventBuffer): boolean {\n return scanEvents.sourceInventoryIncompleteMessage !== undefined\n}\n\nfunction scanFilesystemError(scanEvents: ScanEventBuffer): Error | undefined {\n return scanEvents.fatalFilesystemErrorMessage === undefined\n ? undefined\n : new Error(scanEvents.fatalFilesystemErrorMessage)\n}\n\nfunction sourceSkipMakesInventoryIncomplete(\n event: SyncEvent,\n direction: SyncDirection,\n scanSide: 'source' | 'dest',\n): event is SyncSkipEvent {\n if (scanSide !== 'source' || event.type !== 'skip') return false\n\n if (direction === 'local-to-b2') {\n return (\n event.reason === 'unsafe-name' ||\n event.reason === 'stale-download-partial' ||\n event.reason === 'path-too-long-for-regexp'\n )\n }\n\n if (direction === 'b2-to-local' || direction === 'b2-to-b2') {\n return (\n event.reason === 'unsafe-name' ||\n event.reason === 'local-unsafe-name' ||\n event.reason === 'relative-path-collision' ||\n event.reason === 'local-path-collision' ||\n event.reason === 'path-too-long-for-regexp'\n )\n }\n\n return false\n}\n\nfunction sourceInventoryIncompleteMessage(direction: SyncDirection): string {\n return direction === 'local-to-b2'\n ? 'not removed because the source scan skipped local paths'\n : 'not removed because the source scan skipped unsafe B2 names'\n}\n\nfunction isLocalDeleteSafetyError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n return (\n err.message === 'Local sync root required for filesystem mutation' ||\n err.message.startsWith('Local sync root changed before filesystem action: ') ||\n err.message.startsWith('Refusing to ') ||\n err.message.startsWith('Local sync root is not a directory: ') ||\n err.message.startsWith('unsafe local delete path: ')\n )\n}\n\nasync function resolveContainedLocalPath(\n root: string,\n relativePath: string,\n absolutePath?: string,\n): Promise<string> {\n if (root === '') {\n throw new Error('Local sync root required for filesystem mutation')\n }\n\n const { isAbsolute, relative, resolve, sep } = await import('node:path')\n const safeRoot = resolve(root)\n const target =\n absolutePath === undefined ? resolve(safeRoot, relativePath) : resolve(absolutePath)\n const pathFromRoot = relative(safeRoot, target)\n const escapesRoot =\n pathFromRoot === '..' || pathFromRoot.startsWith(`..${sep}`) || isAbsolute(pathFromRoot)\n\n /* v8 ignore next -- defense-in-depth after prior no-follow and symlink checks. */\n if (escapesRoot) {\n throw new Error(`Refusing to access path outside sync root: ${relativePath}`)\n }\n\n await assertLocalRootHasNoSymlink(safeRoot, relativePath)\n await assertPathHasNoSymlinkComponents(safeRoot, pathFromRoot, relativePath)\n\n return target\n}\n\nasync function assertLocalRootHasNoSymlink(safeRoot: string, relativePath: string): Promise<void> {\n const { lstat } = await import('node:fs/promises')\n try {\n const stats = await lstat(safeRoot)\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${relativePath}`)\n }\n } catch (error) {\n if (isNotFoundError(error)) return\n throw error\n }\n}\n\nasync function assertPathHasNoSymlinkComponents(\n safeRoot: string,\n pathFromRoot: string,\n relativePath: string,\n): Promise<void> {\n if (pathFromRoot === '') return\n\n const { lstat } = await import('node:fs/promises')\n const { join, sep } = await import('node:path')\n let current = safeRoot\n\n for (const segment of pathFromRoot.split(sep)) {\n current = join(current, segment)\n let stats: Awaited<ReturnType<typeof lstat>>\n try {\n stats = await lstat(current)\n } catch (error) {\n if (isNotFoundError(error)) return\n throw error\n }\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to access path through symlink: ${relativePath}`)\n }\n }\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { readonly code?: unknown }).code === 'ENOENT'\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"synchronizer.cjs","names":[],"sources":["../../src/sync/synchronizer.ts"],"sourcesContent":["import type { Bucket } from '../bucket.ts'\nimport type { SseCDownloadKey } from '../raw/index.ts'\nimport { type ContentSource, FileSource } from '../streams/source.ts'\nimport type { EncryptionSetting } from '../types/encryption.ts'\nimport { fileId as fileIdOf } from '../types/ids.ts'\nimport { DEFAULT_TRANSFER_CONCURRENCY } from '../util/defaults.ts'\nimport { sanitizeErrorReason } from '../util/error-reason.ts'\nimport type { SyncAction } from './actions/index.ts'\nimport {\n CopyAction,\n DeleteLocalAction,\n DeleteRemoteAction,\n DownloadAction,\n HideAction,\n SkipAction,\n UploadAction,\n} from './actions/index.ts'\nimport {\n hashReadableStreamSha1,\n normalizeSha1VerificationMaxBytes,\n withSha1VerificationDeadline,\n} from './b2-sha1-reader.ts'\nimport { localFilesystemErrorReason } from './filesystem-errors.ts'\nimport {\n deleteLocalFileInsideRoot,\n validateScannedLocalFile,\n writeLocalStreamInsideRoot,\n} from './local-file-io.ts'\nimport { isLocalFilesystemRoot } from './local-filesystem-root.ts'\nimport { readLocalSha1File } from './local-sha1.ts'\nimport { type SyncPair, zipFolders } from './pairing.ts'\nimport { safeRelativePathSegments } from './path-safety.ts'\nimport {\n assertSupportedCompareMode,\n type B2Sha1Reader,\n type ComparePreparationResult,\n preparePairsForCompare,\n readyComparePair,\n} from './policies/compare.ts'\nimport type { ActionFactory } from './policies/index.ts'\nimport { generateActions } from './policies/index.ts'\nimport { asRawB2KeyPrefix, b2KeyToRelativePathUnderPrefix } from './prefix.ts'\nimport {\n DEFAULT_SHA1_VERIFICATION_TIMEOUT_MILLIS,\n normalizeSha1TimeoutMillis,\n} from './sha1-options.ts'\nimport type {\n B2SyncPath,\n LocalSyncPath,\n SyncDirection,\n SyncEvent,\n SyncFolder,\n SyncOptions,\n SyncPath,\n SyncScanOptions,\n SyncSkipEvent,\n} from './types.ts'\n\nconst MAX_BUFFERED_SCAN_EVENTS = 100\nconst MAX_AGGREGATE_FAILED_PATHS = 100\nconst DEFAULT_DOWNLOAD_IDLE_TIMEOUT_MILLIS = 60_000\n\ninterface ScanEventBuffer {\n readonly events: SyncEvent[]\n fatalFilesystemErrorMessage?: string\n sourceInventoryIncompleteMessage?: string\n dropped: number\n}\n\n/**\n * Test hooks for bounded planning behavior.\n *\n * @internal\n */\nexport const synchronizerTestHooks: {\n afterNonSha1PlanBatch?: (batchSize: number) => void\n} = {}\n\n/** Base configuration for a sync operation. */\nexport interface SynchronizerConfig {\n /** The folder to read files from. */\n readonly source: SyncFolder\n /** The folder to write files to. */\n readonly dest: SyncFolder\n /** Options controlling comparison, deletion policy, and concurrency. */\n readonly options: SyncOptions\n}\n\n/** A sync folder constrained to the local filesystem. */\nexport interface LocalSyncFolder extends SyncFolder {\n /** Discriminant identifying a local folder. */\n readonly type: 'local'\n /** Absolute filesystem path to the local root directory. */\n readonly root: string\n}\n\n/** A sync folder constrained to a B2 bucket prefix. */\nexport interface B2SyncFolder extends SyncFolder {\n /** Discriminant identifying a B2 folder. */\n readonly type: 'b2'\n /** Raw B2 key prefix represented by this folder when known. */\n readonly rawPrefix?: string\n}\n\n/** Configuration for a local-to-B2 sync (upload direction). */\nexport interface SynchronizerUpConfig extends SynchronizerConfig {\n /** Local source folder. */\n readonly source: LocalSyncFolder\n /** B2 destination folder. */\n readonly dest: B2SyncFolder\n /** The target B2 bucket. */\n readonly bucket: Bucket\n /**\n * Raw B2 key prefix for uploaded files in the bucket.\n * Backslashes are preserved as raw B2 key characters.\n */\n readonly prefix: string\n}\n\n/** Configuration for a B2-to-local sync (download direction). */\nexport interface SynchronizerDownConfig extends SynchronizerConfig {\n /** B2 source folder. */\n readonly source: B2SyncFolder\n /** Local destination folder. */\n readonly dest: LocalSyncFolder\n /** The source B2 bucket. */\n readonly bucket: Bucket\n}\n\n/** Configuration for a B2-to-B2 sync (copy direction). */\nexport interface SynchronizerB2Config extends SynchronizerConfig {\n /** B2 source folder. */\n readonly source: B2SyncFolder\n /** B2 destination folder. */\n readonly dest: B2SyncFolder\n /** The B2 bucket used for copy, hide, and delete operations. */\n readonly bucket: Bucket\n /**\n * Key prefix for destination-side copy, hide, and delete mutations.\n *\n * When omitted, the synchronizer uses `dest.rawPrefix` when the destination\n * folder exposes one. Set this explicitly for custom B2 folders that do not\n * expose their raw prefix.\n */\n readonly prefix?: string\n}\n\n/** Concrete sync configurations supported by {@link synchronize}. */\nexport type SupportedSynchronizerConfig =\n | SynchronizerUpConfig\n | SynchronizerDownConfig\n | SynchronizerB2Config\n\ninterface PreparedActionPlan {\n readonly event: SyncEvent\n readonly actions: readonly SyncAction[]\n}\n\ninterface LocalRootIdentity {\n readonly deviceId: number | bigint\n readonly inode: number | bigint\n}\n\ninterface LocalRootContext {\n readonly root: string\n readonly realPath: string\n readonly identity?: LocalRootIdentity\n}\n\ninterface LocalRootContexts {\n readonly source?: LocalRootContext\n readonly dest?: LocalRootContext\n}\n\n/**\n * Infers the sync direction from the source and destination folder types.\n * @param source - The folder to read files from.\n * @param dest - The folder to write files to.\n *\n * @returns The resolved sync direction based on folder types.\n *\n * @throws When the source and destination folder types form an unsupported combination.\n */\nfunction resolveDirection(source: SyncFolder, dest: SyncFolder): SyncDirection {\n if (source.type === 'local' && dest.type === 'b2') return 'local-to-b2'\n if (source.type === 'b2' && dest.type === 'local') return 'b2-to-local'\n if (source.type === 'b2' && dest.type === 'b2') return 'b2-to-b2'\n throw new Error(`Unsupported sync direction: ${source.type} to ${dest.type}`)\n}\n\n/**\n * Runs a full sync operation: scans both folders, pairs files, generates actions,\n * and executes them with bounded concurrency. Yields {@link SyncEvent} entries for\n * each comparison and action result.\n *\n * @param config - The synchronizer configuration (source, dest, options, and optional bucket).\n *\n * @returns An async generator that yields comparison and action events.\n */\nexport function synchronize(config: SupportedSynchronizerConfig): AsyncGenerator<SyncEvent>\n/**\n * Legacy overload for broad custom synchronizer configurations.\n *\n * @param config - The synchronizer configuration.\n *\n * @returns An async generator that yields comparison and action events.\n *\n * @deprecated Use {@link SupportedSynchronizerConfig}. This legacy overload\n * accepts broad custom configurations for source compatibility, but the\n * supported union is the public contract for built-in sync directions.\n */\nexport function synchronize(config: SynchronizerConfig): AsyncGenerator<SyncEvent>\nexport async function* synchronize(config: SynchronizerConfig): AsyncGenerator<SyncEvent> {\n const { source, dest, options } = config\n assertSupportedCompareMode(options.compareMode)\n const direction = resolveDirection(source, dest)\n const dryRun = options.dryRun ?? false\n const concurrency = normalizeSyncConcurrency(options.concurrency)\n const keepDays = options.keepDays ?? 0\n const compareThreshold = options.compareThreshold ?? 0\n const nowMillis = Date.now()\n const localRootContexts = await resolveLocalRootContexts(config)\n const queuedEvents: SyncEvent[] = []\n const failedPaths: string[] = []\n const failedPathSet = new Set<string>()\n let errorCount = 0\n let failedPathOmittedCount = 0\n let scanHadError = false\n const runningActions = new Set<Promise<void>>()\n const scanEvents: ScanEventBuffer = { events: [], dropped: 0 }\n const scanOptions: SyncScanOptions = {\n ...(options.include !== undefined ? { include: options.include } : {}),\n ...(options.exclude !== undefined ? { exclude: options.exclude } : {}),\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.maxScanEntries !== undefined ? { maxScanEntries: options.maxScanEntries } : {}),\n ...(direction === 'b2-to-local' ? { requireLocalSafePaths: true } : {}),\n onError: (event) => {\n scanHadError = true\n recordSyncError(event)\n queueEvent(event)\n },\n }\n\n const factory = createActionFactory(config, localRootContexts)\n const readB2Sha1 = dryRun ? undefined : createB2Sha1Reader(config)\n const actionAbortController = new AbortController()\n const removeAbortForwarder = forwardAbortSignal(options.signal, actionAbortController)\n let completed = false\n\n async function* finishAfterAbort(): AsyncGenerator<SyncEvent> {\n await drainActions()\n yield* emitQueuedEvents()\n }\n\n try {\n let pairs: SyncPair[]\n try {\n pairs = await collectPairs()\n } catch (err) {\n await drainActions()\n if (options.signal?.aborted) {\n yield* finishAfterAbort()\n return\n }\n if (!scanHadError) {\n yield* emitQueuedEvents()\n throw err\n }\n\n yield* emitQueuedEvents()\n yield aggregateErrorEvent()\n completed = true\n return\n }\n\n yield* emitQueuedEvents()\n const filesystemError = scanFilesystemError(scanEvents)\n if (filesystemError !== undefined) throw filesystemError\n\n if (options.signal?.aborted) {\n yield* finishAfterAbort()\n return\n }\n\n if (scanHadError) {\n if (errorCount > 0) yield aggregateErrorEvent()\n completed = true\n return\n }\n\n if (options.compareMode === 'sha1') {\n const compareBatchSize = concurrency\n for (let index = 0; index < pairs.length; index += compareBatchSize) {\n if (yield* emitSha1Batch(pairs.slice(index, index + compareBatchSize))) return\n }\n } else {\n for (let index = 0; index < pairs.length; index += concurrency) {\n const items = pairs\n .slice(index, index + concurrency)\n .map((pair) => planPreparedPair(pair, readyComparePair(pair)))\n synchronizerTestHooks.afterNonSha1PlanBatch?.(items.length)\n if (yield* emitPreparedItems(items)) return\n }\n }\n\n await drainActions()\n yield* emitQueuedEvents()\n\n if (errorCount > 0) {\n yield aggregateErrorEvent()\n }\n completed = true\n } finally {\n if (!completed) {\n abortActionController(\n actionAbortController,\n new DOMException('Sync iterator closed', 'AbortError'),\n )\n }\n removeAbortForwarder()\n await drainActions()\n }\n\n async function collectPairs(): Promise<SyncPair[]> {\n const pairs: SyncPair[] = []\n for await (const pair of zipFolders(source, dest, scanOptions, {\n onSourceSkip(event) {\n bufferScanEvent(scanEvents, event, direction, 'source')\n },\n onDestSkip(event) {\n bufferScanEvent(scanEvents, event, direction, 'dest')\n },\n })) {\n if (options.signal?.aborted) return pairs\n validateB2SourcePairPrefix(pair, config)\n pairs.push(pair)\n }\n return pairs\n }\n\n async function* emitSha1Batch(batch: readonly SyncPair[]): AsyncGenerator<SyncEvent, boolean> {\n if (batch.length === 0) return false\n\n // Keep SHA-1 hashing / B2 verification and transfer actions under one effective\n // concurrency ceiling. This intentionally creates a batch barrier instead of\n // overlapping compare reads with prior transfers.\n await drainActions()\n yield* emitQueuedEvents()\n\n const preparedBatch = await processPreparedBatch(batch)\n if (yield* emitPreparedItems(preparedBatch.items)) return true\n if (preparedBatch.aborted || options.signal?.aborted) {\n yield* finishAfterAbort()\n return true\n }\n return false\n }\n\n async function* emitPreparedItems(\n items: readonly PreparedActionPlan[],\n ): AsyncGenerator<SyncEvent, boolean> {\n for (const item of items) {\n yield* emitQueuedEvents()\n yield item.event\n yield* emitQueuedEvents()\n /* v8 ignore next -- abort between compare yield and scheduling is timing-dependent */\n if (options.signal?.aborted) {\n yield* finishAfterAbort()\n return true\n }\n for (const action of item.actions) {\n await scheduleAction(action)\n yield* emitQueuedEvents()\n }\n }\n return false\n }\n\n async function processPreparedBatch(\n batch: readonly SyncPair[],\n ): Promise<{ readonly items: readonly PreparedActionPlan[]; readonly aborted: boolean }> {\n if (batch.length === 0) return { items: [], aborted: false }\n const preparedPairs = await preparePairsForCompare(batch, 'sha1', {\n concurrency,\n ...(options.signal !== undefined ? { signal: options.signal } : {}),\n ...(options.sha1ReadTimeoutMillis !== undefined\n ? { sha1ReadTimeoutMillis: options.sha1ReadTimeoutMillis }\n : {}),\n readLocalSha1: readLocalSha1File,\n ...(readB2Sha1 !== undefined ? { readB2Sha1 } : {}),\n })\n\n const items: PreparedActionPlan[] = []\n for (const { originalPair, prepared } of preparedPairs) {\n if (prepared.aborted || options.signal?.aborted) return { items, aborted: true }\n items.push(planPreparedPair(originalPair, prepared))\n }\n return { items, aborted: false }\n }\n\n function planPreparedPair(\n pair: SyncPair,\n prepared: ComparePreparationResult,\n ): PreparedActionPlan {\n const event: SyncEvent = {\n type: 'compare',\n path: (pair[0] ?? pair[1])?.relativePath ?? '',\n size: 0,\n bytesHashed: prepared.bytesHashed,\n ...(prepared.bytesVerified > 0 ? { bytesVerified: prepared.bytesVerified } : {}),\n }\n\n let preparedErrorEventCount = 0\n for (const preparedEvent of prepared.events) {\n queueEvent(preparedEvent)\n if (preparedEvent.type === 'error') {\n preparedErrorEventCount++\n recordFailurePath(preparedEvent.path)\n }\n }\n errorCount += prepared.errors.length\n for (let index = preparedErrorEventCount; index < prepared.errors.length; index++) {\n recordFailurePath(event.path)\n }\n if (prepared.skipActionGeneration) return { event, actions: [] }\n if (\n (scanHadError || scanHadFilesystemError(scanEvents)) &&\n prepared.pair[0] === null &&\n prepared.pair[1] !== null\n ) {\n return {\n event,\n actions: [\n new SkipAction(prepared.pair[1].relativePath, 'not removed because scan errors occurred'),\n ],\n }\n }\n if (\n sourceInventoryIncomplete(scanEvents) &&\n prepared.pair[0] === null &&\n prepared.pair[1] !== null\n ) {\n return {\n event,\n actions: [\n new SkipAction(\n prepared.pair[1].relativePath,\n scanEvents.sourceInventoryIncompleteMessage ??\n 'not removed because the source scan skipped unsafe B2 names',\n ),\n ],\n }\n }\n\n const actions = [\n ...generateActions(\n prepared.pair,\n direction,\n options.compareMode,\n options.keepMode,\n keepDays,\n nowMillis,\n factory,\n compareThreshold,\n ),\n ]\n\n return { event, actions }\n }\n\n async function scheduleAction(action: SyncAction): Promise<void> {\n const task = executeAction(action).finally(() => {\n runningActions.delete(task)\n })\n runningActions.add(task)\n if (runningActions.size >= concurrency) await Promise.race(runningActions)\n }\n\n async function executeAction(action: SyncAction): Promise<void> {\n try {\n if (actionAbortController.signal.aborted) return\n const event = await action.execute(dryRun, actionAbortController.signal)\n queueEvent(event)\n } catch (err) {\n const event: SyncEvent = {\n type: 'error',\n path: action.relativePath,\n size: 0,\n message: sanitizeErrorReason(err),\n }\n recordSyncError(event)\n queueEvent(event)\n }\n }\n\n function recordSyncError(event: SyncEvent): void {\n errorCount += 1\n recordFailurePath(event.path)\n }\n\n function recordFailurePath(path: string): void {\n if (path === '') return\n if (failedPathSet.has(path)) return\n failedPathSet.add(path)\n if (failedPaths.length < MAX_AGGREGATE_FAILED_PATHS) {\n failedPaths.push(path)\n } else {\n failedPathOmittedCount++\n }\n }\n\n function aggregateErrorEvent(): SyncEvent {\n return {\n type: 'error',\n path: '',\n size: 0,\n message: `${errorCount} sync error(s) occurred`,\n failureCount: errorCount,\n failedPaths: [...failedPaths],\n ...(failedPathOmittedCount > 0 ? { failedPathOmittedCount } : {}),\n }\n }\n\n function queueEvent(event: SyncEvent): void {\n queuedEvents.push(event)\n }\n\n async function* emitQueuedEvents(): AsyncGenerator<SyncEvent> {\n yield* drainScanEvents(scanEvents)\n for (const event of queuedEvents.splice(0)) yield event\n }\n\n async function drainActions(): Promise<void> {\n while (runningActions.size > 0) {\n await Promise.race(runningActions)\n }\n }\n}\n\n/**\n * Normalizes user-provided sync concurrency before it controls compare batches and transfers.\n *\n * @param value - Optional concurrency value from sync options.\n *\n * @returns A positive integer concurrency value.\n *\n * @throws When the configured concurrency is not a positive integer.\n */\nfunction normalizeSyncConcurrency(value: number | undefined): number {\n const candidate = value ?? DEFAULT_TRANSFER_CONCURRENCY\n if (!Number.isInteger(candidate) || candidate < 1) {\n throw new RangeError('Sync concurrency must be a positive integer')\n }\n return candidate\n}\n\nfunction normalizeDownloadIdleTimeoutMillis(value: number | undefined): number {\n if (value === undefined) return DEFAULT_DOWNLOAD_IDLE_TIMEOUT_MILLIS\n if (value === Number.POSITIVE_INFINITY) return value\n if (!Number.isFinite(value) || value < 1) {\n throw new RangeError('downloadIdleTimeoutMillis must be a positive finite number or Infinity')\n }\n return Math.floor(value)\n}\n\nfunction assertValidB2ContentLength(contentLength: number): number {\n if (!Number.isSafeInteger(contentLength) || contentLength < 0) {\n throw new Error('B2 contentLength must be a non-negative safe integer')\n }\n return contentLength\n}\n\nfunction createB2Sha1Reader(config: SynchronizerConfig): B2Sha1Reader | undefined {\n const upConfig = config as Partial<SynchronizerUpConfig>\n const downConfig = config as Partial<SynchronizerDownConfig>\n const bucket = upConfig.bucket ?? downConfig.bucket\n if (bucket === undefined) return undefined\n const readablePrefixes = b2ReadableRawPrefixes(config)\n const idleTimeoutMillis = normalizeSha1TimeoutMillis(config.options.sha1ReadTimeoutMillis)\n const verificationTimeoutMillis = normalizeSha1TimeoutMillis(\n config.options.sha1VerificationTimeoutMillis,\n DEFAULT_SHA1_VERIFICATION_TIMEOUT_MILLIS,\n )\n\n return async (path, signal) => {\n const expectedBytes = assertValidB2ContentLength(path.selectedVersion.contentLength)\n const maxBytes = normalizeSha1VerificationMaxBytes(\n expectedBytes,\n config.options.sha1VerificationMaxBytes,\n )\n return withSha1VerificationDeadline(\n signal,\n verificationTimeoutMillis,\n async (deadlineSignal) => {\n deadlineSignal.throwIfAborted()\n if (maxBytes < expectedBytes) {\n throw new Error(\n `sha1 B2 verification skipped because contentLength ${expectedBytes} exceeds ${maxBytes} byte verification budget`,\n )\n }\n const serverSideEncryption = toSseCDownloadKey(\n config.options.encryptionProvider?.getSettingForDownload(path.selectedVersion),\n )\n const fileName = validateB2SyncPathInAnyPrefix(readablePrefixes, path, 'read')\n const result = await bucket.file(fileName).downloadById(path.selectedVersion.fileId, {\n ...(serverSideEncryption !== undefined ? { serverSideEncryption } : {}),\n signal: deadlineSignal,\n })\n const verified = await hashReadableStreamSha1(result.body, deadlineSignal, {\n idleTimeoutMillis,\n maxBytes,\n expectedBytes,\n })\n return { contentSha1: verified.contentSha1, bytesRead: verified.bytesRead }\n },\n )\n }\n}\n\nfunction forwardAbortSignal(\n source: AbortSignal | undefined,\n controller: AbortController,\n): () => void {\n if (source === undefined) return () => undefined\n if (source.aborted) {\n abortActionController(controller, source.reason)\n return () => undefined\n }\n\n const abort = () => abortActionController(controller, source.reason)\n source.addEventListener('abort', abort, { once: true })\n return () => source.removeEventListener('abort', abort)\n}\n\nfunction abortActionController(controller: AbortController, reason: unknown): void {\n if (!controller.signal.aborted) {\n controller.abort(reason)\n }\n}\n\n/**\n * Narrowing assertion that a `Bucket` is present for an action that requires\n * it. Throws with a consistent, context-tagged message when the configured\n * direction did not supply one (e.g. `b2-to-local` direction asking for an\n * upload action).\n *\n * Uses TypeScript's `asserts` signature so call-site flow narrows\n * `bucket` from `Bucket | undefined` to `Bucket` after the check, without\n * requiring a separate `if (!bucket) throw ...` line per action factory.\n *\n * @param bucket - The (possibly missing) bucket reference.\n * @param context - Short verb describing the action being constructed\n * (e.g. `'upload'`, `'download'`). Surfaced in the error message.\n *\n * @throws `Error` when `bucket` is `undefined` or `null`.\n */\nfunction assertBucket(bucket: Bucket | undefined, context: string): asserts bucket is Bucket {\n if (!bucket) throw new Error(`Bucket required for ${context} actions`)\n}\n\n/**\n * Returns the root for a local sync folder required by an action.\n *\n * @param folder - The configured folder to validate.\n * @param role - Whether the local folder is the source or destination.\n * @param context - Short verb describing the action being constructed.\n *\n * @returns The local filesystem root.\n *\n * @throws `Error` when the folder is not local or has no root.\n */\nfunction requireLocalRoot(\n folder: SyncFolder | undefined,\n role: 'source' | 'destination',\n context: string,\n): string {\n const root = folder?.type === 'local' ? (folder as Partial<LocalSyncFolder>).root : undefined\n if (typeof root !== 'string' || root === '') {\n throw new Error(`Local ${role} root required for ${context} actions`)\n }\n return root\n}\n\nasync function resolveLocalRootContexts(config: SynchronizerConfig): Promise<LocalRootContexts> {\n const sourceIsLocalFilesystem = isLocalFilesystemFolder(config.source)\n const destIsLocalFilesystem = isLocalFilesystemFolder(config.dest)\n if (!sourceIsLocalFilesystem && !destIsLocalFilesystem) return {}\n\n const sourceContext = config.dest.type === 'b2' ? 'upload' : 'sync'\n const destContext = config.source.type === 'b2' ? 'download' : 'sync'\n return {\n ...(sourceIsLocalFilesystem\n ? {\n source: await resolveLocalRootContext(\n requireLocalRoot(config.source, 'source', sourceContext),\n ),\n }\n : {}),\n ...(destIsLocalFilesystem\n ? {\n dest: await resolveLocalRootContext(\n requireLocalRoot(config.dest, 'destination', destContext),\n ),\n }\n : {}),\n }\n}\n\nasync function resolveLocalRootContext(root: string): Promise<LocalRootContext> {\n const { realpath, stat } = await import('node:fs/promises')\n const { resolve } = await import('node:path')\n const safeRoot = resolve(root)\n const realPath = await realpath(safeRoot).catch((err: unknown) => {\n if (isNotFoundError(err)) return safeRoot\n throw err\n })\n const stats = await stat(realPath).catch((err: unknown) => {\n if (isNotFoundError(err)) return undefined\n throw err\n })\n if (stats !== undefined && !stats.isDirectory()) {\n throw new Error('Local sync root is not a directory')\n }\n return {\n root: safeRoot,\n realPath,\n ...(stats === undefined\n ? {}\n : { identity: { deviceId: stats.dev, inode: stats.ino } satisfies LocalRootIdentity }),\n }\n}\n\nfunction isLocalFilesystemFolder(folder: SyncFolder | undefined): folder is LocalSyncFolder {\n return folder?.type === 'local' && isLocalFilesystemRoot(folder)\n}\n/**\n * Narrows a setting to SSE-C; non-SSE-C source settings need no key on read.\n *\n * @param setting - Provider-supplied encryption setting, or undefined.\n *\n * @returns The SSE-C setting when one is provided; otherwise undefined.\n */\nfunction toSseCEncryptionSetting(\n setting: EncryptionSetting | undefined,\n): Extract<EncryptionSetting, { readonly mode: 'SSE-C' }> | undefined {\n if (setting?.mode !== 'SSE-C') return undefined\n return setting\n}\n\n/**\n * Returns a download key from SSE-C settings; non-SSE-C downloads need no key.\n *\n * @param setting - Provider-supplied encryption setting, or undefined.\n *\n * @returns A download key for SSE-C files; otherwise undefined.\n */\nfunction toSseCDownloadKey(setting: EncryptionSetting | undefined): SseCDownloadKey | undefined {\n return toSseCEncryptionSetting(setting)\n}\n\n/**\n * Creates a configured sync engine wired to the bucket and paths in the given config.\n *\n * For sync operations that may need to remove destination-only files (the\n * `keepMode: 'delete'` policy), the factory reads the destination\n * bucket's cached `fileLockConfiguration` once so `removeOrphan` can\n * dispatch to either `hide` (locked buckets) or `deleteFileVersion`\n * (vanilla buckets) without a per-file branch. The cache is whatever\n * `client.listBuckets()` or `client.createBucket()` returned — callers\n * who flipped lock state mid-sync (rare) should refresh before\n * synchronize().\n *\n * @param config - Synchronizer configuration containing source, destination, and options.\n * @param localRootContexts - Resolved filesystem roots captured before action creation.\n *\n * @returns An action factory bound to the provided configuration.\n */\nfunction createActionFactory(\n config: SynchronizerConfig,\n localRootContexts: LocalRootContexts,\n): ActionFactory {\n const upConfig = config as Partial<SynchronizerUpConfig>\n const downConfig = config as Partial<SynchronizerDownConfig>\n const uploadPrefix = asRawB2KeyPrefix(upConfig.prefix ?? b2FolderRawPrefix(config.dest) ?? '')\n const sourceB2Prefix = b2FolderRawPrefix(config.source)\n\n const destBucket = upConfig.bucket ?? downConfig.bucket\n // Defensive optional chain on `info`: synchronizer tests use Bucket\n // mocks that don't populate this field. Real `Bucket` instances always\n // do (constructor parameter).\n const bucketIsLocked = destBucket?.info?.fileLockConfiguration?.value?.isFileLockEnabled ?? false\n\n const factory: ActionFactory = {\n upload(source: LocalSyncPath, dest?: B2SyncPath): SyncAction {\n const bucket = upConfig.bucket\n assertBucket(bucket, 'upload')\n\n return new UploadAction(\n source.relativePath,\n source.absolutePath,\n source.size,\n async (absPath, relPath, signal) => {\n const rootContext = localRootContexts.source\n const root = rootContext?.root ?? (upConfig.source as Partial<LocalSyncFolder>).root ?? ''\n const fileName =\n dest !== undefined\n ? validateB2SyncPathInPrefix(uploadPrefix, dest)\n : `${uploadPrefix}${relPath}`\n if (rootContext !== undefined) {\n await assertLocalRootContextCurrent(rootContext, relPath, { allowSymlinkRoot: true })\n }\n const targetPath =\n rootContext === undefined\n ? await resolveContainedLocalPath(root, source.relativePath, absPath)\n : await resolveContainedLocalPath(rootContext.realPath, source.relativePath)\n throwIfAborted(signal)\n // FileSource avoids whole-file buffering and rejects path swaps on a\n // best-effort basis. On Windows, callers that need tamper-resistant\n // same-size rewrite detection should use compareMode: 'sha1' or an\n // independent digest because restored modification times can hide\n // local rewrites.\n const fileSource = await createValidatedUploadFileSource(source, targetPath)\n throwIfAborted(signal)\n const serverSideEncryption = config.options.encryptionProvider?.getSettingForUpload(\n fileName,\n fileSource.size,\n )\n await bucket.upload({\n fileName,\n source: fileSource,\n ...(serverSideEncryption !== undefined ? { serverSideEncryption } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n },\n )\n },\n\n download(source: B2SyncPath, scannedDest?: LocalSyncPath | null): SyncAction {\n const bucket = downConfig.bucket\n assertBucket(bucket, 'download')\n\n return new DownloadAction(source.relativePath, source.size, async (relPath, signal) => {\n const rootContext = localRootContexts.dest\n const root = rootContext?.root ?? (downConfig.dest as Partial<LocalSyncFolder>).root ?? ''\n safeRelativePathSegments(relPath)\n const b2FileName =\n sourceB2Prefix === undefined\n ? source.selectedVersion.fileName\n : validateB2SyncPathInPrefix(sourceB2Prefix, source, 'read')\n const idleTimeoutMillis = normalizeDownloadIdleTimeoutMillis(\n config.options.downloadIdleTimeoutMillis,\n )\n const expectedBytes = assertValidB2ContentLength(source.selectedVersion.contentLength)\n if (rootContext !== undefined) {\n await assertLocalRootContextCurrent(rootContext, relPath, { allowSymlinkRoot: false })\n }\n await ensureLocalSyncRootDirectory(root, relPath)\n const serverSideEncryption = toSseCDownloadKey(\n config.options.encryptionProvider?.getSettingForDownload(source.selectedVersion),\n )\n const result = await bucket.file(b2FileName).downloadById(source.selectedVersion.fileId, {\n ...(serverSideEncryption !== undefined ? { serverSideEncryption } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n try {\n await writeLocalStreamInsideRoot(root, relPath, result.body, {\n expectedBytes,\n ...(scannedDest !== undefined ? { expectedDestination: scannedDest } : {}),\n idleTimeoutMillis,\n ...(signal !== undefined ? { signal } : {}),\n })\n } catch (err) {\n await cancelReadableStreamBody(result.body, err)\n throw err\n }\n })\n },\n\n copy(source: B2SyncPath, destRelativePath: string): SyncAction {\n return copyToB2Key(source, `${uploadPrefix}${destRelativePath}`)\n },\n\n copyB2Path(source: B2SyncPath, dest: B2SyncPath): SyncAction {\n return copyToB2Key(source, validateB2SyncPathInPrefix(uploadPrefix, dest))\n },\n\n hide(path: string): SyncAction {\n const bucket = upConfig.bucket ?? downConfig.bucket\n assertBucket(bucket, 'hide')\n\n return new HideAction(path, async (_relPath, signal) => {\n await bucket.hideFile(\n `${uploadPrefix}${path}`,\n signal === undefined ? undefined : { signal },\n )\n })\n },\n\n hideB2Path(path: B2SyncPath): SyncAction {\n const bucket = upConfig.bucket ?? downConfig.bucket\n assertBucket(bucket, 'hide')\n const b2FileName = validateB2SyncPathInPrefix(uploadPrefix, path)\n\n return new HideAction(path.relativePath, async (_relPath, signal) => {\n await bucket.hideFile(b2FileName, signal === undefined ? undefined : { signal })\n })\n },\n\n deleteRemote(path: B2SyncPath): SyncAction {\n const bucket = upConfig.bucket ?? downConfig.bucket\n assertBucket(bucket, 'delete')\n\n // Bug fix: `DeleteRemoteAction` invokes the closure with\n // `relativePath` (the scanner-stripped name relative to the sync\n // prefix), but B2 stores files under the FULL name including the\n // prefix. Use the FileVersion's authoritative `fileName` — the\n // actual B2 key — rather than reconstructing it via prefix\n // concat. Without this, syncs with a non-empty destination\n // prefix (e.g. `'site/'`) failed orphan deletion with\n // `file_not_present`.\n const b2FileName = validateB2SyncPathInPrefix(uploadPrefix, path)\n return new DeleteRemoteAction(\n path.relativePath,\n path.selectedVersion.fileId as string,\n async (fileId, _fileName, signal) => {\n await bucket.deleteFileVersion(\n b2FileName,\n fileIdOf(fileId),\n signal === undefined ? undefined : { signal },\n )\n },\n )\n },\n\n deleteLocal(path: LocalSyncPath): SyncAction {\n const rootContext = localRootContexts.dest\n const root = rootContext?.root ?? localSyncRoot(downConfig.dest)\n return new DeleteLocalAction(\n path.relativePath,\n path.absolutePath,\n async (absPath, signal) => {\n signal?.throwIfAborted()\n if (absPath !== path.absolutePath) {\n throw new Error(`Refusing to delete outside sync root: ${path.relativePath}`)\n }\n signal?.throwIfAborted()\n try {\n if (rootContext !== undefined) {\n await assertLocalRootContextCurrent(rootContext, path.relativePath, {\n allowSymlinkRoot: false,\n })\n }\n await deleteLocalFileInsideRoot(root, path)\n } catch (err) {\n if (isLocalDeleteSafetyError(err)) throw err\n throw new Error(`failed to delete local file: ${localFilesystemErrorReason(err)}`)\n }\n },\n )\n },\n\n removeOrphan(dest: B2SyncPath): SyncAction {\n // Locked buckets: `b2_delete_file_version` is blocked or stacks\n // hide markers under retention; a hide is the safe choice.\n // Vanilla buckets: plain delete is the right move — hide markers\n // would just litter the version history.\n return bucketIsLocked\n ? (factory.hideB2Path?.(dest) ?? factory.hide(dest.relativePath))\n : factory.deleteRemote(dest)\n },\n }\n\n function copyToB2Key(source: B2SyncPath, targetPath: string): SyncAction {\n const bucket = upConfig.bucket\n assertBucket(bucket, 'copy')\n\n return new CopyAction(source.relativePath, source.size, async (_relPath, signal) => {\n if (sourceB2Prefix !== undefined) validateB2SyncPathInPrefix(sourceB2Prefix, source, 'read')\n const destinationServerSideEncryption =\n config.options.encryptionProvider?.getSettingForUpload(targetPath, source.size)\n const sourceServerSideEncryption = toSseCEncryptionSetting(\n config.options.encryptionProvider?.getSettingForDownload(source.selectedVersion),\n )\n await bucket.copyFile({\n sourceFileId: source.selectedVersion.fileId,\n fileName: targetPath,\n ...(destinationServerSideEncryption !== undefined\n ? { destinationServerSideEncryption }\n : {}),\n ...(sourceServerSideEncryption !== undefined ? { sourceServerSideEncryption } : {}),\n ...(signal !== undefined ? { signal } : {}),\n })\n })\n }\n\n return factory\n}\n\nasync function createValidatedUploadFileSource(\n source: LocalSyncPath,\n absolutePath: string,\n): Promise<ContentSource> {\n try {\n const fileSource = await FileSource.fromPath(absolutePath)\n await validateScannedLocalFile({ ...source, absolutePath })\n return fileSource\n } catch (err) {\n throw normalizeLocalUploadSourceError(err)\n }\n}\n\nfunction normalizeLocalUploadSourceError(err: unknown): Error {\n if (err instanceof Error && err.message.startsWith('local file changed before upload')) {\n return err\n }\n const message = err instanceof Error ? err.message : String(err)\n if (message.includes('not a regular file')) {\n return new Error('local file changed before upload: not a regular file')\n }\n if (message.includes('changed') || message.includes('modified')) {\n return new Error('local file changed before upload')\n }\n return new Error(`local file changed before upload: ${sanitizeErrorReason(err)}`)\n}\n\nfunction localSyncRoot(folder: LocalSyncFolder | undefined): string {\n return folder?.type === 'local' ? folder.root : ''\n}\n\nfunction b2FolderRawPrefix(folder: SyncFolder | undefined): string | undefined {\n if (folder?.type !== 'b2') return undefined\n const rawPrefix = (folder as B2SyncFolder).rawPrefix\n return typeof rawPrefix === 'string' ? rawPrefix : undefined\n}\n\nfunction validateB2SourcePairPrefix(pair: SyncPair, config: SynchronizerConfig): void {\n const [source] = pair\n if (config.source.type !== 'b2' || source === null || !isB2SyncPath(source)) return\n const sourcePrefix = b2FolderRawPrefix(config.source)\n if (sourcePrefix === undefined) return\n validateB2SyncPathInPrefix(sourcePrefix, source, 'read')\n}\n\nfunction b2ReadableRawPrefixes(config: SynchronizerConfig): readonly string[] {\n const prefixes: string[] = []\n const sourcePrefix = b2FolderRawPrefix(config.source)\n if (config.source.type === 'b2') prefixes.push(sourcePrefix ?? '')\n\n const upConfig = config as Partial<SynchronizerUpConfig>\n if (config.dest.type === 'b2') {\n prefixes.push(upConfig.prefix ?? b2FolderRawPrefix(config.dest) ?? '')\n }\n\n return [...new Set(prefixes.map((prefix) => asRawB2KeyPrefix(prefix)))]\n}\n\nfunction validateB2SyncPathInAnyPrefix(\n prefixes: readonly string[],\n path: B2SyncPath,\n operation: string,\n): string {\n let firstError = new Error(`Refusing to ${operation} B2 key: ${path.relativePath}`)\n for (const prefix of prefixes) {\n try {\n return validateB2SyncPathInPrefix(prefix, path, operation)\n } catch (err) {\n if (err instanceof Error) firstError = err\n }\n }\n throw firstError\n}\n\nfunction validateB2SyncPathInPrefix(\n prefix: string,\n path: B2SyncPath,\n operation = 'mutate',\n): string {\n const fileName = path.selectedVersion.fileName\n if (!fileName.startsWith(prefix)) {\n throw new Error(\n `Refusing to ${operation} B2 key outside configured prefix: ${path.relativePath}`,\n )\n }\n\n const relativePath = b2KeyToRelativePathUnderPrefix(prefix, fileName)\n if (relativePath !== path.relativePath) {\n throw new Error(\n `Refusing to ${operation} mismatched B2 key for sync path: ${path.relativePath}`,\n )\n }\n\n return fileName\n}\n\nfunction isB2SyncPath(path: SyncPath): path is B2SyncPath {\n return 'selectedVersion' in path\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted === true) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError')\n }\n}\n\nasync function cancelReadableStreamBody(\n body: ReadableStream<Uint8Array>,\n reason: unknown,\n): Promise<void> {\n if (body.locked) return\n try {\n await body.cancel(reason)\n } catch {\n // Best-effort response cleanup must not mask the setup or write failure.\n }\n}\n\nasync function assertLocalRootContextCurrent(\n context: LocalRootContext,\n relativePath: string,\n options: { readonly allowSymlinkRoot: boolean },\n): Promise<void> {\n if (context.identity === undefined) return\n\n const { lstat, realpath, stat } = await import('node:fs/promises')\n let currentRealPath: string\n try {\n const rootLinkStats = await lstat(context.root)\n if (!options.allowSymlinkRoot && rootLinkStats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${relativePath}`)\n }\n currentRealPath = await realpath(context.root)\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Refusing to access sync root')) throw err\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n\n if (currentRealPath !== context.realPath) {\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n\n let currentStats: Awaited<ReturnType<typeof stat>>\n try {\n currentStats = await stat(context.realPath)\n } catch {\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n\n if (\n !currentStats.isDirectory() ||\n currentStats.dev !== context.identity.deviceId ||\n currentStats.ino !== context.identity.inode\n ) {\n throw new Error(`Local sync root changed before filesystem action: ${relativePath}`)\n }\n}\n\nasync function ensureLocalSyncRootDirectory(root: string, relativePath: string): Promise<void> {\n if (root === '') {\n throw new Error('Local sync root required for filesystem mutation')\n }\n\n const { lstat, mkdir } = await import('node:fs/promises')\n const { resolve } = await import('node:path')\n const safeRoot = resolve(root)\n\n try {\n const stats = await lstat(safeRoot)\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${relativePath}`)\n }\n if (!stats.isDirectory()) {\n throw new Error(`Local sync root is not a directory: ${relativePath}`)\n }\n return\n } catch (error) {\n if (!isNotFoundError(error)) throw error\n }\n\n await mkdir(safeRoot, { recursive: true })\n await assertLocalRootHasNoSymlink(safeRoot, relativePath)\n}\n\nfunction bufferScanEvent(\n buffer: ScanEventBuffer,\n event: SyncEvent,\n direction: SyncDirection,\n scanSide: 'source' | 'dest',\n): void {\n if (event.type === 'skip' && event.reason === 'filesystem-error') {\n buffer.fatalFilesystemErrorMessage ??= event.message\n }\n if (sourceSkipMakesInventoryIncomplete(event, direction, scanSide)) {\n buffer.sourceInventoryIncompleteMessage ??= sourceInventoryIncompleteMessage(direction)\n }\n if (buffer.events.length < MAX_BUFFERED_SCAN_EVENTS) {\n buffer.events.push(event)\n } else {\n buffer.dropped++\n }\n}\n\nfunction* drainScanEvents(buffer: ScanEventBuffer): Generator<SyncEvent> {\n while (buffer.events.length > 0) {\n const event = buffer.events.shift()\n if (event) yield event\n }\n\n if (buffer.dropped > 0) {\n yield {\n type: 'skip',\n path: '',\n size: 0,\n reason: 'scan-skip-overflow',\n message: `${buffer.dropped} scanner skip event(s) were omitted after ${MAX_BUFFERED_SCAN_EVENTS} buffered diagnostics`,\n }\n buffer.dropped = 0\n }\n}\n\nfunction scanHadFilesystemError(scanEvents: ScanEventBuffer): boolean {\n return scanEvents.fatalFilesystemErrorMessage !== undefined\n}\n\nfunction sourceInventoryIncomplete(scanEvents: ScanEventBuffer): boolean {\n return scanEvents.sourceInventoryIncompleteMessage !== undefined\n}\n\nfunction scanFilesystemError(scanEvents: ScanEventBuffer): Error | undefined {\n return scanEvents.fatalFilesystemErrorMessage === undefined\n ? undefined\n : new Error(scanEvents.fatalFilesystemErrorMessage)\n}\n\nfunction sourceSkipMakesInventoryIncomplete(\n event: SyncEvent,\n direction: SyncDirection,\n scanSide: 'source' | 'dest',\n): event is SyncSkipEvent {\n if (scanSide !== 'source' || event.type !== 'skip') return false\n\n if (direction === 'local-to-b2') {\n return (\n event.reason === 'unsafe-name' ||\n event.reason === 'stale-download-partial' ||\n event.reason === 'path-too-long-for-regexp'\n )\n }\n\n if (direction === 'b2-to-local' || direction === 'b2-to-b2') {\n return (\n event.reason === 'unsafe-name' ||\n event.reason === 'local-unsafe-name' ||\n event.reason === 'relative-path-collision' ||\n event.reason === 'local-path-collision' ||\n event.reason === 'path-too-long-for-regexp'\n )\n }\n\n return false\n}\n\nfunction sourceInventoryIncompleteMessage(direction: SyncDirection): string {\n return direction === 'local-to-b2'\n ? 'not removed because the source scan skipped local paths'\n : 'not removed because the source scan skipped unsafe B2 names'\n}\n\nfunction isLocalDeleteSafetyError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n return (\n err.message === 'Local sync root required for filesystem mutation' ||\n err.message.startsWith('Local sync root changed before filesystem action: ') ||\n err.message.startsWith('Refusing to ') ||\n err.message.startsWith('Local sync root is not a directory: ') ||\n err.message.startsWith('unsafe local delete path: ')\n )\n}\n\nasync function resolveContainedLocalPath(\n root: string,\n relativePath: string,\n absolutePath?: string,\n): Promise<string> {\n if (root === '') {\n throw new Error('Local sync root required for filesystem mutation')\n }\n\n const { isAbsolute, relative, resolve, sep } = await import('node:path')\n const safeRoot = resolve(root)\n const target =\n absolutePath === undefined ? resolve(safeRoot, relativePath) : resolve(absolutePath)\n const pathFromRoot = relative(safeRoot, target)\n const escapesRoot =\n pathFromRoot === '..' || pathFromRoot.startsWith(`..${sep}`) || isAbsolute(pathFromRoot)\n\n /* v8 ignore next -- defense-in-depth after prior no-follow and symlink checks. */\n if (escapesRoot) {\n throw new Error(`Refusing to access path outside sync root: ${relativePath}`)\n }\n\n await assertLocalRootHasNoSymlink(safeRoot, relativePath)\n await assertPathHasNoSymlinkComponents(safeRoot, pathFromRoot, relativePath)\n\n return target\n}\n\nasync function assertLocalRootHasNoSymlink(safeRoot: string, relativePath: string): Promise<void> {\n const { lstat } = await import('node:fs/promises')\n try {\n const stats = await lstat(safeRoot)\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to access sync root through symlink: ${relativePath}`)\n }\n } catch (error) {\n if (isNotFoundError(error)) return\n throw error\n }\n}\n\nasync function assertPathHasNoSymlinkComponents(\n safeRoot: string,\n pathFromRoot: string,\n relativePath: string,\n): Promise<void> {\n if (pathFromRoot === '') return\n\n const { lstat } = await import('node:fs/promises')\n const { join, sep } = await import('node:path')\n let current = safeRoot\n\n for (const segment of pathFromRoot.split(sep)) {\n current = join(current, segment)\n let stats: Awaited<ReturnType<typeof lstat>>\n try {\n stats = await lstat(current)\n } catch (error) {\n if (isNotFoundError(error)) return\n throw error\n }\n if (stats.isSymbolicLink()) {\n throw new Error(`Refusing to access path through symlink: ${relativePath}`)\n }\n }\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n (error as { readonly code?: unknown }).code === 'ENOENT'\n )\n}\n"],"mappings":"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"}
@@ -328,10 +328,11 @@ function createB2Sha1Reader(config) {
328
328
  if (maxBytes < expectedBytes) throw new Error(`sha1 B2 verification skipped because contentLength ${expectedBytes} exceeds ${maxBytes} byte verification budget`);
329
329
  const serverSideEncryption = toSseCDownloadKey(config.options.encryptionProvider?.getSettingForDownload(path.selectedVersion));
330
330
  const fileName = validateB2SyncPathInAnyPrefix(readablePrefixes, path, "read");
331
- const verified = await hashReadableStreamSha1((await bucket.file(fileName).downloadById(path.selectedVersion.fileId, {
331
+ const result = await bucket.file(fileName).downloadById(path.selectedVersion.fileId, {
332
332
  ...serverSideEncryption !== void 0 ? { serverSideEncryption } : {},
333
333
  signal: deadlineSignal
334
- })).body, deadlineSignal, {
334
+ });
335
+ const verified = await hashReadableStreamSha1(result.body, deadlineSignal, {
335
336
  idleTimeoutMillis,
336
337
  maxBytes,
337
338
  expectedBytes