@backblaze-labs/b2-sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (701) hide show
  1. package/CHANGELOG.md +110 -5
  2. package/README.md +144 -33
  3. package/dist/_virtual/_b2-sdk-version-json.cjs +4 -0
  4. package/dist/_virtual/_b2-sdk-version-json.js +4 -0
  5. package/dist/auth/account-info.d.cts +32 -0
  6. package/dist/auth/account-info.d.cts.map +1 -1
  7. package/dist/auth/account-info.d.ts +32 -0
  8. package/dist/auth/account-info.d.ts.map +1 -1
  9. package/dist/auth/file.cjs +408 -207
  10. package/dist/auth/file.cjs.map +1 -1
  11. package/dist/auth/file.d.cts +77 -3
  12. package/dist/auth/file.d.cts.map +1 -1
  13. package/dist/auth/file.d.ts +77 -3
  14. package/dist/auth/file.d.ts.map +1 -1
  15. package/dist/auth/file.js +407 -207
  16. package/dist/auth/file.js.map +1 -1
  17. package/dist/auth/in-memory.cjs +212 -183
  18. package/dist/auth/in-memory.cjs.map +1 -1
  19. package/dist/auth/in-memory.d.cts +8 -0
  20. package/dist/auth/in-memory.d.cts.map +1 -1
  21. package/dist/auth/in-memory.d.ts +8 -0
  22. package/dist/auth/in-memory.d.ts.map +1 -1
  23. package/dist/auth/in-memory.js +211 -182
  24. package/dist/auth/in-memory.js.map +1 -1
  25. package/dist/auth/index.cjs +7 -9
  26. package/dist/auth/index.d.cts +3 -2
  27. package/dist/auth/index.d.cts.map +1 -1
  28. package/dist/auth/index.d.ts +3 -2
  29. package/dist/auth/index.d.ts.map +1 -1
  30. package/dist/auth/index.js +2 -8
  31. package/dist/auth/realms.cjs +80 -7
  32. package/dist/auth/realms.cjs.map +1 -1
  33. package/dist/auth/realms.d.cts +27 -4
  34. package/dist/auth/realms.d.cts.map +1 -1
  35. package/dist/auth/realms.d.ts +27 -4
  36. package/dist/auth/realms.d.ts.map +1 -1
  37. package/dist/auth/realms.js +80 -9
  38. package/dist/auth/realms.js.map +1 -1
  39. package/dist/auth/upload-url-pool.cjs +55 -51
  40. package/dist/auth/upload-url-pool.cjs.map +1 -1
  41. package/dist/auth/upload-url-pool.js +55 -51
  42. package/dist/auth/upload-url-pool.js.map +1 -1
  43. package/dist/bucket.cjs +785 -787
  44. package/dist/bucket.cjs.map +1 -1
  45. package/dist/bucket.d.cts +35 -47
  46. package/dist/bucket.d.cts.map +1 -1
  47. package/dist/bucket.d.ts +35 -47
  48. package/dist/bucket.d.ts.map +1 -1
  49. package/dist/bucket.js +780 -782
  50. package/dist/bucket.js.map +1 -1
  51. package/dist/client.cjs +246 -228
  52. package/dist/client.cjs.map +1 -1
  53. package/dist/client.d.cts +25 -17
  54. package/dist/client.d.cts.map +1 -1
  55. package/dist/client.d.ts +25 -17
  56. package/dist/client.d.ts.map +1 -1
  57. package/dist/client.js +243 -225
  58. package/dist/client.js.map +1 -1
  59. package/dist/copy/large.cjs +114 -71
  60. package/dist/copy/large.cjs.map +1 -1
  61. package/dist/copy/large.d.cts +6 -0
  62. package/dist/copy/large.d.cts.map +1 -1
  63. package/dist/copy/large.d.ts +6 -0
  64. package/dist/copy/large.d.ts.map +1 -1
  65. package/dist/copy/large.js +112 -69
  66. package/dist/copy/large.js.map +1 -1
  67. package/dist/download/checksum.cjs +89 -0
  68. package/dist/download/checksum.cjs.map +1 -0
  69. package/dist/download/checksum.d.cts +40 -0
  70. package/dist/download/checksum.d.cts.map +1 -0
  71. package/dist/download/checksum.d.ts +40 -0
  72. package/dist/download/checksum.d.ts.map +1 -0
  73. package/dist/download/checksum.js +87 -0
  74. package/dist/download/checksum.js.map +1 -0
  75. package/dist/download/parallel.cjs +214 -118
  76. package/dist/download/parallel.cjs.map +1 -1
  77. package/dist/download/parallel.d.cts +5 -1
  78. package/dist/download/parallel.d.cts.map +1 -1
  79. package/dist/download/parallel.d.ts +5 -1
  80. package/dist/download/parallel.d.ts.map +1 -1
  81. package/dist/download/parallel.js +212 -116
  82. package/dist/download/parallel.js.map +1 -1
  83. package/dist/download/single.cjs +188 -97
  84. package/dist/download/single.cjs.map +1 -1
  85. package/dist/download/single.d.cts +6 -1
  86. package/dist/download/single.d.cts.map +1 -1
  87. package/dist/download/single.d.ts +6 -1
  88. package/dist/download/single.d.ts.map +1 -1
  89. package/dist/download/single.js +186 -98
  90. package/dist/download/single.js.map +1 -1
  91. package/dist/errors/index.cjs +699 -243
  92. package/dist/errors/index.cjs.map +1 -1
  93. package/dist/errors/index.d.cts +285 -82
  94. package/dist/errors/index.d.cts.map +1 -1
  95. package/dist/errors/index.d.ts +285 -82
  96. package/dist/errors/index.d.ts.map +1 -1
  97. package/dist/errors/index.js +678 -261
  98. package/dist/errors/index.js.map +1 -1
  99. package/dist/http/retry.cjs +45 -28
  100. package/dist/http/retry.cjs.map +1 -1
  101. package/dist/http/retry.d.cts +15 -2
  102. package/dist/http/retry.d.cts.map +1 -1
  103. package/dist/http/retry.d.ts +15 -2
  104. package/dist/http/retry.d.ts.map +1 -1
  105. package/dist/http/retry.js +46 -31
  106. package/dist/http/retry.js.map +1 -1
  107. package/dist/http/transport.cjs +438 -140
  108. package/dist/http/transport.cjs.map +1 -1
  109. package/dist/http/transport.d.cts +21 -2
  110. package/dist/http/transport.d.cts.map +1 -1
  111. package/dist/http/transport.d.ts +21 -2
  112. package/dist/http/transport.d.ts.map +1 -1
  113. package/dist/http/transport.js +437 -140
  114. package/dist/http/transport.js.map +1 -1
  115. package/dist/http/url-guard.cjs +154 -89
  116. package/dist/http/url-guard.cjs.map +1 -1
  117. package/dist/http/url-guard.d.cts +25 -3
  118. package/dist/http/url-guard.d.cts.map +1 -1
  119. package/dist/http/url-guard.d.ts +25 -3
  120. package/dist/http/url-guard.d.ts.map +1 -1
  121. package/dist/http/url-guard.js +152 -90
  122. package/dist/http/url-guard.js.map +1 -1
  123. package/dist/http/user-agent.cjs +84 -44
  124. package/dist/http/user-agent.cjs.map +1 -1
  125. package/dist/http/user-agent.js +84 -46
  126. package/dist/http/user-agent.js.map +1 -1
  127. package/dist/index.cjs +101 -77
  128. package/dist/index.d.cts +27 -21
  129. package/dist/index.d.cts.map +1 -1
  130. package/dist/index.d.ts +27 -21
  131. package/dist/index.d.ts.map +1 -1
  132. package/dist/index.js +15 -71
  133. package/dist/internal/b2-naming.cjs +66 -0
  134. package/dist/internal/b2-naming.cjs.map +1 -0
  135. package/dist/internal/b2-naming.d.cts +43 -0
  136. package/dist/internal/b2-naming.d.cts.map +1 -0
  137. package/dist/internal/b2-naming.d.ts +43 -0
  138. package/dist/internal/b2-naming.d.ts.map +1 -0
  139. package/dist/internal/b2-naming.js +60 -0
  140. package/dist/internal/b2-naming.js.map +1 -0
  141. package/dist/internal/upload-retry-options.cjs +21 -0
  142. package/dist/internal/upload-retry-options.cjs.map +1 -0
  143. package/dist/internal/upload-retry-options.d.cts +12 -0
  144. package/dist/internal/upload-retry-options.d.cts.map +1 -0
  145. package/dist/internal/upload-retry-options.d.ts +12 -0
  146. package/dist/internal/upload-retry-options.d.ts.map +1 -0
  147. package/dist/internal/upload-retry-options.js +21 -0
  148. package/dist/internal/upload-retry-options.js.map +1 -0
  149. package/dist/internal/url-redaction.cjs +31 -0
  150. package/dist/internal/url-redaction.cjs.map +1 -0
  151. package/dist/internal/url-redaction.d.cts +17 -0
  152. package/dist/internal/url-redaction.d.cts.map +1 -0
  153. package/dist/internal/url-redaction.d.ts +17 -0
  154. package/dist/internal/url-redaction.d.ts.map +1 -0
  155. package/dist/internal/url-redaction.js +31 -0
  156. package/dist/internal/url-redaction.js.map +1 -0
  157. package/dist/notifications/index.cjs +4 -6
  158. package/dist/notifications/index.d.cts +1 -1
  159. package/dist/notifications/index.d.cts.map +1 -1
  160. package/dist/notifications/index.d.ts +1 -1
  161. package/dist/notifications/index.d.ts.map +1 -1
  162. package/dist/notifications/index.js +1 -6
  163. package/dist/notifications/webhook.cjs +195 -85
  164. package/dist/notifications/webhook.cjs.map +1 -1
  165. package/dist/notifications/webhook.d.cts.map +1 -1
  166. package/dist/notifications/webhook.d.ts.map +1 -1
  167. package/dist/notifications/webhook.js +195 -87
  168. package/dist/notifications/webhook.js.map +1 -1
  169. package/dist/object.cjs +253 -210
  170. package/dist/object.cjs.map +1 -1
  171. package/dist/object.d.cts +9 -55
  172. package/dist/object.d.cts.map +1 -1
  173. package/dist/object.d.ts +9 -55
  174. package/dist/object.d.ts.map +1 -1
  175. package/dist/object.js +249 -206
  176. package/dist/object.js.map +1 -1
  177. package/dist/raw/encoding.cjs +74 -35
  178. package/dist/raw/encoding.cjs.map +1 -1
  179. package/dist/raw/encoding.js +74 -38
  180. package/dist/raw/encoding.js.map +1 -1
  181. package/dist/raw/index.cjs +678 -587
  182. package/dist/raw/index.cjs.map +1 -1
  183. package/dist/raw/index.d.cts +121 -18
  184. package/dist/raw/index.d.cts.map +1 -1
  185. package/dist/raw/index.d.ts +121 -18
  186. package/dist/raw/index.d.ts.map +1 -1
  187. package/dist/raw/index.js +674 -589
  188. package/dist/raw/index.js.map +1 -1
  189. package/dist/s3/index.cjs +355 -18
  190. package/dist/s3/index.cjs.map +1 -1
  191. package/dist/s3/index.d.cts +233 -5
  192. package/dist/s3/index.d.cts.map +1 -1
  193. package/dist/s3/index.d.ts +233 -5
  194. package/dist/s3/index.d.ts.map +1 -1
  195. package/dist/s3/index.js +351 -22
  196. package/dist/s3/index.js.map +1 -1
  197. package/dist/s3/sigv4.cjs +149 -0
  198. package/dist/s3/sigv4.cjs.map +1 -0
  199. package/dist/s3/sigv4.d.cts +43 -0
  200. package/dist/s3/sigv4.d.cts.map +1 -0
  201. package/dist/s3/sigv4.d.ts +43 -0
  202. package/dist/s3/sigv4.d.ts.map +1 -0
  203. package/dist/s3/sigv4.js +148 -0
  204. package/dist/s3/sigv4.js.map +1 -0
  205. package/dist/s3/validation.cjs +69 -0
  206. package/dist/s3/validation.cjs.map +1 -0
  207. package/dist/s3/validation.d.cts +43 -0
  208. package/dist/s3/validation.d.cts.map +1 -0
  209. package/dist/s3/validation.d.ts +43 -0
  210. package/dist/s3/validation.d.ts.map +1 -0
  211. package/dist/s3/validation.js +66 -0
  212. package/dist/s3/validation.js.map +1 -0
  213. package/dist/simulator/capabilities.cjs +63 -48
  214. package/dist/simulator/capabilities.cjs.map +1 -1
  215. package/dist/simulator/capabilities.js +63 -49
  216. package/dist/simulator/capabilities.js.map +1 -1
  217. package/dist/simulator/index.cjs +2124 -1548
  218. package/dist/simulator/index.cjs.map +1 -1
  219. package/dist/simulator/index.d.cts +79 -11
  220. package/dist/simulator/index.d.cts.map +1 -1
  221. package/dist/simulator/index.d.ts +79 -11
  222. package/dist/simulator/index.d.ts.map +1 -1
  223. package/dist/simulator/index.js +2112 -1546
  224. package/dist/simulator/index.js.map +1 -1
  225. package/dist/simulator/validation.cjs +191 -153
  226. package/dist/simulator/validation.cjs.map +1 -1
  227. package/dist/simulator/validation.d.cts +3 -21
  228. package/dist/simulator/validation.d.cts.map +1 -1
  229. package/dist/simulator/validation.d.ts +3 -21
  230. package/dist/simulator/validation.d.ts.map +1 -1
  231. package/dist/simulator/validation.js +188 -162
  232. package/dist/simulator/validation.js.map +1 -1
  233. package/dist/streams/collect.cjs +74 -24
  234. package/dist/streams/collect.cjs.map +1 -1
  235. package/dist/streams/collect.d.cts +14 -1
  236. package/dist/streams/collect.d.cts.map +1 -1
  237. package/dist/streams/collect.d.ts +14 -1
  238. package/dist/streams/collect.d.ts.map +1 -1
  239. package/dist/streams/collect.js +74 -25
  240. package/dist/streams/collect.js.map +1 -1
  241. package/dist/streams/file-source.cjs +287 -0
  242. package/dist/streams/file-source.cjs.map +1 -0
  243. package/dist/streams/file-source.d.cts +96 -0
  244. package/dist/streams/file-source.d.cts.map +1 -0
  245. package/dist/streams/file-source.d.ts +96 -0
  246. package/dist/streams/file-source.d.ts.map +1 -0
  247. package/dist/streams/file-source.js +286 -0
  248. package/dist/streams/file-source.js.map +1 -0
  249. package/dist/streams/hash.cjs +207 -115
  250. package/dist/streams/hash.cjs.map +1 -1
  251. package/dist/streams/hash.d.cts +5 -4
  252. package/dist/streams/hash.d.cts.map +1 -1
  253. package/dist/streams/hash.d.ts +5 -4
  254. package/dist/streams/hash.d.ts.map +1 -1
  255. package/dist/streams/hash.js +208 -95
  256. package/dist/streams/hash.js.map +1 -1
  257. package/dist/streams/index.cjs +12 -12
  258. package/dist/streams/index.d.cts +3 -3
  259. package/dist/streams/index.d.cts.map +1 -1
  260. package/dist/streams/index.d.ts +3 -3
  261. package/dist/streams/index.d.ts.map +1 -1
  262. package/dist/streams/index.js +2 -10
  263. package/dist/streams/progress.cjs +60 -46
  264. package/dist/streams/progress.cjs.map +1 -1
  265. package/dist/streams/progress.js +60 -46
  266. package/dist/streams/progress.js.map +1 -1
  267. package/dist/streams/source.cjs +316 -146
  268. package/dist/streams/source.cjs.map +1 -1
  269. package/dist/streams/source.d.cts +56 -18
  270. package/dist/streams/source.d.cts.map +1 -1
  271. package/dist/streams/source.d.ts +56 -18
  272. package/dist/streams/source.d.ts.map +1 -1
  273. package/dist/streams/source.js +312 -149
  274. package/dist/streams/source.js.map +1 -1
  275. package/dist/sync/actions/index.cjs +236 -184
  276. package/dist/sync/actions/index.cjs.map +1 -1
  277. package/dist/sync/actions/index.d.cts +29 -21
  278. package/dist/sync/actions/index.d.cts.map +1 -1
  279. package/dist/sync/actions/index.d.ts +29 -21
  280. package/dist/sync/actions/index.d.ts.map +1 -1
  281. package/dist/sync/actions/index.js +236 -190
  282. package/dist/sync/actions/index.js.map +1 -1
  283. package/dist/sync/b2-sha1-reader.cjs +148 -0
  284. package/dist/sync/b2-sha1-reader.cjs.map +1 -0
  285. package/dist/sync/b2-sha1-reader.d.cts +58 -0
  286. package/dist/sync/b2-sha1-reader.d.cts.map +1 -0
  287. package/dist/sync/b2-sha1-reader.d.ts +58 -0
  288. package/dist/sync/b2-sha1-reader.d.ts.map +1 -0
  289. package/dist/sync/b2-sha1-reader.js +145 -0
  290. package/dist/sync/b2-sha1-reader.js.map +1 -0
  291. package/dist/sync/download-staging.cjs +265 -0
  292. package/dist/sync/download-staging.cjs.map +1 -0
  293. package/dist/sync/download-staging.d.cts +54 -0
  294. package/dist/sync/download-staging.d.cts.map +1 -0
  295. package/dist/sync/download-staging.d.ts +54 -0
  296. package/dist/sync/download-staging.d.ts.map +1 -0
  297. package/dist/sync/download-staging.js +259 -0
  298. package/dist/sync/download-staging.js.map +1 -0
  299. package/dist/sync/filesystem-errors.cjs +32 -0
  300. package/dist/sync/filesystem-errors.cjs.map +1 -0
  301. package/dist/sync/filesystem-errors.d.cts +8 -0
  302. package/dist/sync/filesystem-errors.d.cts.map +1 -0
  303. package/dist/sync/filesystem-errors.d.ts +8 -0
  304. package/dist/sync/filesystem-errors.d.ts.map +1 -0
  305. package/dist/sync/filesystem-errors.js +32 -0
  306. package/dist/sync/filesystem-errors.js.map +1 -0
  307. package/dist/sync/filters.cjs +217 -0
  308. package/dist/sync/filters.cjs.map +1 -0
  309. package/dist/sync/filters.d.cts +51 -0
  310. package/dist/sync/filters.d.cts.map +1 -0
  311. package/dist/sync/filters.d.ts +51 -0
  312. package/dist/sync/filters.d.ts.map +1 -0
  313. package/dist/sync/filters.js +213 -0
  314. package/dist/sync/filters.js.map +1 -0
  315. package/dist/sync/index.cjs +39 -22
  316. package/dist/sync/index.d.cts +14 -9
  317. package/dist/sync/index.d.cts.map +1 -1
  318. package/dist/sync/index.d.ts +14 -9
  319. package/dist/sync/index.d.ts.map +1 -1
  320. package/dist/sync/index.js +9 -20
  321. package/dist/sync/local-file-identity.cjs +61 -0
  322. package/dist/sync/local-file-identity.cjs.map +1 -0
  323. package/dist/sync/local-file-identity.d.cts +37 -0
  324. package/dist/sync/local-file-identity.d.cts.map +1 -0
  325. package/dist/sync/local-file-identity.d.ts +37 -0
  326. package/dist/sync/local-file-identity.d.ts.map +1 -0
  327. package/dist/sync/local-file-identity.js +60 -0
  328. package/dist/sync/local-file-identity.js.map +1 -0
  329. package/dist/sync/local-file-io.cjs +364 -0
  330. package/dist/sync/local-file-io.cjs.map +1 -0
  331. package/dist/sync/local-file-io.d.cts +75 -0
  332. package/dist/sync/local-file-io.d.cts.map +1 -0
  333. package/dist/sync/local-file-io.d.ts +75 -0
  334. package/dist/sync/local-file-io.d.ts.map +1 -0
  335. package/dist/sync/local-file-io.js +360 -0
  336. package/dist/sync/local-file-io.js.map +1 -0
  337. package/dist/sync/local-filesystem-root.cjs +27 -0
  338. package/dist/sync/local-filesystem-root.cjs.map +1 -0
  339. package/dist/sync/local-filesystem-root.d.cts +18 -0
  340. package/dist/sync/local-filesystem-root.d.cts.map +1 -0
  341. package/dist/sync/local-filesystem-root.d.ts +18 -0
  342. package/dist/sync/local-filesystem-root.d.ts.map +1 -0
  343. package/dist/sync/local-filesystem-root.js +26 -0
  344. package/dist/sync/local-filesystem-root.js.map +1 -0
  345. package/dist/sync/local-sha1.cjs +119 -0
  346. package/dist/sync/local-sha1.cjs.map +1 -0
  347. package/dist/sync/local-sha1.d.cts +39 -0
  348. package/dist/sync/local-sha1.d.cts.map +1 -0
  349. package/dist/sync/local-sha1.d.ts +39 -0
  350. package/dist/sync/local-sha1.d.ts.map +1 -0
  351. package/dist/sync/local-sha1.js +117 -0
  352. package/dist/sync/local-sha1.js.map +1 -0
  353. package/dist/sync/pairing.cjs +118 -29
  354. package/dist/sync/pairing.cjs.map +1 -1
  355. package/dist/sync/pairing.d.cts +7 -2
  356. package/dist/sync/pairing.d.cts.map +1 -1
  357. package/dist/sync/pairing.d.ts +7 -2
  358. package/dist/sync/pairing.d.ts.map +1 -1
  359. package/dist/sync/pairing.js +120 -31
  360. package/dist/sync/pairing.js.map +1 -1
  361. package/dist/sync/path-order.cjs +30 -0
  362. package/dist/sync/path-order.cjs.map +1 -0
  363. package/dist/sync/path-order.d.cts +28 -0
  364. package/dist/sync/path-order.d.cts.map +1 -0
  365. package/dist/sync/path-order.d.ts +28 -0
  366. package/dist/sync/path-order.d.ts.map +1 -0
  367. package/dist/sync/path-order.js +29 -0
  368. package/dist/sync/path-order.js.map +1 -0
  369. package/dist/sync/path-safety.cjs +111 -0
  370. package/dist/sync/path-safety.cjs.map +1 -0
  371. package/dist/sync/path-safety.d.cts +78 -0
  372. package/dist/sync/path-safety.d.cts.map +1 -0
  373. package/dist/sync/path-safety.d.ts +78 -0
  374. package/dist/sync/path-safety.d.ts.map +1 -0
  375. package/dist/sync/path-safety.js +105 -0
  376. package/dist/sync/path-safety.js.map +1 -0
  377. package/dist/sync/paths.d.cts +19 -0
  378. package/dist/sync/paths.d.cts.map +1 -0
  379. package/dist/sync/paths.d.ts +19 -0
  380. package/dist/sync/paths.d.ts.map +1 -0
  381. package/dist/sync/policies/compare.cjs +389 -11
  382. package/dist/sync/policies/compare.cjs.map +1 -1
  383. package/dist/sync/policies/compare.d.cts +100 -2
  384. package/dist/sync/policies/compare.d.cts.map +1 -1
  385. package/dist/sync/policies/compare.d.ts +100 -2
  386. package/dist/sync/policies/compare.d.ts.map +1 -1
  387. package/dist/sync/policies/compare.js +387 -13
  388. package/dist/sync/policies/compare.js.map +1 -1
  389. package/dist/sync/policies/index.cjs +76 -65
  390. package/dist/sync/policies/index.cjs.map +1 -1
  391. package/dist/sync/policies/index.d.cts +35 -6
  392. package/dist/sync/policies/index.d.cts.map +1 -1
  393. package/dist/sync/policies/index.d.ts +35 -6
  394. package/dist/sync/policies/index.d.ts.map +1 -1
  395. package/dist/sync/policies/index.js +76 -65
  396. package/dist/sync/policies/index.js.map +1 -1
  397. package/dist/sync/prefix.cjs +96 -0
  398. package/dist/sync/prefix.cjs.map +1 -0
  399. package/dist/sync/prefix.d.cts +55 -0
  400. package/dist/sync/prefix.d.cts.map +1 -0
  401. package/dist/sync/prefix.d.ts +55 -0
  402. package/dist/sync/prefix.d.ts.map +1 -0
  403. package/dist/sync/prefix.js +92 -0
  404. package/dist/sync/prefix.js.map +1 -0
  405. package/dist/sync/regexp-safety.cjs +192 -0
  406. package/dist/sync/regexp-safety.cjs.map +1 -0
  407. package/dist/sync/regexp-safety.d.cts +29 -0
  408. package/dist/sync/regexp-safety.d.cts.map +1 -0
  409. package/dist/sync/regexp-safety.d.ts +29 -0
  410. package/dist/sync/regexp-safety.d.ts.map +1 -0
  411. package/dist/sync/regexp-safety.js +190 -0
  412. package/dist/sync/regexp-safety.js.map +1 -0
  413. package/dist/sync/scan-events.cjs +33 -0
  414. package/dist/sync/scan-events.cjs.map +1 -0
  415. package/dist/sync/scan-events.d.cts +17 -0
  416. package/dist/sync/scan-events.d.cts.map +1 -0
  417. package/dist/sync/scan-events.d.ts +17 -0
  418. package/dist/sync/scan-events.d.ts.map +1 -0
  419. package/dist/sync/scan-events.js +32 -0
  420. package/dist/sync/scan-events.js.map +1 -0
  421. package/dist/sync/scan-limit.cjs +33 -0
  422. package/dist/sync/scan-limit.cjs.map +1 -0
  423. package/dist/sync/scan-limit.d.cts +21 -0
  424. package/dist/sync/scan-limit.d.cts.map +1 -0
  425. package/dist/sync/scan-limit.d.ts +21 -0
  426. package/dist/sync/scan-limit.d.ts.map +1 -0
  427. package/dist/sync/scan-limit.js +31 -0
  428. package/dist/sync/scan-limit.js.map +1 -0
  429. package/dist/sync/scanners/b2.cjs +239 -55
  430. package/dist/sync/scanners/b2.cjs.map +1 -1
  431. package/dist/sync/scanners/b2.d.cts +26 -7
  432. package/dist/sync/scanners/b2.d.cts.map +1 -1
  433. package/dist/sync/scanners/b2.d.ts +26 -7
  434. package/dist/sync/scanners/b2.d.ts.map +1 -1
  435. package/dist/sync/scanners/b2.js +239 -55
  436. package/dist/sync/scanners/b2.js.map +1 -1
  437. package/dist/sync/scanners/local.cjs +215 -56
  438. package/dist/sync/scanners/local.cjs.map +1 -1
  439. package/dist/sync/scanners/local.d.cts +22 -7
  440. package/dist/sync/scanners/local.d.cts.map +1 -1
  441. package/dist/sync/scanners/local.d.ts +22 -7
  442. package/dist/sync/scanners/local.d.ts.map +1 -1
  443. package/dist/sync/scanners/local.js +216 -57
  444. package/dist/sync/scanners/local.js.map +1 -1
  445. package/dist/sync/sha1-metadata.cjs +94 -0
  446. package/dist/sync/sha1-metadata.cjs.map +1 -0
  447. package/dist/sync/sha1-metadata.d.cts +82 -0
  448. package/dist/sync/sha1-metadata.d.cts.map +1 -0
  449. package/dist/sync/sha1-metadata.d.ts +82 -0
  450. package/dist/sync/sha1-metadata.d.ts.map +1 -0
  451. package/dist/sync/sha1-metadata.js +89 -0
  452. package/dist/sync/sha1-metadata.js.map +1 -0
  453. package/dist/sync/sha1-options.cjs +23 -0
  454. package/dist/sync/sha1-options.cjs.map +1 -0
  455. package/dist/sync/sha1-options.d.cts +14 -0
  456. package/dist/sync/sha1-options.d.cts.map +1 -0
  457. package/dist/sync/sha1-options.d.ts +14 -0
  458. package/dist/sync/sha1-options.d.ts.map +1 -0
  459. package/dist/sync/sha1-options.js +21 -0
  460. package/dist/sync/sha1-options.js.map +1 -0
  461. package/dist/sync/synchronizer.cjs +782 -197
  462. package/dist/sync/synchronizer.cjs.map +1 -1
  463. package/dist/sync/synchronizer.d.cts +47 -1
  464. package/dist/sync/synchronizer.d.cts.map +1 -1
  465. package/dist/sync/synchronizer.d.ts +47 -1
  466. package/dist/sync/synchronizer.d.ts.map +1 -1
  467. package/dist/sync/synchronizer.js +779 -173
  468. package/dist/sync/synchronizer.js.map +1 -1
  469. package/dist/sync/types.d.cts +208 -16
  470. package/dist/sync/types.d.cts.map +1 -1
  471. package/dist/sync/types.d.ts +208 -16
  472. package/dist/sync/types.d.ts.map +1 -1
  473. package/dist/types/auth.cjs +76 -56
  474. package/dist/types/auth.cjs.map +1 -1
  475. package/dist/types/auth.d.cts +67 -9
  476. package/dist/types/auth.d.cts.map +1 -1
  477. package/dist/types/auth.d.ts +67 -9
  478. package/dist/types/auth.d.ts.map +1 -1
  479. package/dist/types/auth.js +77 -57
  480. package/dist/types/auth.js.map +1 -1
  481. package/dist/types/bucket.cjs +74 -38
  482. package/dist/types/bucket.cjs.map +1 -1
  483. package/dist/types/bucket.js +75 -41
  484. package/dist/types/bucket.js.map +1 -1
  485. package/dist/types/encryption.cjs +364 -327
  486. package/dist/types/encryption.cjs.map +1 -1
  487. package/dist/types/encryption.d.cts +16 -0
  488. package/dist/types/encryption.d.cts.map +1 -1
  489. package/dist/types/encryption.d.ts +16 -0
  490. package/dist/types/encryption.d.ts.map +1 -1
  491. package/dist/types/encryption.js +364 -310
  492. package/dist/types/encryption.js.map +1 -1
  493. package/dist/types/errors.cjs +47 -0
  494. package/dist/types/errors.cjs.map +1 -0
  495. package/dist/types/errors.d.cts +13 -4
  496. package/dist/types/errors.d.cts.map +1 -1
  497. package/dist/types/errors.d.ts +13 -4
  498. package/dist/types/errors.d.ts.map +1 -1
  499. package/dist/types/errors.js +47 -0
  500. package/dist/types/errors.js.map +1 -0
  501. package/dist/types/file.cjs +36 -19
  502. package/dist/types/file.cjs.map +1 -1
  503. package/dist/types/file.d.cts +2 -2
  504. package/dist/types/file.d.cts.map +1 -1
  505. package/dist/types/file.d.ts +2 -2
  506. package/dist/types/file.d.ts.map +1 -1
  507. package/dist/types/file.js +37 -21
  508. package/dist/types/file.js.map +1 -1
  509. package/dist/types/ids.cjs +53 -9
  510. package/dist/types/ids.cjs.map +1 -1
  511. package/dist/types/ids.js +54 -15
  512. package/dist/types/ids.js.map +1 -1
  513. package/dist/types/index.d.cts +12 -11
  514. package/dist/types/index.d.cts.map +1 -1
  515. package/dist/types/index.d.ts +12 -11
  516. package/dist/types/index.d.ts.map +1 -1
  517. package/dist/types/key.d.cts +44 -8
  518. package/dist/types/key.d.cts.map +1 -1
  519. package/dist/types/key.d.ts +44 -8
  520. package/dist/types/key.d.ts.map +1 -1
  521. package/dist/types/lock.cjs +33 -13
  522. package/dist/types/lock.cjs.map +1 -1
  523. package/dist/types/lock.js +34 -15
  524. package/dist/types/lock.js.map +1 -1
  525. package/dist/types/notifications.cjs +44 -22
  526. package/dist/types/notifications.cjs.map +1 -1
  527. package/dist/types/notifications.js +45 -23
  528. package/dist/types/notifications.js.map +1 -1
  529. package/dist/types/upload.d.cts +39 -5
  530. package/dist/types/upload.d.cts.map +1 -1
  531. package/dist/types/upload.d.ts +39 -5
  532. package/dist/types/upload.d.ts.map +1 -1
  533. package/dist/upload/abort-scope.cjs +91 -0
  534. package/dist/upload/abort-scope.cjs.map +1 -0
  535. package/dist/upload/abort-scope.d.cts +48 -0
  536. package/dist/upload/abort-scope.d.cts.map +1 -0
  537. package/dist/upload/abort-scope.d.ts +48 -0
  538. package/dist/upload/abort-scope.d.ts.map +1 -0
  539. package/dist/upload/abort-scope.js +88 -0
  540. package/dist/upload/abort-scope.js.map +1 -0
  541. package/dist/upload/cancel.cjs +183 -8
  542. package/dist/upload/cancel.cjs.map +1 -1
  543. package/dist/upload/cancel.d.cts +110 -2
  544. package/dist/upload/cancel.d.cts.map +1 -1
  545. package/dist/upload/cancel.d.ts +110 -2
  546. package/dist/upload/cancel.d.ts.map +1 -1
  547. package/dist/upload/cancel.js +178 -9
  548. package/dist/upload/cancel.js.map +1 -1
  549. package/dist/upload/concurrency.cjs +79 -52
  550. package/dist/upload/concurrency.cjs.map +1 -1
  551. package/dist/upload/concurrency.js +79 -53
  552. package/dist/upload/concurrency.js.map +1 -1
  553. package/dist/upload/finish.cjs +59 -0
  554. package/dist/upload/finish.cjs.map +1 -0
  555. package/dist/upload/finish.d.cts +26 -0
  556. package/dist/upload/finish.d.cts.map +1 -0
  557. package/dist/upload/finish.d.ts +26 -0
  558. package/dist/upload/finish.d.ts.map +1 -0
  559. package/dist/upload/finish.js +59 -0
  560. package/dist/upload/finish.js.map +1 -0
  561. package/dist/upload/large.cjs +301 -218
  562. package/dist/upload/large.cjs.map +1 -1
  563. package/dist/upload/large.d.cts +76 -9
  564. package/dist/upload/large.d.cts.map +1 -1
  565. package/dist/upload/large.d.ts +76 -9
  566. package/dist/upload/large.d.ts.map +1 -1
  567. package/dist/upload/large.js +301 -218
  568. package/dist/upload/large.js.map +1 -1
  569. package/dist/upload/options.cjs +28 -0
  570. package/dist/upload/options.cjs.map +1 -0
  571. package/dist/upload/options.d.cts +104 -0
  572. package/dist/upload/options.d.cts.map +1 -0
  573. package/dist/upload/options.d.ts +104 -0
  574. package/dist/upload/options.d.ts.map +1 -0
  575. package/dist/upload/options.js +27 -0
  576. package/dist/upload/options.js.map +1 -0
  577. package/dist/upload/resume.cjs +333 -33
  578. package/dist/upload/resume.cjs.map +1 -1
  579. package/dist/upload/resume.d.cts +82 -12
  580. package/dist/upload/resume.d.cts.map +1 -1
  581. package/dist/upload/resume.d.ts +82 -12
  582. package/dist/upload/resume.d.ts.map +1 -1
  583. package/dist/upload/resume.js +330 -33
  584. package/dist/upload/resume.js.map +1 -1
  585. package/dist/upload/retry.cjs +186 -0
  586. package/dist/upload/retry.cjs.map +1 -0
  587. package/dist/upload/retry.d.cts +127 -0
  588. package/dist/upload/retry.d.cts.map +1 -0
  589. package/dist/upload/retry.d.ts +127 -0
  590. package/dist/upload/retry.d.ts.map +1 -0
  591. package/dist/upload/retry.js +182 -0
  592. package/dist/upload/retry.js.map +1 -0
  593. package/dist/upload/single.cjs +68 -44
  594. package/dist/upload/single.cjs.map +1 -1
  595. package/dist/upload/single.d.cts +2 -1
  596. package/dist/upload/single.d.cts.map +1 -1
  597. package/dist/upload/single.d.ts +2 -1
  598. package/dist/upload/single.d.ts.map +1 -1
  599. package/dist/upload/single.js +68 -44
  600. package/dist/upload/single.js.map +1 -1
  601. package/dist/upload/stream.cjs +317 -193
  602. package/dist/upload/stream.cjs.map +1 -1
  603. package/dist/upload/stream.d.cts +3 -1
  604. package/dist/upload/stream.d.cts.map +1 -1
  605. package/dist/upload/stream.d.ts +3 -1
  606. package/dist/upload/stream.d.ts.map +1 -1
  607. package/dist/upload/stream.js +316 -192
  608. package/dist/upload/stream.js.map +1 -1
  609. package/dist/util/best-effort.cjs +42 -8
  610. package/dist/util/best-effort.cjs.map +1 -1
  611. package/dist/util/best-effort.d.cts +2 -1
  612. package/dist/util/best-effort.d.cts.map +1 -1
  613. package/dist/util/best-effort.d.ts +2 -1
  614. package/dist/util/best-effort.d.ts.map +1 -1
  615. package/dist/util/best-effort.js +43 -9
  616. package/dist/util/best-effort.js.map +1 -1
  617. package/dist/util/bytes.cjs +23 -0
  618. package/dist/util/bytes.cjs.map +1 -0
  619. package/dist/util/bytes.d.cts +15 -0
  620. package/dist/util/bytes.d.cts.map +1 -0
  621. package/dist/util/bytes.d.ts +15 -0
  622. package/dist/util/bytes.d.ts.map +1 -0
  623. package/dist/util/bytes.js +23 -0
  624. package/dist/util/bytes.js.map +1 -0
  625. package/dist/util/crypto.cjs +68 -0
  626. package/dist/util/crypto.cjs.map +1 -0
  627. package/dist/util/crypto.d.cts +26 -0
  628. package/dist/util/crypto.d.cts.map +1 -0
  629. package/dist/util/crypto.d.ts +26 -0
  630. package/dist/util/crypto.d.ts.map +1 -0
  631. package/dist/util/crypto.js +66 -0
  632. package/dist/util/crypto.js.map +1 -0
  633. package/dist/util/defaults.cjs +25 -9
  634. package/dist/util/defaults.cjs.map +1 -1
  635. package/dist/util/defaults.js +26 -11
  636. package/dist/util/defaults.js.map +1 -1
  637. package/dist/util/error-reason.cjs +41 -0
  638. package/dist/util/error-reason.cjs.map +1 -0
  639. package/dist/util/error-reason.d.cts +9 -0
  640. package/dist/util/error-reason.d.cts.map +1 -0
  641. package/dist/util/error-reason.d.ts +9 -0
  642. package/dist/util/error-reason.d.ts.map +1 -0
  643. package/dist/util/error-reason.js +41 -0
  644. package/dist/util/error-reason.js.map +1 -0
  645. package/dist/util/http.cjs +23 -0
  646. package/dist/util/http.cjs.map +1 -0
  647. package/dist/util/http.d.cts +13 -0
  648. package/dist/util/http.d.cts.map +1 -0
  649. package/dist/util/http.d.ts +13 -0
  650. package/dist/util/http.d.ts.map +1 -0
  651. package/dist/util/http.js +23 -0
  652. package/dist/util/http.js.map +1 -0
  653. package/dist/util/normalize.cjs +66 -7
  654. package/dist/util/normalize.cjs.map +1 -1
  655. package/dist/util/normalize.js +67 -10
  656. package/dist/util/normalize.js.map +1 -1
  657. package/dist/util/paginator.cjs +63 -14
  658. package/dist/util/paginator.cjs.map +1 -1
  659. package/dist/util/paginator.js +64 -16
  660. package/dist/util/paginator.js.map +1 -1
  661. package/dist/util/plan-ranges.cjs +47 -22
  662. package/dist/util/plan-ranges.cjs.map +1 -1
  663. package/dist/util/plan-ranges.js +48 -24
  664. package/dist/util/plan-ranges.js.map +1 -1
  665. package/dist/util/sha1.cjs +27 -0
  666. package/dist/util/sha1.cjs.map +1 -0
  667. package/dist/util/sha1.d.cts +17 -0
  668. package/dist/util/sha1.d.cts.map +1 -0
  669. package/dist/util/sha1.d.ts +17 -0
  670. package/dist/util/sha1.d.ts.map +1 -0
  671. package/dist/util/sha1.js +26 -0
  672. package/dist/util/sha1.js.map +1 -0
  673. package/dist/util/text-codec.cjs +29 -5
  674. package/dist/util/text-codec.cjs.map +1 -1
  675. package/dist/util/text-codec.js +30 -7
  676. package/dist/util/text-codec.js.map +1 -1
  677. package/dist/util/to-error.cjs +18 -4
  678. package/dist/util/to-error.cjs.map +1 -1
  679. package/dist/util/to-error.js +19 -5
  680. package/dist/util/to-error.js.map +1 -1
  681. package/dist/version.cjs +17 -5
  682. package/dist/version.cjs.map +1 -1
  683. package/dist/version.d.cts +3 -3
  684. package/dist/version.d.ts +3 -3
  685. package/dist/version.js +19 -6
  686. package/dist/version.js.map +1 -1
  687. package/package.json +19 -16
  688. package/dist/auth/index.cjs.map +0 -1
  689. package/dist/auth/index.js.map +0 -1
  690. package/dist/index.cjs.map +0 -1
  691. package/dist/index.js.map +0 -1
  692. package/dist/notifications/index.cjs.map +0 -1
  693. package/dist/notifications/index.js.map +0 -1
  694. package/dist/package.json.cjs +0 -9
  695. package/dist/package.json.cjs.map +0 -1
  696. package/dist/package.json.js +0 -9
  697. package/dist/package.json.js.map +0 -1
  698. package/dist/streams/index.cjs.map +0 -1
  699. package/dist/streams/index.js.map +0 -1
  700. package/dist/sync/index.cjs.map +0 -1
  701. package/dist/sync/index.js.map +0 -1
