@milaboratories/pl-drivers 1.9.0 → 1.10.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 (334) hide show
  1. package/dist/clients/constructors.cjs +45 -0
  2. package/dist/clients/constructors.cjs.map +1 -0
  3. package/dist/clients/constructors.d.ts +0 -1
  4. package/dist/clients/constructors.js +39 -0
  5. package/dist/clients/constructors.js.map +1 -0
  6. package/dist/clients/download.cjs +149 -0
  7. package/dist/clients/download.cjs.map +1 -0
  8. package/dist/clients/download.d.ts +0 -1
  9. package/dist/clients/download.js +121 -0
  10. package/dist/clients/download.js.map +1 -0
  11. package/dist/clients/logs.cjs +44 -0
  12. package/dist/clients/logs.cjs.map +1 -0
  13. package/dist/clients/logs.d.ts +0 -1
  14. package/dist/clients/logs.js +42 -0
  15. package/dist/clients/logs.js.map +1 -0
  16. package/dist/clients/ls_api.cjs +23 -0
  17. package/dist/clients/ls_api.cjs.map +1 -0
  18. package/dist/clients/ls_api.d.ts +0 -1
  19. package/dist/clients/ls_api.js +21 -0
  20. package/dist/clients/ls_api.js.map +1 -0
  21. package/dist/clients/progress.cjs +58 -0
  22. package/dist/clients/progress.cjs.map +1 -0
  23. package/dist/clients/progress.d.ts +1 -3
  24. package/dist/clients/progress.js +56 -0
  25. package/dist/clients/progress.js.map +1 -0
  26. package/dist/clients/upload.cjs +209 -0
  27. package/dist/clients/upload.cjs.map +1 -0
  28. package/dist/clients/upload.d.ts +8 -4
  29. package/dist/clients/upload.js +183 -0
  30. package/dist/clients/upload.js.map +1 -0
  31. package/dist/drivers/download_blob/blob_key.cjs +34 -0
  32. package/dist/drivers/download_blob/blob_key.cjs.map +1 -0
  33. package/dist/drivers/download_blob/blob_key.d.ts +0 -1
  34. package/dist/drivers/download_blob/blob_key.js +12 -0
  35. package/dist/drivers/download_blob/blob_key.js.map +1 -0
  36. package/dist/drivers/download_blob/download_blob.cjs +442 -0
  37. package/dist/drivers/download_blob/download_blob.cjs.map +1 -0
  38. package/dist/drivers/download_blob/download_blob.d.ts +0 -1
  39. package/dist/drivers/download_blob/download_blob.js +417 -0
  40. package/dist/drivers/download_blob/download_blob.js.map +1 -0
  41. package/dist/drivers/download_blob/download_blob_task.cjs +170 -0
  42. package/dist/drivers/download_blob/download_blob_task.cjs.map +1 -0
  43. package/dist/drivers/download_blob/download_blob_task.d.ts +0 -1
  44. package/dist/drivers/download_blob/download_blob_task.js +146 -0
  45. package/dist/drivers/download_blob/download_blob_task.js.map +1 -0
  46. package/dist/drivers/download_blob/sparse_cache/cache.cjs +202 -0
  47. package/dist/drivers/download_blob/sparse_cache/cache.cjs.map +1 -0
  48. package/dist/drivers/download_blob/sparse_cache/cache.d.ts +0 -1
  49. package/dist/drivers/download_blob/sparse_cache/cache.js +197 -0
  50. package/dist/drivers/download_blob/sparse_cache/cache.js.map +1 -0
  51. package/dist/drivers/download_blob/sparse_cache/file.cjs +61 -0
  52. package/dist/drivers/download_blob/sparse_cache/file.cjs.map +1 -0
  53. package/dist/drivers/download_blob/sparse_cache/file.d.ts +0 -1
  54. package/dist/drivers/download_blob/sparse_cache/file.js +39 -0
  55. package/dist/drivers/download_blob/sparse_cache/file.js.map +1 -0
  56. package/dist/drivers/download_blob/sparse_cache/ranges.cjs +104 -0
  57. package/dist/drivers/download_blob/sparse_cache/ranges.cjs.map +1 -0
  58. package/dist/drivers/download_blob/sparse_cache/ranges.d.ts +0 -1
  59. package/dist/drivers/download_blob/sparse_cache/ranges.js +76 -0
  60. package/dist/drivers/download_blob/sparse_cache/ranges.js.map +1 -0
  61. package/dist/drivers/download_blob_url/driver.cjs +169 -0
  62. package/dist/drivers/download_blob_url/driver.cjs.map +1 -0
  63. package/dist/drivers/download_blob_url/driver.d.ts +0 -1
  64. package/dist/drivers/download_blob_url/driver.js +148 -0
  65. package/dist/drivers/download_blob_url/driver.js.map +1 -0
  66. package/dist/drivers/download_blob_url/driver_id.cjs +9 -0
  67. package/dist/drivers/download_blob_url/driver_id.cjs.map +1 -0
  68. package/dist/drivers/download_blob_url/driver_id.d.ts +0 -1
  69. package/dist/drivers/download_blob_url/driver_id.js +7 -0
  70. package/dist/drivers/download_blob_url/driver_id.js.map +1 -0
  71. package/dist/drivers/download_blob_url/snapshot.cjs +18 -0
  72. package/dist/drivers/download_blob_url/snapshot.cjs.map +1 -0
  73. package/dist/drivers/download_blob_url/snapshot.d.ts +2 -3
  74. package/dist/drivers/download_blob_url/snapshot.js +15 -0
  75. package/dist/drivers/download_blob_url/snapshot.js.map +1 -0
  76. package/dist/drivers/download_blob_url/task.cjs +209 -0
  77. package/dist/drivers/download_blob_url/task.cjs.map +1 -0
  78. package/dist/drivers/download_blob_url/task.d.ts +0 -1
  79. package/dist/drivers/download_blob_url/task.js +184 -0
  80. package/dist/drivers/download_blob_url/task.js.map +1 -0
  81. package/dist/drivers/download_blob_url/url.d.ts +1 -1
  82. package/dist/drivers/download_url/driver.cjs +149 -0
  83. package/dist/drivers/download_url/driver.cjs.map +1 -0
  84. package/dist/drivers/download_url/driver.d.ts +0 -1
  85. package/dist/drivers/download_url/driver.js +128 -0
  86. package/dist/drivers/download_url/driver.js.map +1 -0
  87. package/dist/drivers/download_url/task.cjs +150 -0
  88. package/dist/drivers/download_url/task.cjs.map +1 -0
  89. package/dist/drivers/download_url/task.d.ts +0 -1
  90. package/dist/drivers/download_url/task.js +124 -0
  91. package/dist/drivers/download_url/task.js.map +1 -0
  92. package/dist/drivers/helpers/download_local_handle.cjs +26 -0
  93. package/dist/drivers/helpers/download_local_handle.cjs.map +1 -0
  94. package/dist/drivers/helpers/download_local_handle.d.ts +0 -1
  95. package/dist/drivers/helpers/download_local_handle.js +22 -0
  96. package/dist/drivers/helpers/download_local_handle.js.map +1 -0
  97. package/dist/drivers/helpers/download_remote_handle.cjs +36 -0
  98. package/dist/drivers/helpers/download_remote_handle.cjs.map +1 -0
  99. package/dist/drivers/helpers/download_remote_handle.d.ts +0 -1
  100. package/dist/drivers/helpers/download_remote_handle.js +32 -0
  101. package/dist/drivers/helpers/download_remote_handle.js.map +1 -0
  102. package/dist/drivers/helpers/files_cache.cjs +68 -0
  103. package/dist/drivers/helpers/files_cache.cjs.map +1 -0
  104. package/dist/drivers/helpers/files_cache.d.ts +0 -1
  105. package/dist/drivers/helpers/files_cache.js +66 -0
  106. package/dist/drivers/helpers/files_cache.js.map +1 -0
  107. package/dist/drivers/helpers/helpers.cjs +34 -0
  108. package/dist/drivers/helpers/helpers.cjs.map +1 -0
  109. package/dist/drivers/helpers/helpers.d.ts +0 -1
  110. package/dist/drivers/helpers/helpers.js +31 -0
  111. package/dist/drivers/helpers/helpers.js.map +1 -0
  112. package/dist/drivers/helpers/logs_handle.cjs +50 -0
  113. package/dist/drivers/helpers/logs_handle.cjs.map +1 -0
  114. package/dist/drivers/helpers/logs_handle.d.ts +0 -1
  115. package/dist/drivers/helpers/logs_handle.js +43 -0
  116. package/dist/drivers/helpers/logs_handle.js.map +1 -0
  117. package/dist/drivers/helpers/ls_remote_import_handle.cjs +34 -0
  118. package/dist/drivers/helpers/ls_remote_import_handle.cjs.map +1 -0
  119. package/dist/drivers/helpers/ls_remote_import_handle.d.ts +0 -1
  120. package/dist/drivers/helpers/ls_remote_import_handle.js +29 -0
  121. package/dist/drivers/helpers/ls_remote_import_handle.js.map +1 -0
  122. package/dist/drivers/helpers/ls_storage_entry.cjs +64 -0
  123. package/dist/drivers/helpers/ls_storage_entry.cjs.map +1 -0
  124. package/dist/drivers/helpers/ls_storage_entry.d.ts +0 -1
  125. package/dist/drivers/helpers/ls_storage_entry.js +58 -0
  126. package/dist/drivers/helpers/ls_storage_entry.js.map +1 -0
  127. package/dist/drivers/helpers/polling_ops.d.ts +0 -1
  128. package/dist/drivers/helpers/read_file.cjs +54 -0
  129. package/dist/drivers/helpers/read_file.cjs.map +1 -0
  130. package/dist/drivers/helpers/read_file.d.ts +0 -1
  131. package/dist/drivers/helpers/read_file.js +33 -0
  132. package/dist/drivers/helpers/read_file.js.map +1 -0
  133. package/dist/drivers/helpers/test_helpers.d.ts +0 -1
  134. package/dist/drivers/logs.cjs +118 -0
  135. package/dist/drivers/logs.cjs.map +1 -0
  136. package/dist/drivers/logs.d.ts +0 -1
  137. package/dist/drivers/logs.js +116 -0
  138. package/dist/drivers/logs.js.map +1 -0
  139. package/dist/drivers/logs_stream.cjs +238 -0
  140. package/dist/drivers/logs_stream.cjs.map +1 -0
  141. package/dist/drivers/logs_stream.d.ts +0 -1
  142. package/dist/drivers/logs_stream.js +236 -0
  143. package/dist/drivers/logs_stream.js.map +1 -0
  144. package/dist/drivers/ls.cjs +236 -0
  145. package/dist/drivers/ls.cjs.map +1 -0
  146. package/dist/drivers/ls.d.ts +0 -1
  147. package/dist/drivers/ls.js +214 -0
  148. package/dist/drivers/ls.js.map +1 -0
  149. package/dist/drivers/types.cjs +72 -0
  150. package/dist/drivers/types.cjs.map +1 -0
  151. package/dist/drivers/types.d.ts +4 -5
  152. package/dist/drivers/types.js +64 -0
  153. package/dist/drivers/types.js.map +1 -0
  154. package/dist/drivers/upload.cjs +154 -0
  155. package/dist/drivers/upload.cjs.map +1 -0
  156. package/dist/drivers/upload.d.ts +0 -1
  157. package/dist/drivers/upload.js +151 -0
  158. package/dist/drivers/upload.js.map +1 -0
  159. package/dist/drivers/upload_task.cjs +297 -0
  160. package/dist/drivers/upload_task.cjs.map +1 -0
  161. package/dist/drivers/upload_task.d.ts +2 -3
  162. package/dist/drivers/upload_task.js +289 -0
  163. package/dist/drivers/upload_task.js.map +1 -0
  164. package/dist/drivers/urls/url.cjs +59 -0
  165. package/dist/drivers/urls/url.cjs.map +1 -0
  166. package/dist/drivers/urls/url.d.ts +0 -1
  167. package/dist/drivers/urls/url.js +54 -0
  168. package/dist/drivers/urls/url.js.map +1 -0
  169. package/dist/drivers/virtual_storages.cjs +53 -0
  170. package/dist/drivers/virtual_storages.cjs.map +1 -0
  171. package/dist/drivers/virtual_storages.d.ts +0 -1
  172. package/dist/drivers/virtual_storages.js +51 -0
  173. package/dist/drivers/virtual_storages.js.map +1 -0
  174. package/dist/helpers/download.cjs +63 -0
  175. package/dist/helpers/download.cjs.map +1 -0
  176. package/dist/helpers/download.d.ts +0 -1
  177. package/dist/helpers/download.js +60 -0
  178. package/dist/helpers/download.js.map +1 -0
  179. package/dist/helpers/validate.cjs +12 -0
  180. package/dist/helpers/validate.cjs.map +1 -0
  181. package/dist/helpers/validate.d.ts +0 -1
  182. package/dist/helpers/validate.js +10 -0
  183. package/dist/helpers/validate.js.map +1 -0
  184. package/dist/index.cjs +73 -0
  185. package/dist/index.cjs.map +1 -0
  186. package/dist/index.d.ts +0 -1
  187. package/dist/index.js +19 -2
  188. package/dist/index.js.map +1 -1
  189. package/dist/proto/github.com/googleapis/googleapis/google/rpc/status.d.ts +0 -1
  190. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.cjs +261 -0
  191. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.cjs.map +1 -0
  192. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.cjs +31 -0
  193. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.cjs.map +1 -0
  194. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.d.ts +3 -5
  195. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.js +29 -0
  196. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.js.map +1 -0
  197. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts +0 -1
  198. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.js +256 -0
  199. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.js.map +1 -0
  200. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.cjs +301 -0
  201. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.cjs.map +1 -0
  202. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.cjs +34 -0
  203. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.cjs.map +1 -0
  204. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.d.ts +3 -5
  205. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.js +32 -0
  206. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.js.map +1 -0
  207. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts +0 -1
  208. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.js +296 -0
  209. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.js.map +1 -0
  210. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.cjs +410 -0
  211. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.cjs.map +1 -0
  212. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.cjs +38 -0
  213. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.cjs.map +1 -0
  214. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.d.ts +3 -5
  215. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.js +36 -0
  216. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.js.map +1 -0
  217. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts +0 -1
  218. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.js +403 -0
  219. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.js.map +1 -0
  220. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.cjs +486 -0
  221. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.cjs.map +1 -0
  222. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.cjs +86 -0
  223. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.cjs.map +1 -0
  224. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.d.ts +3 -5
  225. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.js +84 -0
  226. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.js.map +1 -0
  227. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts +0 -1
  228. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.js +478 -0
  229. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.js.map +1 -0
  230. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.cjs +758 -0
  231. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.cjs.map +1 -0
  232. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.cjs +71 -0
  233. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.cjs.map +1 -0
  234. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.d.ts +3 -5
  235. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.js +69 -0
  236. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.js.map +1 -0
  237. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.d.ts +36 -1
  238. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.js +747 -0
  239. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.js.map +1 -0
  240. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts +3 -5
  241. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +0 -1
  242. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/api_types.d.ts +0 -1
  243. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/base_types.d.ts +0 -1
  244. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/import.d.ts +0 -1
  245. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/resource_types.d.ts +0 -1
  246. package/dist/proto/google/api/http.d.ts +0 -1
  247. package/dist/proto/google/protobuf/any.d.ts +0 -1
  248. package/dist/proto/google/protobuf/descriptor.d.ts +0 -1
  249. package/dist/proto/google/protobuf/duration.cjs +105 -0
  250. package/dist/proto/google/protobuf/duration.cjs.map +1 -0
  251. package/dist/proto/google/protobuf/duration.d.ts +0 -1
  252. package/dist/proto/google/protobuf/duration.js +103 -0
  253. package/dist/proto/google/protobuf/duration.js.map +1 -0
  254. package/dist/proto/google/protobuf/empty.d.ts +0 -1
  255. package/dist/proto/google/protobuf/struct.d.ts +0 -1
  256. package/dist/proto/google/protobuf/timestamp.cjs +133 -0
  257. package/dist/proto/google/protobuf/timestamp.cjs.map +1 -0
  258. package/dist/proto/google/protobuf/timestamp.d.ts +0 -1
  259. package/dist/proto/google/protobuf/timestamp.js +131 -0
  260. package/dist/proto/google/protobuf/timestamp.js.map +1 -0
  261. package/dist/proto/google/protobuf/wrappers.d.ts +0 -1
  262. package/dist/test_env.d.ts +0 -1
  263. package/package.json +17 -15
  264. package/src/clients/upload.ts +38 -14
  265. package/src/drivers/upload_task.ts +10 -3
  266. package/src/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.ts +69 -1
  267. package/dist/clients/constructors.d.ts.map +0 -1
  268. package/dist/clients/download.d.ts.map +0 -1
  269. package/dist/clients/logs.d.ts.map +0 -1
  270. package/dist/clients/ls_api.d.ts.map +0 -1
  271. package/dist/clients/progress.d.ts.map +0 -1
  272. package/dist/clients/upload.d.ts.map +0 -1
  273. package/dist/drivers/download_blob/blob_key.d.ts.map +0 -1
  274. package/dist/drivers/download_blob/download_blob.d.ts.map +0 -1
  275. package/dist/drivers/download_blob/download_blob_task.d.ts.map +0 -1
  276. package/dist/drivers/download_blob/sparse_cache/cache.d.ts.map +0 -1
  277. package/dist/drivers/download_blob/sparse_cache/file.d.ts.map +0 -1
  278. package/dist/drivers/download_blob/sparse_cache/ranges.d.ts.map +0 -1
  279. package/dist/drivers/download_blob_url/driver.d.ts.map +0 -1
  280. package/dist/drivers/download_blob_url/driver_id.d.ts.map +0 -1
  281. package/dist/drivers/download_blob_url/snapshot.d.ts.map +0 -1
  282. package/dist/drivers/download_blob_url/task.d.ts.map +0 -1
  283. package/dist/drivers/download_blob_url/url.d.ts.map +0 -1
  284. package/dist/drivers/download_url/driver.d.ts.map +0 -1
  285. package/dist/drivers/download_url/task.d.ts.map +0 -1
  286. package/dist/drivers/helpers/download_local_handle.d.ts.map +0 -1
  287. package/dist/drivers/helpers/download_remote_handle.d.ts.map +0 -1
  288. package/dist/drivers/helpers/files_cache.d.ts.map +0 -1
  289. package/dist/drivers/helpers/helpers.d.ts.map +0 -1
  290. package/dist/drivers/helpers/logs_handle.d.ts.map +0 -1
  291. package/dist/drivers/helpers/ls_remote_import_handle.d.ts.map +0 -1
  292. package/dist/drivers/helpers/ls_storage_entry.d.ts.map +0 -1
  293. package/dist/drivers/helpers/polling_ops.d.ts.map +0 -1
  294. package/dist/drivers/helpers/read_file.d.ts.map +0 -1
  295. package/dist/drivers/helpers/test_helpers.d.ts.map +0 -1
  296. package/dist/drivers/logs.d.ts.map +0 -1
  297. package/dist/drivers/logs_stream.d.ts.map +0 -1
  298. package/dist/drivers/ls.d.ts.map +0 -1
  299. package/dist/drivers/types.d.ts.map +0 -1
  300. package/dist/drivers/upload.d.ts.map +0 -1
  301. package/dist/drivers/upload_task.d.ts.map +0 -1
  302. package/dist/drivers/urls/url.d.ts.map +0 -1
  303. package/dist/drivers/virtual_storages.d.ts.map +0 -1
  304. package/dist/helpers/download.d.ts.map +0 -1
  305. package/dist/helpers/validate.d.ts.map +0 -1
  306. package/dist/index.d.ts.map +0 -1
  307. package/dist/index.mjs +0 -4892
  308. package/dist/index.mjs.map +0 -1
  309. package/dist/proto/github.com/googleapis/googleapis/google/rpc/status.d.ts.map +0 -1
  310. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.client.d.ts.map +0 -1
  311. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/downloadapi/protocol.d.ts.map +0 -1
  312. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.client.d.ts.map +0 -1
  313. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/lsapi/protocol.d.ts.map +0 -1
  314. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.client.d.ts.map +0 -1
  315. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/progressapi/protocol.d.ts.map +0 -1
  316. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.client.d.ts.map +0 -1
  317. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol.d.ts.map +0 -1
  318. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.client.d.ts.map +0 -1
  319. package/dist/proto/github.com/milaboratory/pl/controllers/shared/grpc/uploadapi/protocol.d.ts.map +0 -1
  320. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts.map +0 -1
  321. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +0 -1
  322. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/api_types.d.ts.map +0 -1
  323. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/base_types.d.ts.map +0 -1
  324. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/import.d.ts.map +0 -1
  325. package/dist/proto/github.com/milaboratory/pl/plapi/plapiproto/resource_types.d.ts.map +0 -1
  326. package/dist/proto/google/api/http.d.ts.map +0 -1
  327. package/dist/proto/google/protobuf/any.d.ts.map +0 -1
  328. package/dist/proto/google/protobuf/descriptor.d.ts.map +0 -1
  329. package/dist/proto/google/protobuf/duration.d.ts.map +0 -1
  330. package/dist/proto/google/protobuf/empty.d.ts.map +0 -1
  331. package/dist/proto/google/protobuf/struct.d.ts.map +0 -1
  332. package/dist/proto/google/protobuf/timestamp.d.ts.map +0 -1
  333. package/dist/proto/google/protobuf/wrappers.d.ts.map +0 -1
  334. package/dist/test_env.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download_blob_task.cjs","sources":["../../../src/drivers/download_blob/download_blob_task.ts"],"sourcesContent":["import type { Watcher } from '@milaboratories/computable';\nimport { ChangeSource } from '@milaboratories/computable';\nimport type { LocalBlobHandle, LocalBlobHandleAndSize } from '@milaboratories/pl-model-common';\nimport type { ResourceSnapshot } from '@milaboratories/pl-tree';\nimport type {\n ValueOrError,\n MiLogger,\n} from '@milaboratories/ts-helpers';\nimport {\n ensureDirExists,\n fileExists,\n createPathAtomically,\n CallersCounter,\n} from '@milaboratories/ts-helpers';\nimport fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Writable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport { UnknownStorageError, WrongLocalFileUrl } from '../../clients/download';\nimport { NetworkError400 } from '../../helpers/download';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\n\n/** Downloads a blob and holds callers and watchers for the blob. */\nexport class DownloadBlobTask {\n readonly change = new ChangeSource();\n private readonly signalCtl = new AbortController();\n public readonly counter = new CallersCounter();\n private error: unknown | undefined;\n private done = false;\n public size = 0;\n private state: DownloadState = {};\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n readonly rInfo: ResourceSnapshot,\n private readonly handle: LocalBlobHandle,\n readonly path: string,\n ) {}\n\n /** Returns a simple object that describes this task for debugging purposes. */\n public info() {\n return {\n rInfo: this.rInfo,\n fPath: this.path,\n done: this.done,\n error: this.error,\n state: this.state,\n };\n }\n\n public attach(w: Watcher, callerId: string) {\n this.counter.inc(callerId);\n if (!this.done) this.change.attachWatcher(w);\n }\n\n public async download() {\n try {\n const size = await this.ensureDownloaded();\n this.setDone(size);\n this.change.markChanged(`blob download for ${resourceIdToString(this.rInfo.id)} finished`);\n } catch (e: any) {\n this.logger.error(`download blob ${stringifyWithResourceId(this.rInfo)} failed: ${e}, state: ${JSON.stringify(this.state)}`);\n if (nonRecoverableError(e)) {\n this.setError(e);\n this.change.markChanged(`blob download for ${resourceIdToString(this.rInfo.id)} failed`);\n // Just in case we were half-way extracting an archive.\n await fsp.rm(this.path, { force: true });\n }\n\n throw e;\n }\n }\n\n private async ensureDownloaded() {\n this.signalCtl.signal.throwIfAborted();\n\n this.state = {};\n this.state.filePath = this.path;\n await ensureDirExists(path.dirname(this.state.filePath));\n this.signalCtl.signal.throwIfAborted();\n this.state.dirExists = true;\n\n const alreadyExists = await fileExists(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n if (alreadyExists) {\n this.state.fileExists = true;\n this.logger.info(`a blob was already downloaded: ${this.state.filePath}`);\n const stat = await fsp.stat(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n this.state.fileSize = stat.size;\n\n return this.state.fileSize;\n }\n\n const fileSize = await this.clientDownload.withBlobContent(\n this.rInfo,\n {},\n { signal: this.signalCtl.signal },\n async (content, size) => {\n this.state.fileSize = size;\n this.state.downloaded = true;\n\n await createPathAtomically(this.logger, this.state.filePath!, async (fPath: string) => {\n const f = Writable.toWeb(fs.createWriteStream(fPath, { flags: 'wx' }));\n await content.pipeTo(f, { signal: this.signalCtl.signal });\n this.state.tempWritten = true;\n });\n\n this.state.done = true;\n return size;\n }\n );\n\n return fileSize;\n }\n\n public abort(reason: string) {\n this.signalCtl.abort(new DownloadAborted(reason));\n }\n\n public getBlob():\n | { done: false }\n | {\n done: true;\n result: ValueOrError<LocalBlobHandleAndSize>;\n } {\n if (!this.done) return { done: false };\n\n return {\n done: this.done,\n result: getDownloadedBlobResponse(this.handle, this.size, this.error),\n };\n }\n\n private setDone(sizeBytes: number) {\n this.done = true;\n this.size = sizeBytes;\n }\n\n private setError(e: unknown) {\n this.done = true;\n this.error = e;\n }\n}\n\nexport function nonRecoverableError(e: any) {\n return (\n e instanceof DownloadAborted\n || e instanceof NetworkError400\n || e instanceof UnknownStorageError\n || e instanceof WrongLocalFileUrl\n // file that we downloads from was moved or deleted.\n || e?.code == 'ENOENT'\n // A resource was deleted.\n || (e.name == 'RpcError' && (e.code == 'NOT_FOUND' || e.code == 'ABORTED'))\n );\n}\n\n/** The downloading task was aborted by a signal.\n * It may happen when the computable is done, for example. */\nclass DownloadAborted extends Error {\n name = 'DownloadAborted';\n}\n\nexport function getDownloadedBlobResponse(\n handle: LocalBlobHandle | undefined,\n size: number,\n error?: unknown,\n): ValueOrError<LocalBlobHandleAndSize> {\n if (error) {\n return { ok: false, error };\n }\n\n if (!handle) {\n return { ok: false, error: new Error('No file or handle provided') };\n }\n\n return {\n ok: true,\n value: {\n handle,\n size,\n },\n };\n}\n\ntype DownloadState = {\n filePath?: string;\n dirExists?: boolean;\n fileExists?: boolean;\n fileSize?: number;\n downloaded?: boolean;\n tempWritten?: boolean;\n done?: boolean;\n}\n"],"names":["ChangeSource","CallersCounter","resourceIdToString","stringifyWithResourceId","fsp","ensureDirExists","path","fileExists","createPathAtomically","Writable","NetworkError400","UnknownStorageError","WrongLocalFileUrl"],"mappings":"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"}
@@ -51,4 +51,3 @@ type DownloadState = {
51
51
  done?: boolean;
52
52
  };
53
53
  export {};
54
- //# sourceMappingURL=download_blob_task.d.ts.map
@@ -0,0 +1,146 @@
1
+ import { ChangeSource } from '@milaboratories/computable';
2
+ import { CallersCounter, ensureDirExists, fileExists, createPathAtomically } from '@milaboratories/ts-helpers';
3
+ import fs__default from 'node:fs';
4
+ import * as fsp from 'node:fs/promises';
5
+ import * as path from 'node:path';
6
+ import { Writable } from 'node:stream';
7
+ import { UnknownStorageError, WrongLocalFileUrl } from '../../clients/download.js';
8
+ import { NetworkError400 } from '../../helpers/download.js';
9
+ import { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';
10
+
11
+ /** Downloads a blob and holds callers and watchers for the blob. */
12
+ class DownloadBlobTask {
13
+ logger;
14
+ clientDownload;
15
+ rInfo;
16
+ handle;
17
+ path;
18
+ change = new ChangeSource();
19
+ signalCtl = new AbortController();
20
+ counter = new CallersCounter();
21
+ error;
22
+ done = false;
23
+ size = 0;
24
+ state = {};
25
+ constructor(logger, clientDownload, rInfo, handle, path) {
26
+ this.logger = logger;
27
+ this.clientDownload = clientDownload;
28
+ this.rInfo = rInfo;
29
+ this.handle = handle;
30
+ this.path = path;
31
+ }
32
+ /** Returns a simple object that describes this task for debugging purposes. */
33
+ info() {
34
+ return {
35
+ rInfo: this.rInfo,
36
+ fPath: this.path,
37
+ done: this.done,
38
+ error: this.error,
39
+ state: this.state,
40
+ };
41
+ }
42
+ attach(w, callerId) {
43
+ this.counter.inc(callerId);
44
+ if (!this.done)
45
+ this.change.attachWatcher(w);
46
+ }
47
+ async download() {
48
+ try {
49
+ const size = await this.ensureDownloaded();
50
+ this.setDone(size);
51
+ this.change.markChanged(`blob download for ${resourceIdToString(this.rInfo.id)} finished`);
52
+ }
53
+ catch (e) {
54
+ this.logger.error(`download blob ${stringifyWithResourceId(this.rInfo)} failed: ${e}, state: ${JSON.stringify(this.state)}`);
55
+ if (nonRecoverableError(e)) {
56
+ this.setError(e);
57
+ this.change.markChanged(`blob download for ${resourceIdToString(this.rInfo.id)} failed`);
58
+ // Just in case we were half-way extracting an archive.
59
+ await fsp.rm(this.path, { force: true });
60
+ }
61
+ throw e;
62
+ }
63
+ }
64
+ async ensureDownloaded() {
65
+ this.signalCtl.signal.throwIfAborted();
66
+ this.state = {};
67
+ this.state.filePath = this.path;
68
+ await ensureDirExists(path.dirname(this.state.filePath));
69
+ this.signalCtl.signal.throwIfAborted();
70
+ this.state.dirExists = true;
71
+ const alreadyExists = await fileExists(this.state.filePath);
72
+ this.signalCtl.signal.throwIfAborted();
73
+ if (alreadyExists) {
74
+ this.state.fileExists = true;
75
+ this.logger.info(`a blob was already downloaded: ${this.state.filePath}`);
76
+ const stat = await fsp.stat(this.state.filePath);
77
+ this.signalCtl.signal.throwIfAborted();
78
+ this.state.fileSize = stat.size;
79
+ return this.state.fileSize;
80
+ }
81
+ const fileSize = await this.clientDownload.withBlobContent(this.rInfo, {}, { signal: this.signalCtl.signal }, async (content, size) => {
82
+ this.state.fileSize = size;
83
+ this.state.downloaded = true;
84
+ await createPathAtomically(this.logger, this.state.filePath, async (fPath) => {
85
+ const f = Writable.toWeb(fs__default.createWriteStream(fPath, { flags: 'wx' }));
86
+ await content.pipeTo(f, { signal: this.signalCtl.signal });
87
+ this.state.tempWritten = true;
88
+ });
89
+ this.state.done = true;
90
+ return size;
91
+ });
92
+ return fileSize;
93
+ }
94
+ abort(reason) {
95
+ this.signalCtl.abort(new DownloadAborted(reason));
96
+ }
97
+ getBlob() {
98
+ if (!this.done)
99
+ return { done: false };
100
+ return {
101
+ done: this.done,
102
+ result: getDownloadedBlobResponse(this.handle, this.size, this.error),
103
+ };
104
+ }
105
+ setDone(sizeBytes) {
106
+ this.done = true;
107
+ this.size = sizeBytes;
108
+ }
109
+ setError(e) {
110
+ this.done = true;
111
+ this.error = e;
112
+ }
113
+ }
114
+ function nonRecoverableError(e) {
115
+ return (e instanceof DownloadAborted
116
+ || e instanceof NetworkError400
117
+ || e instanceof UnknownStorageError
118
+ || e instanceof WrongLocalFileUrl
119
+ // file that we downloads from was moved or deleted.
120
+ || e?.code == 'ENOENT'
121
+ // A resource was deleted.
122
+ || (e.name == 'RpcError' && (e.code == 'NOT_FOUND' || e.code == 'ABORTED')));
123
+ }
124
+ /** The downloading task was aborted by a signal.
125
+ * It may happen when the computable is done, for example. */
126
+ class DownloadAborted extends Error {
127
+ name = 'DownloadAborted';
128
+ }
129
+ function getDownloadedBlobResponse(handle, size, error) {
130
+ if (error) {
131
+ return { ok: false, error };
132
+ }
133
+ if (!handle) {
134
+ return { ok: false, error: new Error('No file or handle provided') };
135
+ }
136
+ return {
137
+ ok: true,
138
+ value: {
139
+ handle,
140
+ size,
141
+ },
142
+ };
143
+ }
144
+
145
+ export { DownloadBlobTask, getDownloadedBlobResponse, nonRecoverableError };
146
+ //# sourceMappingURL=download_blob_task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download_blob_task.js","sources":["../../../src/drivers/download_blob/download_blob_task.ts"],"sourcesContent":["import type { Watcher } from '@milaboratories/computable';\nimport { ChangeSource } from '@milaboratories/computable';\nimport type { LocalBlobHandle, LocalBlobHandleAndSize } from '@milaboratories/pl-model-common';\nimport type { ResourceSnapshot } from '@milaboratories/pl-tree';\nimport type {\n ValueOrError,\n MiLogger,\n} from '@milaboratories/ts-helpers';\nimport {\n ensureDirExists,\n fileExists,\n createPathAtomically,\n CallersCounter,\n} from '@milaboratories/ts-helpers';\nimport fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { Writable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport { UnknownStorageError, WrongLocalFileUrl } from '../../clients/download';\nimport { NetworkError400 } from '../../helpers/download';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\n\n/** Downloads a blob and holds callers and watchers for the blob. */\nexport class DownloadBlobTask {\n readonly change = new ChangeSource();\n private readonly signalCtl = new AbortController();\n public readonly counter = new CallersCounter();\n private error: unknown | undefined;\n private done = false;\n public size = 0;\n private state: DownloadState = {};\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n readonly rInfo: ResourceSnapshot,\n private readonly handle: LocalBlobHandle,\n readonly path: string,\n ) {}\n\n /** Returns a simple object that describes this task for debugging purposes. */\n public info() {\n return {\n rInfo: this.rInfo,\n fPath: this.path,\n done: this.done,\n error: this.error,\n state: this.state,\n };\n }\n\n public attach(w: Watcher, callerId: string) {\n this.counter.inc(callerId);\n if (!this.done) this.change.attachWatcher(w);\n }\n\n public async download() {\n try {\n const size = await this.ensureDownloaded();\n this.setDone(size);\n this.change.markChanged(`blob download for ${resourceIdToString(this.rInfo.id)} finished`);\n } catch (e: any) {\n this.logger.error(`download blob ${stringifyWithResourceId(this.rInfo)} failed: ${e}, state: ${JSON.stringify(this.state)}`);\n if (nonRecoverableError(e)) {\n this.setError(e);\n this.change.markChanged(`blob download for ${resourceIdToString(this.rInfo.id)} failed`);\n // Just in case we were half-way extracting an archive.\n await fsp.rm(this.path, { force: true });\n }\n\n throw e;\n }\n }\n\n private async ensureDownloaded() {\n this.signalCtl.signal.throwIfAborted();\n\n this.state = {};\n this.state.filePath = this.path;\n await ensureDirExists(path.dirname(this.state.filePath));\n this.signalCtl.signal.throwIfAborted();\n this.state.dirExists = true;\n\n const alreadyExists = await fileExists(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n if (alreadyExists) {\n this.state.fileExists = true;\n this.logger.info(`a blob was already downloaded: ${this.state.filePath}`);\n const stat = await fsp.stat(this.state.filePath);\n this.signalCtl.signal.throwIfAborted();\n this.state.fileSize = stat.size;\n\n return this.state.fileSize;\n }\n\n const fileSize = await this.clientDownload.withBlobContent(\n this.rInfo,\n {},\n { signal: this.signalCtl.signal },\n async (content, size) => {\n this.state.fileSize = size;\n this.state.downloaded = true;\n\n await createPathAtomically(this.logger, this.state.filePath!, async (fPath: string) => {\n const f = Writable.toWeb(fs.createWriteStream(fPath, { flags: 'wx' }));\n await content.pipeTo(f, { signal: this.signalCtl.signal });\n this.state.tempWritten = true;\n });\n\n this.state.done = true;\n return size;\n }\n );\n\n return fileSize;\n }\n\n public abort(reason: string) {\n this.signalCtl.abort(new DownloadAborted(reason));\n }\n\n public getBlob():\n | { done: false }\n | {\n done: true;\n result: ValueOrError<LocalBlobHandleAndSize>;\n } {\n if (!this.done) return { done: false };\n\n return {\n done: this.done,\n result: getDownloadedBlobResponse(this.handle, this.size, this.error),\n };\n }\n\n private setDone(sizeBytes: number) {\n this.done = true;\n this.size = sizeBytes;\n }\n\n private setError(e: unknown) {\n this.done = true;\n this.error = e;\n }\n}\n\nexport function nonRecoverableError(e: any) {\n return (\n e instanceof DownloadAborted\n || e instanceof NetworkError400\n || e instanceof UnknownStorageError\n || e instanceof WrongLocalFileUrl\n // file that we downloads from was moved or deleted.\n || e?.code == 'ENOENT'\n // A resource was deleted.\n || (e.name == 'RpcError' && (e.code == 'NOT_FOUND' || e.code == 'ABORTED'))\n );\n}\n\n/** The downloading task was aborted by a signal.\n * It may happen when the computable is done, for example. */\nclass DownloadAborted extends Error {\n name = 'DownloadAborted';\n}\n\nexport function getDownloadedBlobResponse(\n handle: LocalBlobHandle | undefined,\n size: number,\n error?: unknown,\n): ValueOrError<LocalBlobHandleAndSize> {\n if (error) {\n return { ok: false, error };\n }\n\n if (!handle) {\n return { ok: false, error: new Error('No file or handle provided') };\n }\n\n return {\n ok: true,\n value: {\n handle,\n size,\n },\n };\n}\n\ntype DownloadState = {\n filePath?: string;\n dirExists?: boolean;\n fileExists?: boolean;\n fileSize?: number;\n downloaded?: boolean;\n tempWritten?: boolean;\n done?: boolean;\n}\n"],"names":["fs"],"mappings":"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"}
@@ -0,0 +1,202 @@
1
+ 'use strict';
2
+
3
+ var tsHelpers = require('@milaboratories/ts-helpers');
4
+ var fs = require('node:fs');
5
+ var path = require('node:path');
6
+ var ranges = require('./ranges.cjs');
7
+ var file = require('./file.cjs');
8
+ var helpers = require('@milaboratories/helpers');
9
+
10
+ /** The implementer of SparseCacheRanges could throw it if ranges were corrupted. */
11
+ class CorruptedRangesError extends Error {
12
+ name = 'CorruptedRangesError';
13
+ }
14
+ /** Stores ranges in a directory as JSON files (the default implementation). */
15
+ class SparseCacheFsRanges {
16
+ logger;
17
+ cacheDir;
18
+ constructor(logger, cacheDir) {
19
+ this.logger = logger;
20
+ this.cacheDir = cacheDir;
21
+ }
22
+ fPath(key) {
23
+ return path.join(this.cacheDir, ranges.rangesFileName(key));
24
+ }
25
+ async get(key) {
26
+ return await ranges.readRangesFile(this.logger, this.fPath(key));
27
+ }
28
+ async set(key, ranges$1) {
29
+ return await ranges.writeRangesFile(this.logger, this.fPath(key), ranges$1);
30
+ }
31
+ async delete(key) {
32
+ await fs.promises.rm(this.fPath(key));
33
+ }
34
+ }
35
+ /** Stores sparse files in a directory (the default implementation). */
36
+ class SparseCacheFsFile {
37
+ logger;
38
+ cacheDir;
39
+ suffix = '.sparse.bin';
40
+ constructor(logger, cacheDir) {
41
+ this.logger = logger;
42
+ this.cacheDir = cacheDir;
43
+ }
44
+ async all() {
45
+ await tsHelpers.ensureDirExists(this.cacheDir);
46
+ const files = await fs.promises.readdir(this.cacheDir);
47
+ return files.filter((f) => f.endsWith(this.suffix));
48
+ }
49
+ async exists(key) {
50
+ return await tsHelpers.fileExists(this.path(key));
51
+ }
52
+ path(key) {
53
+ return path.join(this.cacheDir, key + this.suffix);
54
+ }
55
+ async write(key, data, from) {
56
+ await tsHelpers.ensureDirExists(this.cacheDir);
57
+ await file.writeToSparseFile(this.logger, process.platform, this.path(key), data, from);
58
+ }
59
+ async delete(key) {
60
+ await fs.promises.rm(this.path(key));
61
+ }
62
+ }
63
+ /** LRU cache for ranges of sparse files. */
64
+ class SparseCache {
65
+ logger;
66
+ maxSize;
67
+ ranges;
68
+ storage;
69
+ /** Fields are public for tests. */
70
+ /** The lock to make sure cache requests are done one by one. */
71
+ lock = new helpers.functions.AwaitLock();
72
+ keyToLastAccessTime = new Map();
73
+ size = 0;
74
+ constructor(logger,
75
+ /** The hard limit in bytes. */
76
+ maxSize, ranges, storage) {
77
+ this.logger = logger;
78
+ this.maxSize = maxSize;
79
+ this.ranges = ranges;
80
+ this.storage = storage;
81
+ }
82
+ /** Resets a cache's size by rereading everything we already store.
83
+ * Safe for concurrent use. */
84
+ async reset() {
85
+ await withLock(this.lock, async () => {
86
+ await this.resetUnsafe();
87
+ });
88
+ }
89
+ /** Returns a path to the key if the range exists in a cache, otherwise returns undefined.
90
+ * Safe for concurrent use. */
91
+ async get(key, range) {
92
+ return await withLock(this.lock, async () => {
93
+ return await this.getUnsafe(key, range);
94
+ });
95
+ }
96
+ /** Sets data to the cache's file and clear the cache if it's needed.
97
+ * Safe for concurrent use. */
98
+ async set(key, range, data) {
99
+ await withLock(this.lock, async () => {
100
+ await this.setUnsafe(key, range, data);
101
+ });
102
+ }
103
+ async resetUnsafe() {
104
+ this.size = 0;
105
+ this.keyToLastAccessTime = new Map();
106
+ const now = new Date();
107
+ // In rmKey method we first deletes the key from a storage and only then from ranges,
108
+ // so if something goes wrong between 2 operations,
109
+ // on reset the logic will be correct.
110
+ for (const key of await this.storage.all()) {
111
+ const ranges$1 = await this.ranges.get(key);
112
+ this.size += ranges.rangesSize(ranges$1);
113
+ this.keyToLastAccessTime.set(key, now);
114
+ }
115
+ }
116
+ async getUnsafe(key, range) {
117
+ // It first checks the storage, and then the ranges.
118
+ // In another method, when we remove a key, it first deletes a key from a storage and then from ranges,
119
+ // so if we don't have a key in storage but have it in ranges, the logic here is correct.
120
+ // We probably could reverse the operations here and there, and everywhere we work with both storage and ranges.
121
+ if (await this.storage.exists(key)) {
122
+ this.keyToLastAccessTime.set(key, new Date());
123
+ const ranges$1 = await this.getRanges(key);
124
+ if (ranges.doesRangeExist(ranges$1, range)) {
125
+ return this.storage.path(key);
126
+ }
127
+ return undefined;
128
+ }
129
+ return undefined;
130
+ }
131
+ async setUnsafe(key, range, data) {
132
+ await this.setWithoutEviction(key, range, data);
133
+ await this.ensureEvicted();
134
+ }
135
+ /** Sets a key and recalculates a size, but doesn't ensures that the size is less than the hard limit. */
136
+ async setWithoutEviction(key, range, data) {
137
+ if (range.to - range.from !== data.length) {
138
+ throw new Error(`SparseCache.set: trying to set ${key} with wrong range length: `
139
+ + `range: ${JSON.stringify(range)}, data: ${data.length}`);
140
+ }
141
+ this.keyToLastAccessTime.set(key, new Date());
142
+ const ranges$1 = await this.getRanges(key);
143
+ this.size -= ranges.rangesSize(ranges$1);
144
+ await this.storage.write(key, data, range.from);
145
+ const newRanges = ranges.addRange(ranges$1, range);
146
+ this.size += ranges.rangesSize(newRanges);
147
+ await this.ranges.set(key, newRanges);
148
+ }
149
+ /** Ensures the size is less than hard limit by deleting the oldest keys. */
150
+ async ensureEvicted() {
151
+ const byTime = tsHelpers.mapEntries(this.keyToLastAccessTime);
152
+ byTime.sort(([_, aDate], [__, bDate]) => bDate.getTime() - aDate.getTime());
153
+ while (this.size > this.maxSize) {
154
+ const keyAndDate = byTime.pop(); // removes the oldest
155
+ if (!keyAndDate) {
156
+ break;
157
+ }
158
+ const [key, _] = keyAndDate;
159
+ const ranges$1 = await this.getRanges(key);
160
+ this.size -= ranges.rangesSize(ranges$1);
161
+ this.rmKey(key);
162
+ }
163
+ }
164
+ /** Gets ranges and if they were corrupted, then remove the file from the cache and reset the cache's size. */
165
+ async getRanges(key) {
166
+ try {
167
+ return await this.ranges.get(key);
168
+ }
169
+ catch (e) {
170
+ if (e instanceof CorruptedRangesError) {
171
+ // We need to reset a state of the cache and update current size,
172
+ // it's the only way to calculate the real size when one of the ranges were corrupted.
173
+ await this.rmKey(key);
174
+ await this.resetUnsafe();
175
+ return await this.ranges.get(key);
176
+ }
177
+ throw e;
178
+ }
179
+ }
180
+ /** Removes a key the state of the cache. The size should be updated. */
181
+ async rmKey(key) {
182
+ await this.storage.delete(key);
183
+ await this.ranges.delete(key);
184
+ this.keyToLastAccessTime.delete(key);
185
+ }
186
+ }
187
+ /** Acquires the lock and executes a callback. */
188
+ async function withLock(lock, cb) {
189
+ try {
190
+ await lock.acquireAsync();
191
+ return await cb();
192
+ }
193
+ finally {
194
+ lock.release();
195
+ }
196
+ }
197
+
198
+ exports.CorruptedRangesError = CorruptedRangesError;
199
+ exports.SparseCache = SparseCache;
200
+ exports.SparseCacheFsFile = SparseCacheFsFile;
201
+ exports.SparseCacheFsRanges = SparseCacheFsRanges;
202
+ //# sourceMappingURL=cache.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.cjs","sources":["../../../../src/drivers/download_blob/sparse_cache/cache.ts"],"sourcesContent":["import { RangeBytes } from '@milaboratories/pl-model-common';\nimport { ensureDirExists, fileExists, mapEntries, MiLogger } from '@milaboratories/ts-helpers';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { addRange, doesRangeExist, Ranges, rangesFileName, rangesFilePostfix, rangesSize, readRangesFile, writeRangesFile } from './ranges';\nimport { writeToSparseFile } from './file';\nimport { functions } from '@milaboratories/helpers';\n\n/** The implementer of SparseCacheRanges could throw it if ranges were corrupted. */\nexport class CorruptedRangesError extends Error {\n name = 'CorruptedRangesError';\n}\n\n/** Extracted ranges methods to be able to store ranges somewhere else (e.g. in memory for tests). */\nexport interface SparseCacheRanges {\n get(key: string): Promise<Ranges>;\n set(key: string, ranges: Ranges): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores ranges in a directory as JSON files (the default implementation). */\nexport class SparseCacheFsRanges implements SparseCacheRanges {\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n private fPath(key: string): string {\n return path.join(this.cacheDir, rangesFileName(key));\n }\n\n async get(key: string): Promise<Ranges> {\n return await readRangesFile(this.logger, this.fPath(key));\n }\n\n async set(key: string, ranges: Ranges) {\n return await writeRangesFile(this.logger, this.fPath(key), ranges);\n }\n\n async delete(key: string) {\n await fs.rm(this.fPath(key));\n }\n}\n\n/** Extracted interface for storing sparse files. */\nexport interface SparseFileStorage {\n all(): Promise<string[]>;\n exists(key: string): Promise<boolean>;\n path(key: string): string;\n write(key: string, data: Uint8Array, from: number): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores sparse files in a directory (the default implementation). */\nexport class SparseCacheFsFile implements SparseFileStorage {\n private readonly suffix = '.sparse.bin';\n\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n async all(): Promise<string[]> {\n await ensureDirExists(this.cacheDir);\n const files = await fs.readdir(this.cacheDir);\n return files.filter((f) => f.endsWith(this.suffix));\n }\n\n async exists(key: string): Promise<boolean> {\n return await fileExists(this.path(key));\n }\n\n path(key: string): string {\n return path.join(this.cacheDir, key + this.suffix);\n }\n\n async write(key: string, data: Uint8Array, from: number): Promise<void> {\n await ensureDirExists(this.cacheDir);\n await writeToSparseFile(this.logger, process.platform, this.path(key), data, from);\n }\n\n async delete(key: string): Promise<void> {\n await fs.rm(this.path(key));\n }\n}\n\n/** LRU cache for ranges of sparse files. */\nexport class SparseCache {\n /** Fields are public for tests. */\n\n /** The lock to make sure cache requests are done one by one. */\n private lock = new functions.AwaitLock()\n\n public keyToLastAccessTime = new Map<string, Date>();\n public size = 0;\n\n constructor(\n public readonly logger: MiLogger,\n /** The hard limit in bytes. */\n public readonly maxSize: number,\n public readonly ranges: SparseCacheRanges,\n public readonly storage: SparseFileStorage,\n ) {}\n\n /** Resets a cache's size by rereading everything we already store.\n * Safe for concurrent use. */\n async reset() {\n await withLock(this.lock, async () => {\n await this.resetUnsafe();\n })\n }\n\n /** Returns a path to the key if the range exists in a cache, otherwise returns undefined.\n * Safe for concurrent use. */\n async get(key: string, range: RangeBytes): Promise<string | undefined> {\n return await withLock(this.lock, async () => {\n return await this.getUnsafe(key, range);\n });\n }\n\n /** Sets data to the cache's file and clear the cache if it's needed.\n * Safe for concurrent use. */\n async set(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n await withLock(this.lock, async () => {\n await this.setUnsafe(key, range, data);\n })\n }\n\n private async resetUnsafe() {\n this.size = 0;\n this.keyToLastAccessTime = new Map<string, Date>();\n\n const now = new Date();\n // In rmKey method we first deletes the key from a storage and only then from ranges,\n // so if something goes wrong between 2 operations,\n // on reset the logic will be correct.\n for (const key of await this.storage.all()) {\n const ranges = await this.ranges.get(key);\n this.size += rangesSize(ranges);\n this.keyToLastAccessTime.set(key, now);\n }\n }\n\n private async getUnsafe(key: string, range: RangeBytes): Promise<string | undefined> {\n // It first checks the storage, and then the ranges.\n // In another method, when we remove a key, it first deletes a key from a storage and then from ranges,\n // so if we don't have a key in storage but have it in ranges, the logic here is correct.\n // We probably could reverse the operations here and there, and everywhere we work with both storage and ranges.\n if (await this.storage.exists(key)) {\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n if (doesRangeExist(ranges, range)) {\n return this.storage.path(key);\n }\n\n return undefined;\n }\n\n return undefined;\n }\n\n private async setUnsafe(key: string, range: { from: number; to: number; }, data: Uint8Array) {\n await this.setWithoutEviction(key, range, data);\n await this.ensureEvicted();\n }\n\n /** Sets a key and recalculates a size, but doesn't ensures that the size is less than the hard limit. */\n async setWithoutEviction(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n if (range.to - range.from !== data.length) {\n throw new Error(\n `SparseCache.set: trying to set ${key} with wrong range length: `\n + `range: ${JSON.stringify(range)}, data: ${data.length}`\n );\n }\n\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n\n await this.storage.write(key, data, range.from);\n\n const newRanges = addRange(ranges, range);\n this.size += rangesSize(newRanges);\n\n await this.ranges.set(key, newRanges);\n }\n\n /** Ensures the size is less than hard limit by deleting the oldest keys. */\n async ensureEvicted(): Promise<void> {\n const byTime = mapEntries(this.keyToLastAccessTime);\n byTime.sort(([_, aDate], [__, bDate]) => bDate.getTime() - aDate.getTime());\n\n while (this.size > this.maxSize) {\n const keyAndDate = byTime.pop(); // removes the oldest\n if (!keyAndDate) {\n break;\n }\n const [key, _] = keyAndDate;\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n this.rmKey(key);\n }\n }\n\n /** Gets ranges and if they were corrupted, then remove the file from the cache and reset the cache's size. */\n private async getRanges(key: string) {\n try {\n return await this.ranges.get(key);\n } catch (e: unknown) {\n if (e instanceof CorruptedRangesError) {\n // We need to reset a state of the cache and update current size,\n // it's the only way to calculate the real size when one of the ranges were corrupted.\n await this.rmKey(key);\n await this.resetUnsafe();\n\n return await this.ranges.get(key);\n }\n\n throw e;\n }\n }\n\n /** Removes a key the state of the cache. The size should be updated. */\n private async rmKey(key: string) {\n await this.storage.delete(key);\n await this.ranges.delete(key);\n this.keyToLastAccessTime.delete(key);\n }\n}\n\n/** Acquires the lock and executes a callback. */\nasync function withLock<T>(lock: functions.AwaitLock, cb: () => Promise<T>): Promise<T> {\n try {\n await lock.acquireAsync();\n return await cb();\n } finally {\n lock.release();\n }\n}\n"],"names":["rangesFileName","readRangesFile","ranges","writeRangesFile","fs","ensureDirExists","fileExists","writeToSparseFile","functions","rangesSize","doesRangeExist","addRange","mapEntries"],"mappings":"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"}
@@ -77,4 +77,3 @@ export declare class SparseCache {
77
77
  /** Removes a key the state of the cache. The size should be updated. */
78
78
  private rmKey;
79
79
  }
80
- //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1,197 @@
1
+ import { ensureDirExists, fileExists, mapEntries } from '@milaboratories/ts-helpers';
2
+ import { promises } from 'node:fs';
3
+ import path__default from 'node:path';
4
+ import { rangesFileName, readRangesFile, writeRangesFile, rangesSize, doesRangeExist, addRange } from './ranges.js';
5
+ import { writeToSparseFile } from './file.js';
6
+ import { functions } from '@milaboratories/helpers';
7
+
8
+ /** The implementer of SparseCacheRanges could throw it if ranges were corrupted. */
9
+ class CorruptedRangesError extends Error {
10
+ name = 'CorruptedRangesError';
11
+ }
12
+ /** Stores ranges in a directory as JSON files (the default implementation). */
13
+ class SparseCacheFsRanges {
14
+ logger;
15
+ cacheDir;
16
+ constructor(logger, cacheDir) {
17
+ this.logger = logger;
18
+ this.cacheDir = cacheDir;
19
+ }
20
+ fPath(key) {
21
+ return path__default.join(this.cacheDir, rangesFileName(key));
22
+ }
23
+ async get(key) {
24
+ return await readRangesFile(this.logger, this.fPath(key));
25
+ }
26
+ async set(key, ranges) {
27
+ return await writeRangesFile(this.logger, this.fPath(key), ranges);
28
+ }
29
+ async delete(key) {
30
+ await promises.rm(this.fPath(key));
31
+ }
32
+ }
33
+ /** Stores sparse files in a directory (the default implementation). */
34
+ class SparseCacheFsFile {
35
+ logger;
36
+ cacheDir;
37
+ suffix = '.sparse.bin';
38
+ constructor(logger, cacheDir) {
39
+ this.logger = logger;
40
+ this.cacheDir = cacheDir;
41
+ }
42
+ async all() {
43
+ await ensureDirExists(this.cacheDir);
44
+ const files = await promises.readdir(this.cacheDir);
45
+ return files.filter((f) => f.endsWith(this.suffix));
46
+ }
47
+ async exists(key) {
48
+ return await fileExists(this.path(key));
49
+ }
50
+ path(key) {
51
+ return path__default.join(this.cacheDir, key + this.suffix);
52
+ }
53
+ async write(key, data, from) {
54
+ await ensureDirExists(this.cacheDir);
55
+ await writeToSparseFile(this.logger, process.platform, this.path(key), data, from);
56
+ }
57
+ async delete(key) {
58
+ await promises.rm(this.path(key));
59
+ }
60
+ }
61
+ /** LRU cache for ranges of sparse files. */
62
+ class SparseCache {
63
+ logger;
64
+ maxSize;
65
+ ranges;
66
+ storage;
67
+ /** Fields are public for tests. */
68
+ /** The lock to make sure cache requests are done one by one. */
69
+ lock = new functions.AwaitLock();
70
+ keyToLastAccessTime = new Map();
71
+ size = 0;
72
+ constructor(logger,
73
+ /** The hard limit in bytes. */
74
+ maxSize, ranges, storage) {
75
+ this.logger = logger;
76
+ this.maxSize = maxSize;
77
+ this.ranges = ranges;
78
+ this.storage = storage;
79
+ }
80
+ /** Resets a cache's size by rereading everything we already store.
81
+ * Safe for concurrent use. */
82
+ async reset() {
83
+ await withLock(this.lock, async () => {
84
+ await this.resetUnsafe();
85
+ });
86
+ }
87
+ /** Returns a path to the key if the range exists in a cache, otherwise returns undefined.
88
+ * Safe for concurrent use. */
89
+ async get(key, range) {
90
+ return await withLock(this.lock, async () => {
91
+ return await this.getUnsafe(key, range);
92
+ });
93
+ }
94
+ /** Sets data to the cache's file and clear the cache if it's needed.
95
+ * Safe for concurrent use. */
96
+ async set(key, range, data) {
97
+ await withLock(this.lock, async () => {
98
+ await this.setUnsafe(key, range, data);
99
+ });
100
+ }
101
+ async resetUnsafe() {
102
+ this.size = 0;
103
+ this.keyToLastAccessTime = new Map();
104
+ const now = new Date();
105
+ // In rmKey method we first deletes the key from a storage and only then from ranges,
106
+ // so if something goes wrong between 2 operations,
107
+ // on reset the logic will be correct.
108
+ for (const key of await this.storage.all()) {
109
+ const ranges = await this.ranges.get(key);
110
+ this.size += rangesSize(ranges);
111
+ this.keyToLastAccessTime.set(key, now);
112
+ }
113
+ }
114
+ async getUnsafe(key, range) {
115
+ // It first checks the storage, and then the ranges.
116
+ // In another method, when we remove a key, it first deletes a key from a storage and then from ranges,
117
+ // so if we don't have a key in storage but have it in ranges, the logic here is correct.
118
+ // We probably could reverse the operations here and there, and everywhere we work with both storage and ranges.
119
+ if (await this.storage.exists(key)) {
120
+ this.keyToLastAccessTime.set(key, new Date());
121
+ const ranges = await this.getRanges(key);
122
+ if (doesRangeExist(ranges, range)) {
123
+ return this.storage.path(key);
124
+ }
125
+ return undefined;
126
+ }
127
+ return undefined;
128
+ }
129
+ async setUnsafe(key, range, data) {
130
+ await this.setWithoutEviction(key, range, data);
131
+ await this.ensureEvicted();
132
+ }
133
+ /** Sets a key and recalculates a size, but doesn't ensures that the size is less than the hard limit. */
134
+ async setWithoutEviction(key, range, data) {
135
+ if (range.to - range.from !== data.length) {
136
+ throw new Error(`SparseCache.set: trying to set ${key} with wrong range length: `
137
+ + `range: ${JSON.stringify(range)}, data: ${data.length}`);
138
+ }
139
+ this.keyToLastAccessTime.set(key, new Date());
140
+ const ranges = await this.getRanges(key);
141
+ this.size -= rangesSize(ranges);
142
+ await this.storage.write(key, data, range.from);
143
+ const newRanges = addRange(ranges, range);
144
+ this.size += rangesSize(newRanges);
145
+ await this.ranges.set(key, newRanges);
146
+ }
147
+ /** Ensures the size is less than hard limit by deleting the oldest keys. */
148
+ async ensureEvicted() {
149
+ const byTime = mapEntries(this.keyToLastAccessTime);
150
+ byTime.sort(([_, aDate], [__, bDate]) => bDate.getTime() - aDate.getTime());
151
+ while (this.size > this.maxSize) {
152
+ const keyAndDate = byTime.pop(); // removes the oldest
153
+ if (!keyAndDate) {
154
+ break;
155
+ }
156
+ const [key, _] = keyAndDate;
157
+ const ranges = await this.getRanges(key);
158
+ this.size -= rangesSize(ranges);
159
+ this.rmKey(key);
160
+ }
161
+ }
162
+ /** Gets ranges and if they were corrupted, then remove the file from the cache and reset the cache's size. */
163
+ async getRanges(key) {
164
+ try {
165
+ return await this.ranges.get(key);
166
+ }
167
+ catch (e) {
168
+ if (e instanceof CorruptedRangesError) {
169
+ // We need to reset a state of the cache and update current size,
170
+ // it's the only way to calculate the real size when one of the ranges were corrupted.
171
+ await this.rmKey(key);
172
+ await this.resetUnsafe();
173
+ return await this.ranges.get(key);
174
+ }
175
+ throw e;
176
+ }
177
+ }
178
+ /** Removes a key the state of the cache. The size should be updated. */
179
+ async rmKey(key) {
180
+ await this.storage.delete(key);
181
+ await this.ranges.delete(key);
182
+ this.keyToLastAccessTime.delete(key);
183
+ }
184
+ }
185
+ /** Acquires the lock and executes a callback. */
186
+ async function withLock(lock, cb) {
187
+ try {
188
+ await lock.acquireAsync();
189
+ return await cb();
190
+ }
191
+ finally {
192
+ lock.release();
193
+ }
194
+ }
195
+
196
+ export { CorruptedRangesError, SparseCache, SparseCacheFsFile, SparseCacheFsRanges };
197
+ //# sourceMappingURL=cache.js.map