@@ -1,174 +1,200 @@
1
1
  import { utf8Encoder } from "../util/text-codec.js";
2
- const BUCKET_NAME_MIN = 6;
3
- const BUCKET_NAME_MAX = 63;
4
- const BUCKET_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$/;
5
- const BUCKET_NAME_RESERVED_PREFIX = "b2-";
2
+ import { BUCKET_NAME_MAX, BUCKET_NAME_MIN, FILE_NAME_MAX_BYTES, getB2FileNameByteLength, hasB2FileNameControlCharacter, hasValidB2BucketNameShape, isB2BucketNameIpv4Address } from "../internal/b2-naming.js";
3
+ //#region src/simulator/validation.ts
4
+ /**
5
+ * B2 spec-compliance validation helpers for the simulator.
6
+ *
7
+ * Each function mirrors a documented B2 input limit (file name length,
8
+ * bucket name regex, file-info byte budget, etc.). Validators return
9
+ * `null` for valid input or a `{ code, message }` pair that the handler
10
+ * converts into a synthetic `400`/`403` response with the right B2
11
+ * error code.
12
+ *
13
+ * Keeping validation in one module makes the simulator's "what does B2
14
+ * reject?" surface auditable in a single file. Doc references inline at
15
+ * each rule so the spec source is immediate.
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ /**
20
+ * Validates a bucket name against B2's documented rules.
21
+ *
22
+ * @param name - Caller-supplied bucket name.
23
+ *
24
+ * @returns A `{ code, message }` pair on failure, or `null` when valid.
25
+ *
26
+ * @see https://www.backblaze.com/apidocs/b2-create-bucket
27
+ */
6
28
  function validateBucketName(name) {
7
- if (typeof name !== "string" || name.length < BUCKET_NAME_MIN || name.length > BUCKET_NAME_MAX) {
8
- return {
9
- code: "invalid_bucket_name",
10
- message: `bucketName must be ${BUCKET_NAME_MIN}-${BUCKET_NAME_MAX} characters`
11
- };
12
- }
13
- if (!BUCKET_NAME_REGEX.test(name)) {
14
- return {
15
- code: "invalid_bucket_name",
16
- message: "bucketName must contain only letters, digits, and hyphens; cannot start or end with a hyphen"
17
- };
18
- }
19
- if (name.startsWith(BUCKET_NAME_RESERVED_PREFIX)) {
20
- return {
21
- code: "invalid_bucket_name",
22
- message: `bucketName cannot start with the reserved prefix "${BUCKET_NAME_RESERVED_PREFIX}"`
23
- };
24
- }
25
- return null;
29
+ if (typeof name !== "string" || name.length < 6 || name.length > 63) return {
30
+ code: "invalid_bucket_name",
31
+ message: `bucketName must be 6-63 characters`
32
+ };
33
+ if (isB2BucketNameIpv4Address(name)) return {
34
+ code: "invalid_bucket_name",
35
+ message: "bucketName must not be formatted as an IPv4 address"
36
+ };
37
+ if (!hasValidB2BucketNameShape(name)) return {
38
+ code: "invalid_bucket_name",
39
+ message: "bucketName must contain only letters, digits, hyphens, and periods; cannot start or end with punctuation or contain consecutive periods"
40
+ };
41
+ if (name.startsWith("b2-")) return {
42
+ code: "invalid_bucket_name",
43
+ message: `bucketName cannot start with the reserved prefix "b2-"`
44
+ };
45
+ return null;
26
46
  }
27
- const FILE_NAME_MAX_BYTES = 1024;
28
- const FILE_NAME_DEL = 127;
47
+ /**
48
+ * Validates a file name against B2's documented rules. Mirrors the
49
+ * server-side checks B2 runs on `b2_upload_file` / `b2_hide_file` /
50
+ * `b2_start_large_file` / `b2_copy_file` filename inputs.
51
+ *
52
+ * @param name - Caller-supplied file name (raw, unencoded).
53
+ *
54
+ * @returns A `{ code, message }` pair on failure, or `null` when valid.
55
+ *
56
+ * @see https://www.backblaze.com/apidocs/b2-upload-file
57
+ */
29
58
  function validateFileName(name) {
30
- if (typeof name !== "string" || name.length === 0) {
31
- return { code: "invalid_file_name", message: "fileName must be a non-empty string" };
32
- }
33
- const bytes = utf8Encoder.encode(name);
34
- if (bytes.byteLength > FILE_NAME_MAX_BYTES) {
35
- return {
36
- code: "invalid_file_name",
37
- message: `fileName exceeds the 1024-byte UTF-8 limit (got ${bytes.byteLength})`
38
- };
39
- }
40
- for (let i = 0; i < name.length; i++) {
41
- const code = name.charCodeAt(i);
42
- if (code < 32 || code === FILE_NAME_DEL) {
43
- return {
44
- code: "invalid_file_name",
45
- message: "fileName must not contain control characters (U+0000-U+001F or U+007F)"
46
- };
47
- }
48
- }
49
- if (name === "." || name === "..") {
50
- return {
51
- code: "invalid_file_name",
52
- message: 'fileName cannot be exactly "." or ".."'
53
- };
54
- }
55
- if (name.startsWith("/") || name.endsWith("/") || name.includes("//")) {
56
- return {
57
- code: "invalid_file_name",
58
- message: 'fileName cannot start with "/", end with "/", or contain "//"'
59
- };
60
- }
61
- return null;
59
+ if (typeof name !== "string" || name.length === 0) return {
60
+ code: "invalid_file_name",
61
+ message: "fileName must be a non-empty string"
62
+ };
63
+ if (getB2FileNameByteLength(name) === null) return {
64
+ code: "invalid_file_name",
65
+ message: "fileName exceeds the 1024-byte UTF-8 limit"
66
+ };
67
+ if (hasB2FileNameControlCharacter(name)) return {
68
+ code: "invalid_file_name",
69
+ message: "fileName must not contain control characters (U+0000-U+001F or U+007F)"
70
+ };
71
+ if (name === "." || name === "..") return {
72
+ code: "invalid_file_name",
73
+ message: "fileName cannot be exactly \".\" or \"..\""
74
+ };
75
+ if (name.startsWith("/") || name.endsWith("/") || name.includes("//")) return {
76
+ code: "invalid_file_name",
77
+ message: "fileName cannot start with \"/\", end with \"/\", or contain \"//\""
78
+ };
79
+ return null;
62
80
  }
63
- const FILE_INFO_TOTAL_MAX = 2048;
64
- const FILE_INFO_VALUE_MAX = 2048;
65
- const FILE_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/;
81
+ /** Per-file fileInfo total size cap (sum of key+value bytes). */
82
+ var FILE_INFO_TOTAL_MAX = 2048;
83
+ /** Per-key fileInfo value byte cap. */
84
+ var FILE_INFO_VALUE_MAX = 2048;
85
+ /** Allowed key character set (case-insensitive). */
86
+ var FILE_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/;
87
+ /**
88
+ * Validates a `fileInfo` record against B2's byte-budget and
89
+ * key-shape rules.
90
+ *
91
+ * @param info - Caller-supplied fileInfo record.
92
+ *
93
+ * @returns A `{ code, message }` pair on failure, or `null` when valid.
94
+ *
95
+ * @see https://www.backblaze.com/apidocs/b2-upload-file
96
+ */
66
97
  function validateFileInfo(info) {
67
- let total = 0;
68
- for (const [key, value] of Object.entries(info)) {
69
- if (!FILE_INFO_KEY_REGEX.test(key)) {
70
- return {
71
- code: "invalid_file_info",
72
- message: `fileInfo key "${key}" must match ^[a-zA-Z0-9_-]+$`
73
- };
74
- }
75
- if (typeof value !== "string") {
76
- return { code: "invalid_file_info", message: `fileInfo value for "${key}" must be a string` };
77
- }
78
- const keyBytes = utf8Encoder.encode(key).byteLength;
79
- const valueBytes = utf8Encoder.encode(value).byteLength;
80
- if (valueBytes > FILE_INFO_VALUE_MAX) {
81
- return {
82
- code: "invalid_file_info",
83
- message: `fileInfo value for "${key}" exceeds ${FILE_INFO_VALUE_MAX} bytes`
84
- };
85
- }
86
- total += keyBytes + valueBytes;
87
- }
88
- if (total > FILE_INFO_TOTAL_MAX) {
89
- return {
90
- code: "invalid_file_info",
91
- message: `fileInfo total size ${total} bytes exceeds the ${FILE_INFO_TOTAL_MAX}-byte limit`
92
- };
93
- }
94
- return null;
98
+ let total = 0;
99
+ for (const [key, value] of Object.entries(info)) {
100
+ if (!FILE_INFO_KEY_REGEX.test(key)) return {
101
+ code: "invalid_file_info",
102
+ message: `fileInfo key "${key}" must match ^[a-zA-Z0-9_-]+$`
103
+ };
104
+ if (typeof value !== "string") return {
105
+ code: "invalid_file_info",
106
+ message: `fileInfo value for "${key}" must be a string`
107
+ };
108
+ const keyBytes = utf8Encoder.encode(key).byteLength;
109
+ const valueBytes = utf8Encoder.encode(value).byteLength;
110
+ if (valueBytes > 2048) return {
111
+ code: "invalid_file_info",
112
+ message: `fileInfo value for "${key}" exceeds ${FILE_INFO_VALUE_MAX} bytes`
113
+ };
114
+ total += keyBytes + valueBytes;
115
+ }
116
+ if (total > 2048) return {
117
+ code: "invalid_file_info",
118
+ message: `fileInfo total size ${total} bytes exceeds the ${FILE_INFO_TOTAL_MAX}-byte limit`
119
+ };
120
+ return null;
95
121
  }
96
- const BUCKET_INFO_MAX_KEYS = 10;
97
- const BUCKET_INFO_VALUE_MAX = 2048;
98
- const BUCKET_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/;
122
+ /** Per-bucket bucketInfo total max keys. */
123
+ var BUCKET_INFO_MAX_KEYS = 10;
124
+ /** Per-key bucketInfo value byte cap. */
125
+ var BUCKET_INFO_VALUE_MAX = 2048;
126
+ /** Allowed key character set for bucketInfo. */
127
+ var BUCKET_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/;
128
+ /**
129
+ * Validates a `bucketInfo` record against B2's per-bucket metadata rules.
130
+ *
131
+ * @param info - Caller-supplied bucketInfo record.
132
+ *
133
+ * @returns A `{ code, message }` pair on failure, or `null` when valid.
134
+ *
135
+ * @see https://www.backblaze.com/apidocs/b2-create-bucket
136
+ */
99
137
  function validateBucketInfo(info) {
100
- const entries = Object.entries(info);
101
- if (entries.length > BUCKET_INFO_MAX_KEYS) {
102
- return {
103
- code: "invalid_bucket_info",
104
- message: `bucketInfo cannot have more than ${BUCKET_INFO_MAX_KEYS} keys (got ${entries.length})`
105
- };
106
- }
107
- for (const [key, value] of entries) {
108
- if (!BUCKET_INFO_KEY_REGEX.test(key)) {
109
- return {
110
- code: "invalid_bucket_info",
111
- message: `bucketInfo key "${key}" must match ^[a-zA-Z0-9_-]+$`
112
- };
113
- }
114
- if (typeof value !== "string") {
115
- return {
116
- code: "invalid_bucket_info",
117
- message: `bucketInfo value for "${key}" must be a string`
118
- };
119
- }
120
- const valueBytes = utf8Encoder.encode(value).byteLength;
121
- if (valueBytes > BUCKET_INFO_VALUE_MAX) {
122
- return {
123
- code: "invalid_bucket_info",
124
- message: `bucketInfo value for "${key}" exceeds ${BUCKET_INFO_VALUE_MAX} bytes`
125
- };
126
- }
127
- }
128
- return null;
138
+ const entries = Object.entries(info);
139
+ if (entries.length > 10) return {
140
+ code: "invalid_bucket_info",
141
+ message: `bucketInfo cannot have more than 10 keys (got ${entries.length})`
142
+ };
143
+ for (const [key, value] of entries) {
144
+ if (!BUCKET_INFO_KEY_REGEX.test(key)) return {
145
+ code: "invalid_bucket_info",
146
+ message: `bucketInfo key "${key}" must match ^[a-zA-Z0-9_-]+$`
147
+ };
148
+ if (typeof value !== "string") return {
149
+ code: "invalid_bucket_info",
150
+ message: `bucketInfo value for "${key}" must be a string`
151
+ };
152
+ if (utf8Encoder.encode(value).byteLength > 2048) return {
153
+ code: "invalid_bucket_info",
154
+ message: `bucketInfo value for "${key}" exceeds ${BUCKET_INFO_VALUE_MAX} bytes`
155
+ };
156
+ }
157
+ return null;
129
158
  }
130
- const LIST_ENDPOINT_CAPS = {
131
- /** `b2_list_file_names.maxFileCount` cap. */
132
- b2_list_file_names: 1e4,
133
- /** `b2_list_file_versions.maxFileCount` cap. */
134
- b2_list_file_versions: 1e4,
135
- /** `b2_list_unfinished_large_files.maxFileCount` cap. */
136
- b2_list_unfinished_large_files: 100,
137
- /** `b2_list_keys.maxKeyCount` cap. */
138
- b2_list_keys: 1e4,
139
- /** `b2_list_parts.maxPartCount` cap. */
140
- b2_list_parts: 1e4
159
+ /**
160
+ * Per-endpoint upper bound on the `maxFileCount` (or `maxKeyCount` /
161
+ * `maxPartCount`) request field. Sourced from the B2 endpoint docs.
162
+ */
163
+ var LIST_ENDPOINT_CAPS = {
164
+ /** `b2_list_file_names.maxFileCount` cap. */
165
+ b2_list_file_names: 1e4,
166
+ /** `b2_list_file_versions.maxFileCount` cap. */
167
+ b2_list_file_versions: 1e4,
168
+ /** `b2_list_unfinished_large_files.maxFileCount` cap. */
169
+ b2_list_unfinished_large_files: 100,
170
+ /** `b2_list_keys.maxKeyCount` cap. */
171
+ b2_list_keys: 1e4,
172
+ /** `b2_list_parts.maxPartCount` cap. */
173
+ b2_list_parts: 1e4
141
174
  };
175
+ /**
176
+ * Validates a `maxFileCount` (or `maxKeyCount` / `maxPartCount`) value
177
+ * against the per-endpoint cap documented by B2.
178
+ *
179
+ * @param requested - Caller-supplied count.
180
+ * @param endpoint - Which list endpoint this applies to.
181
+ *
182
+ * @returns A `{ code, message }` pair on failure, or `null` when valid.
183
+ */
142
184
  function validateMaxCount(requested, endpoint) {
143
- if (requested === void 0) return null;
144
- if (!Number.isInteger(requested) || requested < 1) {
145
- return {
146
- code: "bad_request",
147
- message: `maxFileCount must be a positive integer (got ${requested})`
148
- };
149
- }
150
- const cap = LIST_ENDPOINT_CAPS[endpoint];
151
- if (requested > cap) {
152
- return {
153
- code: "bad_request",
154
- message: `maxFileCount ${requested} exceeds the ${endpoint} cap of ${cap}`
155
- };
156
- }
157
- return null;
185
+ if (requested === void 0) return null;
186
+ if (!Number.isInteger(requested) || requested < 1) return {
187
+ code: "bad_request",
188
+ message: `maxFileCount must be a positive integer (got ${requested})`
189
+ };
190
+ const cap = LIST_ENDPOINT_CAPS[endpoint];
191
+ if (requested > cap) return {
192
+ code: "bad_request",
193
+ message: `maxFileCount ${requested} exceeds the ${endpoint} cap of ${cap}`
194
+ };
195
+ return null;
158
196
  }
159
- export {
160
- BUCKET_INFO_MAX_KEYS,
161
- BUCKET_INFO_VALUE_MAX,
162
- BUCKET_NAME_MAX,
163
- BUCKET_NAME_MIN,
164
- FILE_INFO_TOTAL_MAX,
165
- FILE_INFO_VALUE_MAX,
166
- FILE_NAME_MAX_BYTES,
167
- LIST_ENDPOINT_CAPS,
168
- validateBucketInfo,
169
- validateBucketName,
170
- validateFileInfo,
171
- validateFileName,
172
- validateMaxCount
173
- };
174
- //# sourceMappingURL=validation.js.map
197
+ //#endregion
198
+ export { BUCKET_INFO_MAX_KEYS, BUCKET_INFO_VALUE_MAX, BUCKET_NAME_MAX, BUCKET_NAME_MIN, FILE_INFO_TOTAL_MAX, FILE_INFO_VALUE_MAX, FILE_NAME_MAX_BYTES, LIST_ENDPOINT_CAPS, validateBucketInfo, validateBucketName, validateFileInfo, validateFileName, validateMaxCount };
199
+
200
+ //# sourceMappingURL=validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sources":["../../src/simulator/validation.ts"],"sourcesContent":["/**\n * B2 spec-compliance validation helpers for the simulator.\n *\n * Each function mirrors a documented B2 input limit (file name length,\n * bucket name regex, file-info byte budget, etc.). Validators return\n * `null` for valid input or a `{ code, message }` pair that the handler\n * converts into a synthetic `400`/`403` response with the right B2\n * error code.\n *\n * Keeping validation in one module makes the simulator's \"what does B2\n * reject?\" surface auditable in a single file. Doc references inline at\n * each rule so the spec source is immediate.\n *\n * @packageDocumentation\n */\n\nimport { utf8Encoder } from '../util/text-codec.ts'\n\n/** Shape returned by validation functions when input is rejected. */\nexport interface ValidationError {\n readonly code: string\n readonly message: string\n}\n\n// ---------------------------------------------------------------------------\n// Bucket name (`b2_create_bucket`, `b2_update_bucket`)\n// ---------------------------------------------------------------------------\n\n/** Minimum B2 bucket-name length per https://www.backblaze.com/apidocs/b2-create-bucket. */\nexport const BUCKET_NAME_MIN = 6\n/** Maximum B2 bucket-name length per https://www.backblaze.com/apidocs/b2-create-bucket. */\nexport const BUCKET_NAME_MAX = 63\n/** Bucket-name char set: letters, digits, hyphens. Anchored, leading/trailing hyphens are illegal. */\nconst BUCKET_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$/\n/** Reserved prefix — B2 forbids consumer-created buckets starting with `b2-`. */\nconst BUCKET_NAME_RESERVED_PREFIX = 'b2-'\n\n/**\n * Validates a bucket name against B2's documented rules.\n *\n * @param name - Caller-supplied bucket name.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-create-bucket\n */\nexport function validateBucketName(name: string): ValidationError | null {\n if (typeof name !== 'string' || name.length < BUCKET_NAME_MIN || name.length > BUCKET_NAME_MAX) {\n return {\n code: 'invalid_bucket_name',\n message: `bucketName must be ${BUCKET_NAME_MIN}-${BUCKET_NAME_MAX} characters`,\n }\n }\n if (!BUCKET_NAME_REGEX.test(name)) {\n return {\n code: 'invalid_bucket_name',\n message:\n 'bucketName must contain only letters, digits, and hyphens; cannot start or end with a hyphen',\n }\n }\n if (name.startsWith(BUCKET_NAME_RESERVED_PREFIX)) {\n return {\n code: 'invalid_bucket_name',\n message: `bucketName cannot start with the reserved prefix \"${BUCKET_NAME_RESERVED_PREFIX}\"`,\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// File name (every upload / hide / copy entry point)\n// ---------------------------------------------------------------------------\n\n/** Max file-name length is 1024 BYTES (UTF-8 encoded), not chars. */\nexport const FILE_NAME_MAX_BYTES = 1024\n/** Control-character range B2 rejects: U+0000-U+001F (32 codepoints) plus DEL (U+007F). */\nconst FILE_NAME_DEL = 0x7f\n\n/**\n * Validates a file name against B2's documented rules. Mirrors the\n * server-side checks B2 runs on `b2_upload_file` / `b2_hide_file` /\n * `b2_start_large_file` / `b2_copy_file` filename inputs.\n *\n * @param name - Caller-supplied file name (raw, unencoded).\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-upload-file\n */\nexport function validateFileName(name: string): ValidationError | null {\n if (typeof name !== 'string' || name.length === 0) {\n return { code: 'invalid_file_name', message: 'fileName must be a non-empty string' }\n }\n const bytes = utf8Encoder.encode(name)\n if (bytes.byteLength > FILE_NAME_MAX_BYTES) {\n return {\n code: 'invalid_file_name',\n message: `fileName exceeds the 1024-byte UTF-8 limit (got ${bytes.byteLength})`,\n }\n }\n for (let i = 0; i < name.length; i++) {\n const code = name.charCodeAt(i)\n if (code < 0x20 || code === FILE_NAME_DEL) {\n return {\n code: 'invalid_file_name',\n message: 'fileName must not contain control characters (U+0000-U+001F or U+007F)',\n }\n }\n }\n // Path segments equal to `.` or `..` alone are illegal per B2 docs.\n // Embedded `..` within a segment (e.g. `a..b`) is fine, and a path\n // like `../foo` is fine because the SDK doesn't interpret it; only\n // bare `.` or `..` as a complete name is rejected.\n if (name === '.' || name === '..') {\n return {\n code: 'invalid_file_name',\n message: 'fileName cannot be exactly \".\" or \"..\"',\n }\n }\n // No leading or trailing slash, no `//`.\n if (name.startsWith('/') || name.endsWith('/') || name.includes('//')) {\n return {\n code: 'invalid_file_name',\n message: 'fileName cannot start with \"/\", end with \"/\", or contain \"//\"',\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// File-info / Bucket-info byte budget\n// ---------------------------------------------------------------------------\n\n/** Per-file fileInfo total size cap (sum of key+value bytes). */\nexport const FILE_INFO_TOTAL_MAX = 2048\n/** Per-key fileInfo value byte cap. */\nexport const FILE_INFO_VALUE_MAX = 2048\n/** Allowed key character set (case-insensitive). */\nconst FILE_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/\n\n/**\n * Validates a `fileInfo` record against B2's byte-budget and\n * key-shape rules.\n *\n * @param info - Caller-supplied fileInfo record.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-upload-file\n */\nexport function validateFileInfo(info: Record<string, string>): ValidationError | null {\n let total = 0\n for (const [key, value] of Object.entries(info)) {\n if (!FILE_INFO_KEY_REGEX.test(key)) {\n return {\n code: 'invalid_file_info',\n message: `fileInfo key \"${key}\" must match ^[a-zA-Z0-9_-]+$`,\n }\n }\n if (typeof value !== 'string') {\n return { code: 'invalid_file_info', message: `fileInfo value for \"${key}\" must be a string` }\n }\n const keyBytes = utf8Encoder.encode(key).byteLength\n const valueBytes = utf8Encoder.encode(value).byteLength\n if (valueBytes > FILE_INFO_VALUE_MAX) {\n return {\n code: 'invalid_file_info',\n message: `fileInfo value for \"${key}\" exceeds ${FILE_INFO_VALUE_MAX} bytes`,\n }\n }\n total += keyBytes + valueBytes\n }\n if (total > FILE_INFO_TOTAL_MAX) {\n return {\n code: 'invalid_file_info',\n message: `fileInfo total size ${total} bytes exceeds the ${FILE_INFO_TOTAL_MAX}-byte limit`,\n }\n }\n return null\n}\n\n/** Per-bucket bucketInfo total max keys. */\nexport const BUCKET_INFO_MAX_KEYS = 10\n/** Per-key bucketInfo value byte cap. */\nexport const BUCKET_INFO_VALUE_MAX = 2048\n/** Allowed key character set for bucketInfo. */\nconst BUCKET_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/\n\n/**\n * Validates a `bucketInfo` record against B2's per-bucket metadata rules.\n *\n * @param info - Caller-supplied bucketInfo record.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-create-bucket\n */\nexport function validateBucketInfo(info: Record<string, string>): ValidationError | null {\n const entries = Object.entries(info)\n if (entries.length > BUCKET_INFO_MAX_KEYS) {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo cannot have more than ${BUCKET_INFO_MAX_KEYS} keys (got ${entries.length})`,\n }\n }\n for (const [key, value] of entries) {\n if (!BUCKET_INFO_KEY_REGEX.test(key)) {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo key \"${key}\" must match ^[a-zA-Z0-9_-]+$`,\n }\n }\n if (typeof value !== 'string') {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo value for \"${key}\" must be a string`,\n }\n }\n const valueBytes = utf8Encoder.encode(value).byteLength\n if (valueBytes > BUCKET_INFO_VALUE_MAX) {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo value for \"${key}\" exceeds ${BUCKET_INFO_VALUE_MAX} bytes`,\n }\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// maxFileCount caps per endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * Per-endpoint upper bound on the `maxFileCount` (or `maxKeyCount` /\n * `maxPartCount`) request field. Sourced from the B2 endpoint docs.\n */\nexport const LIST_ENDPOINT_CAPS = {\n /** `b2_list_file_names.maxFileCount` cap. */\n b2_list_file_names: 10_000,\n /** `b2_list_file_versions.maxFileCount` cap. */\n b2_list_file_versions: 10_000,\n /** `b2_list_unfinished_large_files.maxFileCount` cap. */\n b2_list_unfinished_large_files: 100,\n /** `b2_list_keys.maxKeyCount` cap. */\n b2_list_keys: 10_000,\n /** `b2_list_parts.maxPartCount` cap. */\n b2_list_parts: 10_000,\n} as const\n\n/**\n * Validates a `maxFileCount` (or `maxKeyCount` / `maxPartCount`) value\n * against the per-endpoint cap documented by B2.\n *\n * @param requested - Caller-supplied count.\n * @param endpoint - Which list endpoint this applies to.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n */\nexport function validateMaxCount(\n requested: number | undefined,\n endpoint: keyof typeof LIST_ENDPOINT_CAPS,\n): ValidationError | null {\n if (requested === undefined) return null\n if (!Number.isInteger(requested) || requested < 1) {\n return {\n code: 'bad_request',\n message: `maxFileCount must be a positive integer (got ${requested})`,\n }\n }\n const cap = LIST_ENDPOINT_CAPS[endpoint]\n if (requested > cap) {\n return {\n code: 'bad_request',\n message: `maxFileCount ${requested} exceeds the ${endpoint} cap of ${cap}`,\n }\n }\n return null\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validation.js","names":[],"sources":["../../src/simulator/validation.ts"],"sourcesContent":["/**\n * B2 spec-compliance validation helpers for the simulator.\n *\n * Each function mirrors a documented B2 input limit (file name length,\n * bucket name regex, file-info byte budget, etc.). Validators return\n * `null` for valid input or a `{ code, message }` pair that the handler\n * converts into a synthetic `400`/`403` response with the right B2\n * error code.\n *\n * Keeping validation in one module makes the simulator's \"what does B2\n * reject?\" surface auditable in a single file. Doc references inline at\n * each rule so the spec source is immediate.\n *\n * @packageDocumentation\n */\n\nimport {\n BUCKET_NAME_MAX,\n BUCKET_NAME_MIN,\n BUCKET_NAME_RESERVED_PREFIX,\n FILE_NAME_MAX_BYTES,\n getB2FileNameByteLength,\n hasB2FileNameControlCharacter,\n hasValidB2BucketNameShape,\n isB2BucketNameIpv4Address,\n} from '../internal/b2-naming.ts'\nimport { utf8Encoder } from '../util/text-codec.ts'\n\n/** Shape returned by validation functions when input is rejected. */\nexport interface ValidationError {\n readonly code: string\n readonly message: string\n}\n\n// ---------------------------------------------------------------------------\n// Bucket name (`b2_create_bucket`, `b2_update_bucket`)\n// ---------------------------------------------------------------------------\n\nexport { BUCKET_NAME_MAX, BUCKET_NAME_MIN }\n\n/**\n * Validates a bucket name against B2's documented rules.\n *\n * @param name - Caller-supplied bucket name.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-create-bucket\n */\nexport function validateBucketName(name: string): ValidationError | null {\n if (typeof name !== 'string' || name.length < BUCKET_NAME_MIN || name.length > BUCKET_NAME_MAX) {\n return {\n code: 'invalid_bucket_name',\n message: `bucketName must be ${BUCKET_NAME_MIN}-${BUCKET_NAME_MAX} characters`,\n }\n }\n if (isB2BucketNameIpv4Address(name)) {\n return {\n code: 'invalid_bucket_name',\n message: 'bucketName must not be formatted as an IPv4 address',\n }\n }\n if (!hasValidB2BucketNameShape(name)) {\n return {\n code: 'invalid_bucket_name',\n message:\n 'bucketName must contain only letters, digits, hyphens, and periods; cannot start or end with punctuation or contain consecutive periods',\n }\n }\n if (name.startsWith(BUCKET_NAME_RESERVED_PREFIX)) {\n return {\n code: 'invalid_bucket_name',\n message: `bucketName cannot start with the reserved prefix \"${BUCKET_NAME_RESERVED_PREFIX}\"`,\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// File name (every upload / hide / copy entry point)\n// ---------------------------------------------------------------------------\n\nexport { FILE_NAME_MAX_BYTES }\n\n/**\n * Validates a file name against B2's documented rules. Mirrors the\n * server-side checks B2 runs on `b2_upload_file` / `b2_hide_file` /\n * `b2_start_large_file` / `b2_copy_file` filename inputs.\n *\n * @param name - Caller-supplied file name (raw, unencoded).\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-upload-file\n */\nexport function validateFileName(name: string): ValidationError | null {\n if (typeof name !== 'string' || name.length === 0) {\n return { code: 'invalid_file_name', message: 'fileName must be a non-empty string' }\n }\n const byteLength = getB2FileNameByteLength(name)\n if (byteLength === null) {\n return {\n code: 'invalid_file_name',\n message: 'fileName exceeds the 1024-byte UTF-8 limit',\n }\n }\n if (hasB2FileNameControlCharacter(name)) {\n return {\n code: 'invalid_file_name',\n message: 'fileName must not contain control characters (U+0000-U+001F or U+007F)',\n }\n }\n // Path segments equal to `.` or `..` alone are illegal per B2 docs.\n // Embedded `..` within a segment (e.g. `a..b`) is fine, and a path\n // like `../foo` is fine because the SDK doesn't interpret it; only\n // bare `.` or `..` as a complete name is rejected.\n if (name === '.' || name === '..') {\n return {\n code: 'invalid_file_name',\n message: 'fileName cannot be exactly \".\" or \"..\"',\n }\n }\n // No leading or trailing slash, no `//`.\n if (name.startsWith('/') || name.endsWith('/') || name.includes('//')) {\n return {\n code: 'invalid_file_name',\n message: 'fileName cannot start with \"/\", end with \"/\", or contain \"//\"',\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// File-info / Bucket-info byte budget\n// ---------------------------------------------------------------------------\n\n/** Per-file fileInfo total size cap (sum of key+value bytes). */\nexport const FILE_INFO_TOTAL_MAX = 2048\n/** Per-key fileInfo value byte cap. */\nexport const FILE_INFO_VALUE_MAX = 2048\n/** Allowed key character set (case-insensitive). */\nconst FILE_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/\n\n/**\n * Validates a `fileInfo` record against B2's byte-budget and\n * key-shape rules.\n *\n * @param info - Caller-supplied fileInfo record.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-upload-file\n */\nexport function validateFileInfo(info: Record<string, string>): ValidationError | null {\n let total = 0\n for (const [key, value] of Object.entries(info)) {\n if (!FILE_INFO_KEY_REGEX.test(key)) {\n return {\n code: 'invalid_file_info',\n message: `fileInfo key \"${key}\" must match ^[a-zA-Z0-9_-]+$`,\n }\n }\n if (typeof value !== 'string') {\n return { code: 'invalid_file_info', message: `fileInfo value for \"${key}\" must be a string` }\n }\n const keyBytes = utf8Encoder.encode(key).byteLength\n const valueBytes = utf8Encoder.encode(value).byteLength\n if (valueBytes > FILE_INFO_VALUE_MAX) {\n return {\n code: 'invalid_file_info',\n message: `fileInfo value for \"${key}\" exceeds ${FILE_INFO_VALUE_MAX} bytes`,\n }\n }\n total += keyBytes + valueBytes\n }\n if (total > FILE_INFO_TOTAL_MAX) {\n return {\n code: 'invalid_file_info',\n message: `fileInfo total size ${total} bytes exceeds the ${FILE_INFO_TOTAL_MAX}-byte limit`,\n }\n }\n return null\n}\n\n/** Per-bucket bucketInfo total max keys. */\nexport const BUCKET_INFO_MAX_KEYS = 10\n/** Per-key bucketInfo value byte cap. */\nexport const BUCKET_INFO_VALUE_MAX = 2048\n/** Allowed key character set for bucketInfo. */\nconst BUCKET_INFO_KEY_REGEX = /^[a-zA-Z0-9_-]+$/\n\n/**\n * Validates a `bucketInfo` record against B2's per-bucket metadata rules.\n *\n * @param info - Caller-supplied bucketInfo record.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n *\n * @see https://www.backblaze.com/apidocs/b2-create-bucket\n */\nexport function validateBucketInfo(info: Record<string, string>): ValidationError | null {\n const entries = Object.entries(info)\n if (entries.length > BUCKET_INFO_MAX_KEYS) {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo cannot have more than ${BUCKET_INFO_MAX_KEYS} keys (got ${entries.length})`,\n }\n }\n for (const [key, value] of entries) {\n if (!BUCKET_INFO_KEY_REGEX.test(key)) {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo key \"${key}\" must match ^[a-zA-Z0-9_-]+$`,\n }\n }\n if (typeof value !== 'string') {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo value for \"${key}\" must be a string`,\n }\n }\n const valueBytes = utf8Encoder.encode(value).byteLength\n if (valueBytes > BUCKET_INFO_VALUE_MAX) {\n return {\n code: 'invalid_bucket_info',\n message: `bucketInfo value for \"${key}\" exceeds ${BUCKET_INFO_VALUE_MAX} bytes`,\n }\n }\n }\n return null\n}\n\n// ---------------------------------------------------------------------------\n// maxFileCount caps per endpoint\n// ---------------------------------------------------------------------------\n\n/**\n * Per-endpoint upper bound on the `maxFileCount` (or `maxKeyCount` /\n * `maxPartCount`) request field. Sourced from the B2 endpoint docs.\n */\nexport const LIST_ENDPOINT_CAPS = {\n /** `b2_list_file_names.maxFileCount` cap. */\n b2_list_file_names: 10_000,\n /** `b2_list_file_versions.maxFileCount` cap. */\n b2_list_file_versions: 10_000,\n /** `b2_list_unfinished_large_files.maxFileCount` cap. */\n b2_list_unfinished_large_files: 100,\n /** `b2_list_keys.maxKeyCount` cap. */\n b2_list_keys: 10_000,\n /** `b2_list_parts.maxPartCount` cap. */\n b2_list_parts: 10_000,\n} as const\n\n/**\n * Validates a `maxFileCount` (or `maxKeyCount` / `maxPartCount`) value\n * against the per-endpoint cap documented by B2.\n *\n * @param requested - Caller-supplied count.\n * @param endpoint - Which list endpoint this applies to.\n *\n * @returns A `{ code, message }` pair on failure, or `null` when valid.\n */\nexport function validateMaxCount(\n requested: number | undefined,\n endpoint: keyof typeof LIST_ENDPOINT_CAPS,\n): ValidationError | null {\n if (requested === undefined) return null\n if (!Number.isInteger(requested) || requested < 1) {\n return {\n code: 'bad_request',\n message: `maxFileCount must be a positive integer (got ${requested})`,\n }\n }\n const cap = LIST_ENDPOINT_CAPS[endpoint]\n if (requested > cap) {\n return {\n code: 'bad_request',\n message: `maxFileCount ${requested} exceeds the ${endpoint} cap of ${cap}`,\n }\n }\n return null\n}\n"],"mappings":"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"}
@@ -1,26 +1,76 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- async function collectStream(stream) {
4
- const reader = stream.getReader();
5
- try {
6
- const chunks = [];
7
- let total = 0;
8
- while (true) {
9
- const { done, value } = await reader.read();
10
- if (done) break;
11
- chunks.push(value);
12
- total += value.byteLength;
13
- }
14
- const result = new Uint8Array(total);
15
- let offset = 0;
16
- for (const chunk of chunks) {
17
- result.set(chunk, offset);
18
- offset += chunk.byteLength;
19
- }
20
- return result;
21
- } finally {
22
- reader.releaseLock();
23
- }
1
+ //#region src/streams/collect.ts
2
+ /**
3
+ * Drain a `ReadableStream<Uint8Array>` into a single contiguous
4
+ * `Uint8Array`. Releases the reader lock on both the happy and error
5
+ * paths so the underlying stream can propagate close / error events to
6
+ * the upstream producer.
7
+ *
8
+ * Used by both `createParallelDownloadStream` (per-range fetch) and
9
+ * `StreamSource.toArrayBuffer` (whole-source materialisation). The two
10
+ * code paths previously hand-rolled the same accumulate-then-concat
11
+ * loop; consolidating here removes ~25 duplicated lines and a class of
12
+ * lock-leak bugs.
13
+ *
14
+ * @param stream - Readable stream to consume. Will be fully drained.
15
+ * @param options - Optional abort signal used to stop a pending read.
16
+ *
17
+ * @returns A new `Uint8Array` containing every byte the stream produced.
18
+ */
19
+ async function collectStream(stream, options = {}) {
20
+ const reader = stream.getReader();
21
+ try {
22
+ const chunks = [];
23
+ let total = 0;
24
+ while (true) {
25
+ const { done, value } = await readStreamChunkWithSignal(reader, options.signal);
26
+ if (done) break;
27
+ chunks.push(value);
28
+ total += value.byteLength;
29
+ }
30
+ const result = new Uint8Array(total);
31
+ let offset = 0;
32
+ for (const chunk of chunks) {
33
+ result.set(chunk, offset);
34
+ offset += chunk.byteLength;
35
+ }
36
+ return result;
37
+ } finally {
38
+ reader.releaseLock();
39
+ }
24
40
  }
41
+ /**
42
+ * Reads one chunk and rejects when the supplied signal aborts.
43
+ * @param reader - Stream reader to read from.
44
+ * @param signal - Optional abort signal that cancels the read.
45
+ *
46
+ * @returns The next stream read result.
47
+ *
48
+ * @internal
49
+ */
50
+ async function readStreamChunkWithSignal(reader, signal) {
51
+ if (signal === void 0) return reader.read();
52
+ signal.throwIfAborted();
53
+ let removeAbortListener;
54
+ const abortPromise = new Promise((_, reject) => {
55
+ const onAbort = () => {
56
+ const reason = abortReason(signal);
57
+ reject(reason);
58
+ reader.cancel(reason).catch(() => {});
59
+ };
60
+ signal.addEventListener("abort", onAbort, { once: true });
61
+ removeAbortListener = () => signal.removeEventListener("abort", onAbort);
62
+ });
63
+ try {
64
+ return await Promise.race([reader.read(), abortPromise]);
65
+ } finally {
66
+ removeAbortListener?.();
67
+ }
68
+ }
69
+ function abortReason(signal) {
70
+ return signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
71
+ }
72
+ //#endregion
25
73
  exports.collectStream = collectStream;
26
- //# sourceMappingURL=collect.cjs.map
74
+ exports.readStreamChunkWithSignal = readStreamChunkWithSignal;
75
+
76
+ //# sourceMappingURL=collect.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect.cjs","sources":["../../src/streams/collect.ts"],"sourcesContent":["/**\n * Drain a `ReadableStream<Uint8Array>` into a single contiguous\n * `Uint8Array`. Releases the reader lock on both the happy and error\n * paths so the underlying stream can propagate close / error events to\n * the upstream producer.\n *\n * Used by both `createParallelDownloadStream` (per-range fetch) and\n * `StreamSource.toArrayBuffer` (whole-source materialisation). The two\n * code paths previously hand-rolled the same accumulate-then-concat\n * loop; consolidating here removes ~25 duplicated lines and a class of\n * lock-leak bugs.\n *\n * @param stream - Readable stream to consume. Will be fully drained.\n *\n * @returns A new `Uint8Array` containing every byte the stream produced.\n */\nexport async function collectStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array> {\n const reader = stream.getReader()\n try {\n const chunks: Uint8Array[] = []\n let total = 0\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n chunks.push(value)\n total += value.byteLength\n }\n const result = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n result.set(chunk, offset)\n offset += chunk.byteLength\n }\n return result\n } finally {\n // Releasing here lets the underlying stream propagate close / error\n // events to any upstream producer (e.g. a Node `Readable`) even on\n // the error path where a mid-read throw left the reader locked.\n reader.releaseLock()\n }\n}\n"],"names":[],"mappings":";;AAgBA,eAAsB,cAAc,QAAyD;AAC3F,QAAM,SAAS,OAAO,UAAA;AACtB,MAAI;AACF,UAAM,SAAuB,CAAA;AAC7B,QAAI,QAAQ;AACZ,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAA,IAAU,MAAM,OAAO,KAAA;AACrC,UAAI,KAAM;AACV,aAAO,KAAK,KAAK;AACjB,eAAS,MAAM;AAAA,IACjB;AACA,UAAM,SAAS,IAAI,WAAW,KAAK;AACnC,QAAI,SAAS;AACb,eAAW,SAAS,QAAQ;AAC1B,aAAO,IAAI,OAAO,MAAM;AACxB,gBAAU,MAAM;AAAA,IAClB;AACA,WAAO;AAAA,EACT,UAAA;AAIE,WAAO,YAAA;AAAA,EACT;AACF;;"}
1
+ {"version":3,"file":"collect.cjs","names":[],"sources":["../../src/streams/collect.ts"],"sourcesContent":["/**\n * Drain a `ReadableStream<Uint8Array>` into a single contiguous\n * `Uint8Array`. Releases the reader lock on both the happy and error\n * paths so the underlying stream can propagate close / error events to\n * the upstream producer.\n *\n * Used by both `createParallelDownloadStream` (per-range fetch) and\n * `StreamSource.toArrayBuffer` (whole-source materialisation). The two\n * code paths previously hand-rolled the same accumulate-then-concat\n * loop; consolidating here removes ~25 duplicated lines and a class of\n * lock-leak bugs.\n *\n * @param stream - Readable stream to consume. Will be fully drained.\n * @param options - Optional abort signal used to stop a pending read.\n *\n * @returns A new `Uint8Array` containing every byte the stream produced.\n */\nexport async function collectStream(\n stream: ReadableStream<Uint8Array>,\n options: { readonly signal?: AbortSignal } = {},\n): Promise<Uint8Array> {\n const reader = stream.getReader()\n try {\n const chunks: Uint8Array[] = []\n let total = 0\n while (true) {\n const { done, value } = await readStreamChunkWithSignal(reader, options.signal)\n if (done) break\n chunks.push(value)\n total += value.byteLength\n }\n const result = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n result.set(chunk, offset)\n offset += chunk.byteLength\n }\n return result\n } finally {\n // Releasing here lets the underlying stream propagate close / error\n // events to any upstream producer (e.g. a Node `Readable`) even on\n // the error path where a mid-read throw left the reader locked.\n reader.releaseLock()\n }\n}\n\n/**\n * Reads one chunk and rejects when the supplied signal aborts.\n * @param reader - Stream reader to read from.\n * @param signal - Optional abort signal that cancels the read.\n *\n * @returns The next stream read result.\n *\n * @internal\n */\nexport async function readStreamChunkWithSignal<T>(\n reader: ReadableStreamDefaultReader<T>,\n signal: AbortSignal | undefined,\n): Promise<ReadableStreamReadResult<T>> {\n if (signal === undefined) return reader.read()\n signal.throwIfAborted()\n\n let removeAbortListener: (() => void) | undefined\n const abortPromise = new Promise<never>((_, reject) => {\n const onAbort = (): void => {\n const reason = abortReason(signal)\n reject(reason)\n void reader.cancel(reason).catch(() => {})\n }\n signal.addEventListener('abort', onAbort, { once: true })\n removeAbortListener = () => signal.removeEventListener('abort', onAbort)\n })\n\n try {\n return await Promise.race([reader.read(), abortPromise])\n } finally {\n removeAbortListener?.()\n }\n}\n\nfunction abortReason(signal: AbortSignal): unknown {\n return signal.reason ?? new DOMException('The operation was aborted.', 'AbortError')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,eAAsB,cACpB,QACA,UAA6C,CAAC,GACzB;CACrB,MAAM,SAAS,OAAO,UAAU;CAChC,IAAI;EACF,MAAM,SAAuB,CAAC;EAC9B,IAAI,QAAQ;EACZ,OAAO,MAAM;GACX,MAAM,EAAE,MAAM,UAAU,MAAM,0BAA0B,QAAQ,QAAQ,MAAM;GAC9E,IAAI,MAAM;GACV,OAAO,KAAK,KAAK;GACjB,SAAS,MAAM;EACjB;EACA,MAAM,SAAS,IAAI,WAAW,KAAK;EACnC,IAAI,SAAS;EACb,KAAK,MAAM,SAAS,QAAQ;GAC1B,OAAO,IAAI,OAAO,MAAM;GACxB,UAAU,MAAM;EAClB;EACA,OAAO;CACT,UAAU;EAIR,OAAO,YAAY;CACrB;AACF;;;;;;;;;;AAWA,eAAsB,0BACpB,QACA,QACsC;CACtC,IAAI,WAAW,KAAA,GAAW,OAAO,OAAO,KAAK;CAC7C,OAAO,eAAe;CAEtB,IAAI;CACJ,MAAM,eAAe,IAAI,SAAgB,GAAG,WAAW;EACrD,MAAM,gBAAsB;GAC1B,MAAM,SAAS,YAAY,MAAM;GACjC,OAAO,MAAM;GACb,OAAY,OAAO,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;EAC3C;EACA,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACxD,4BAA4B,OAAO,oBAAoB,SAAS,OAAO;CACzE,CAAC;CAED,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CAAC,OAAO,KAAK,GAAG,YAAY,CAAC;CACzD,UAAU;EACR,sBAAsB;CACxB;AACF;AAEA,SAAS,YAAY,QAA8B;CACjD,OAAO,OAAO,UAAU,IAAI,aAAa,8BAA8B,YAAY;AACrF"}
@@ -11,8 +11,21 @@
11
11
  * lock-leak bugs.
12
12
  *
13
13
  * @param stream - Readable stream to consume. Will be fully drained.
14
+ * @param options - Optional abort signal used to stop a pending read.
14
15
  *
15
16
  * @returns A new `Uint8Array` containing every byte the stream produced.
16
17
  */
17
- export declare function collectStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array>;
18
+ export declare function collectStream(stream: ReadableStream<Uint8Array>, options?: {
19
+ readonly signal?: AbortSignal;
20
+ }): Promise<Uint8Array>;
21
+ /**
22
+ * Reads one chunk and rejects when the supplied signal aborts.
23
+ * @param reader - Stream reader to read from.
24
+ * @param signal - Optional abort signal that cancels the read.
25
+ *
26
+ * @returns The next stream read result.
27
+ *
28
+ * @internal
29
+ */
30
+ export declare function readStreamChunkWithSignal<T>(reader: ReadableStreamDefaultReader<T>, signal: AbortSignal | undefined): Promise<ReadableStreamReadResult<T>>;
18
31
  //# sourceMappingURL=collect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/streams/collect.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAwB3F"}
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/streams/collect.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAC9C,OAAO,CAAC,UAAU,CAAC,CAwBrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,EACtC,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAoBtC"}
@@ -11,8 +11,21 @@
11
11
  * lock-leak bugs.
12
12
  *
13
13
  * @param stream - Readable stream to consume. Will be fully drained.
14
+ * @param options - Optional abort signal used to stop a pending read.
14
15
  *
15
16
  * @returns A new `Uint8Array` containing every byte the stream produced.
16
17
  */
17
- export declare function collectStream(stream: ReadableStream<Uint8Array>): Promise<Uint8Array>;
18
+ export declare function collectStream(stream: ReadableStream<Uint8Array>, options?: {
19
+ readonly signal?: AbortSignal;
20
+ }): Promise<Uint8Array>;
21
+ /**
22
+ * Reads one chunk and rejects when the supplied signal aborts.
23
+ * @param reader - Stream reader to read from.
24
+ * @param signal - Optional abort signal that cancels the read.
25
+ *
26
+ * @returns The next stream read result.
27
+ *
28
+ * @internal
29
+ */
30
+ export declare function readStreamChunkWithSignal<T>(reader: ReadableStreamDefaultReader<T>, signal: AbortSignal | undefined): Promise<ReadableStreamReadResult<T>>;
18
31
  //# sourceMappingURL=collect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/streams/collect.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAwB3F"}
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/streams/collect.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAC9C,OAAO,CAAC,UAAU,CAAC,CAwBrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,MAAM,EAAE,2BAA2B,CAAC,CAAC,CAAC,EACtC,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAoBtC"}