@vbarbarosh/node-helpers 3.74.1 → 3.76.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 (367) hide show
  1. package/README.md +29 -3
  2. package/dist/browser.js +1 -2504
  3. package/package.json +31 -11
  4. package/src/HeartbeatServer.js +24 -3
  5. package/src/HeartbeatServer.md +33 -0
  6. package/src/array_chunk.js +1 -1
  7. package/src/array_chunk.md +8 -0
  8. package/src/array_chunk_balanced.md +14 -0
  9. package/src/array_fps.md +12 -0
  10. package/src/array_gcd.js +4 -2
  11. package/src/array_gcd.md +2 -1
  12. package/src/array_group.js +3 -3
  13. package/src/array_group.md +14 -0
  14. package/src/array_group_map.js +3 -3
  15. package/src/array_group_map.md +13 -0
  16. package/src/array_index.js +2 -2
  17. package/src/array_index.md +9 -0
  18. package/src/array_lcm.js +4 -2
  19. package/src/array_lcm.md +2 -1
  20. package/src/array_max.js +11 -4
  21. package/src/array_max.md +1 -1
  22. package/src/array_min.js +11 -4
  23. package/src/array_min.md +1 -1
  24. package/src/array_permutations.js +4 -0
  25. package/src/array_permutations.md +13 -0
  26. package/src/array_sort.js +7 -2
  27. package/src/array_sort.md +13 -0
  28. package/src/array_sort_other.js +8 -5
  29. package/src/array_sort_other.md +11 -0
  30. package/src/array_sum.js +2 -2
  31. package/src/array_sum.md +8 -0
  32. package/src/array_unique.js +2 -2
  33. package/src/array_unique_last.js +2 -2
  34. package/src/assert_valid_expires_at.md +10 -0
  35. package/src/clamp.md +9 -0
  36. package/src/cli-apps/watchdog.js +5 -0
  37. package/src/cli.js +3 -1
  38. package/src/cli.md +15 -0
  39. package/src/const_stream.md +7 -0
  40. package/src/const_type.md +5 -0
  41. package/src/countdown.js +22 -5
  42. package/src/countdown.md +21 -0
  43. package/src/crypto_decrypt_aes256.md +13 -0
  44. package/src/crypto_encrypt_aes256.md +13 -0
  45. package/src/crypto_hash_md5.md +10 -0
  46. package/src/crypto_hash_sha256.md +10 -0
  47. package/src/crypto_hmac_sha256.md +11 -0
  48. package/src/date_add_hours.md +8 -0
  49. package/src/date_add_milliseconds.md +4 -0
  50. package/src/date_add_minutes.md +4 -0
  51. package/src/date_add_months.md +13 -0
  52. package/src/date_add_seconds.md +4 -0
  53. package/src/date_diff_seconds.md +4 -0
  54. package/src/date_is_leap_year.md +9 -0
  55. package/src/escape_content_disposition.js +6 -3
  56. package/src/escape_content_disposition.md +13 -0
  57. package/src/escape_regexp.md +7 -0
  58. package/src/eta.js +7 -1
  59. package/src/eta.md +18 -0
  60. package/src/factorize_ms.js +3 -0
  61. package/src/factorize_ms.md +10 -0
  62. package/src/fastdl.js +14 -3
  63. package/src/fcmp_dates.md +12 -0
  64. package/src/fcmp_default.js +7 -0
  65. package/src/fcmp_default.md +29 -0
  66. package/src/fcmp_default_desc.js +4 -7
  67. package/src/fcmp_default_desc.md +10 -0
  68. package/src/fcmp_from_spec.md +13 -0
  69. package/src/fcmp_numbers.md +11 -0
  70. package/src/fcmp_tuples.md +17 -0
  71. package/src/fcmp_utf8_bin.md +14 -0
  72. package/src/fcmp_utf8_ci.md +14 -0
  73. package/src/fcmp_utf8_cs.md +15 -0
  74. package/src/fcmp_utf8_natural_bin.js +38 -0
  75. package/src/fcmp_utf8_natural_bin.md +8 -0
  76. package/src/fcmp_utf8_natural_ci.md +13 -0
  77. package/src/fcmp_utf8_natural_cs.md +13 -0
  78. package/src/fcmpx.md +26 -9
  79. package/src/filter1_from_spec.js +3 -3
  80. package/src/format_bytes.md +15 -0
  81. package/src/format_date.md +7 -0
  82. package/src/format_date_fs.md +7 -0
  83. package/src/format_date_human.md +6 -0
  84. package/src/format_date_ymd.md +6 -0
  85. package/src/format_error_report.js +3 -3
  86. package/src/format_error_report.md +12 -0
  87. package/src/format_kilo.md +13 -0
  88. package/src/format_ms.js +3 -2
  89. package/src/format_ms.md +12 -0
  90. package/src/format_ms2.js +6 -3
  91. package/src/format_ms2.md +12 -0
  92. package/src/format_ms3.js +3 -2
  93. package/src/format_ms3.md +12 -0
  94. package/src/format_percents.md +10 -0
  95. package/src/format_progress_bytes.md +13 -0
  96. package/src/format_progress_kilo.md +10 -0
  97. package/src/format_seconds.md +11 -0
  98. package/src/format_thousands.md +9 -0
  99. package/src/fs_append.md +7 -0
  100. package/src/fs_append_json.md +8 -0
  101. package/src/fs_append_utf8.md +6 -0
  102. package/src/fs_assert_file_exists.md +3 -0
  103. package/src/fs_assert_file_readable.js +1 -1
  104. package/src/fs_assert_file_readable.md +3 -0
  105. package/src/fs_assert_file_socket.md +2 -0
  106. package/src/fs_copy.md +7 -0
  107. package/src/fs_copy_excl.md +2 -0
  108. package/src/fs_exists.md +6 -0
  109. package/src/fs_fclose.md +5 -0
  110. package/src/fs_fi.js +13 -1
  111. package/src/fs_fi.md +11 -0
  112. package/src/fs_file_md5.md +8 -0
  113. package/src/fs_find.md +7 -0
  114. package/src/fs_fopen.md +6 -0
  115. package/src/fs_fread.js +2 -2
  116. package/src/fs_fread.md +11 -0
  117. package/src/fs_fsize.md +2 -0
  118. package/src/fs_fstat.md +2 -0
  119. package/src/fs_fwrite.js +2 -2
  120. package/src/fs_fwrite.md +9 -0
  121. package/src/fs_list.md +5 -0
  122. package/src/fs_list_deep.md +5 -0
  123. package/src/fs_ls.md +2 -0
  124. package/src/fs_lsr.md +4 -0
  125. package/src/fs_lstat.md +3 -0
  126. package/src/fs_mkdir.md +3 -0
  127. package/src/fs_mkdirp.js +3 -30
  128. package/src/fs_mkdirp.md +7 -0
  129. package/src/fs_path_basename.md +2 -0
  130. package/src/fs_path_dirname.md +2 -0
  131. package/src/fs_path_extname.md +9 -0
  132. package/src/fs_path_join.md +2 -0
  133. package/src/fs_path_relative.md +2 -0
  134. package/src/fs_path_resolve.md +2 -0
  135. package/src/fs_path_tempdir.md +2 -0
  136. package/src/fs_path_untildify.md +8 -0
  137. package/src/fs_read.md +3 -0
  138. package/src/fs_read_buffer.md +5 -0
  139. package/src/fs_read_json.md +6 -0
  140. package/src/fs_read_lines.md +7 -0
  141. package/src/fs_read_stream.md +2 -0
  142. package/src/fs_read_utf8.md +5 -0
  143. package/src/fs_readdir.md +3 -0
  144. package/src/fs_readlink.md +6 -0
  145. package/src/fs_rename.md +6 -0
  146. package/src/fs_rm.md +4 -0
  147. package/src/fs_rmdir.md +3 -0
  148. package/src/fs_rmf.md +2 -0
  149. package/src/fs_rmrf.md +8 -0
  150. package/src/fs_sha256.js +16 -0
  151. package/src/fs_sha256.md +9 -0
  152. package/src/fs_size.js +2 -2
  153. package/src/fs_size.md +6 -0
  154. package/src/fs_stat.md +2 -0
  155. package/src/fs_tempdir.js +20 -5
  156. package/src/fs_touch.md +6 -0
  157. package/src/fs_walk.js +2 -2
  158. package/src/fs_walk.md +14 -0
  159. package/src/fs_write.md +3 -0
  160. package/src/fs_write_json.md +7 -0
  161. package/src/fs_write_stream.md +2 -0
  162. package/src/gettype.md +13 -0
  163. package/src/http_delete.md +11 -0
  164. package/src/http_get_blob.md +8 -0
  165. package/src/http_get_buffer.md +10 -0
  166. package/src/http_get_file.js +8 -8
  167. package/src/http_get_file.md +14 -0
  168. package/src/http_get_json.md +10 -0
  169. package/src/http_get_stream.md +14 -0
  170. package/src/http_get_stream_range.js +27 -9
  171. package/src/http_get_stream_range.md +22 -0
  172. package/src/http_get_utf8.md +9 -0
  173. package/src/http_head.md +9 -0
  174. package/src/http_patch_json.md +10 -0
  175. package/src/http_post_json.md +11 -0
  176. package/src/http_post_multipart.md +15 -0
  177. package/src/http_post_urlencoded.md +11 -0
  178. package/src/http_put_buffer.md +9 -0
  179. package/src/http_put_file.js +9 -6
  180. package/src/http_put_file.md +16 -0
  181. package/src/http_put_json.md +11 -0
  182. package/src/http_put_utf8.md +9 -0
  183. package/src/http_range_parse.js +6 -3
  184. package/src/http_range_parse.md +18 -0
  185. package/src/http_stream_range.js +35 -22
  186. package/src/http_stream_range.md +29 -0
  187. package/src/identity.md +2 -0
  188. package/src/ignore.md +2 -0
  189. package/src/json_stringify_safe.md +10 -0
  190. package/src/json_stringify_stable.md +9 -0
  191. package/src/logger.js +2 -3
  192. package/src/logger.md +15 -0
  193. package/src/make_progress.js +8 -5
  194. package/src/make_progress.md +20 -0
  195. package/src/math_gcd.js +2 -1
  196. package/src/mongo_stream_upsert.js +10 -3
  197. package/src/mongo_stream_upsert.md +17 -0
  198. package/src/mongo_stream_write.js +10 -3
  199. package/src/mongo_stream_write.md +16 -0
  200. package/src/msval.md +10 -0
  201. package/src/now_fs.md +7 -0
  202. package/src/now_human.md +6 -0
  203. package/src/object_defaults.md +11 -0
  204. package/src/object_schema.js +2 -1
  205. package/src/object_schema.md +12 -0
  206. package/src/object_walk_preorder.md +16 -0
  207. package/src/parallel.js +23 -10
  208. package/src/parse_bytes.js +4 -2
  209. package/src/parse_bytes.md +15 -0
  210. package/src/parse_hms.js +1 -1
  211. package/src/parse_http_content_range.js +9 -5
  212. package/src/parse_http_content_range.md +16 -0
  213. package/src/perf_end_human.md +10 -0
  214. package/src/perf_end_ms.md +8 -0
  215. package/src/perf_measure.md +10 -0
  216. package/src/perf_measure_human.md +8 -0
  217. package/src/perf_start.md +9 -0
  218. package/src/pgid_exists.md +6 -0
  219. package/src/pgid_kill_grace.js +1 -1
  220. package/src/pgid_kill_grace.md +15 -0
  221. package/src/pid_exists.md +5 -0
  222. package/src/pid_kill_grace.js +1 -1
  223. package/src/pid_kill_grace.md +14 -0
  224. package/src/plural.md +13 -0
  225. package/src/random_hex.md +9 -0
  226. package/src/random_int.md +8 -0
  227. package/src/range.js +4 -2
  228. package/src/range.md +2 -0
  229. package/src/redis_connect.js +1 -1
  230. package/src/redis_connect.md +10 -0
  231. package/src/redis_lshift.md +14 -0
  232. package/src/redis_poll.js +1 -0
  233. package/src/redis_poll.md +17 -0
  234. package/src/redis_poll_zshift_callback_rpush.js +1 -2
  235. package/src/redis_poll_zshift_callback_rpush.md +17 -0
  236. package/src/redis_poll_zshift_spawn_rpush.js +2 -1
  237. package/src/redis_poll_zshift_spawn_rpush.md +20 -0
  238. package/src/redis_zshift.md +14 -0
  239. package/src/round.js +7 -8
  240. package/src/round.md +15 -0
  241. package/src/sanitize_dash_name.md +13 -0
  242. package/src/sanitize_filename.md +9 -0
  243. package/src/sanitize_var_name.md +14 -0
  244. package/src/sftp_get_file_info.js +2 -2
  245. package/src/sftp_get_file_info.md +14 -0
  246. package/src/sftp_get_stream_range.js +32 -22
  247. package/src/sftp_get_stream_range.md +20 -0
  248. package/src/shell_curl_progress.js +2 -1
  249. package/src/shell_curl_progress.md +17 -0
  250. package/src/shell_lines.js +2 -2
  251. package/src/shell_lines.md +1 -1
  252. package/src/shell_spawn.js +7 -0
  253. package/src/shell_ytdlp_progress.js +2 -1
  254. package/src/shell_ytdlp_progress.md +17 -0
  255. package/src/str_camel_to_snake.md +11 -0
  256. package/src/str_parse_kv.md +11 -0
  257. package/src/stream_curl_progress.md +14 -0
  258. package/src/stream_data_ln.js +7 -1
  259. package/src/stream_discard.md +8 -0
  260. package/src/stream_group.md +3 -0
  261. package/src/stream_gunzip.md +7 -0
  262. package/src/stream_hash.md +10 -0
  263. package/src/stream_map.js +8 -1
  264. package/src/stream_map_parallel.js +9 -1
  265. package/src/stream_md5.md +9 -0
  266. package/src/stream_multiplex.js +11 -0
  267. package/src/stream_multiplex.md +16 -0
  268. package/src/stream_through.md +3 -0
  269. package/src/stream_transform.md +2 -0
  270. package/src/stream_xml_analyze.js +9 -1
  271. package/src/stream_xml_parse.js +9 -1
  272. package/src/stream_ytdlp_progress.js +1 -1
  273. package/src/stream_ytdlp_progress.md +18 -0
  274. package/src/strtr.js +15 -5
  275. package/src/strtr.md +11 -0
  276. package/src/urlclean.js +9 -2
  277. package/src/urlmod.js +5 -0
  278. package/src/urlparts.md +17 -0
  279. package/src/wait_while.js +1 -1
  280. package/src/wait_while.md +1 -1
  281. package/src/HeartbeatServer.test.js +0 -31
  282. package/src/array_chunk.test.js +0 -24
  283. package/src/array_chunk_balanced.test.js +0 -37
  284. package/src/array_fps.test.js +0 -72
  285. package/src/array_gcd.test.js +0 -10
  286. package/src/array_index.test.js +0 -14
  287. package/src/array_lcm.test.js +0 -12
  288. package/src/array_max.test.js +0 -20
  289. package/src/array_min.test.js +0 -20
  290. package/src/array_permutations.test.js +0 -20
  291. package/src/array_sort.test.js +0 -85
  292. package/src/array_sort_other.test.js +0 -9
  293. package/src/array_sum.test.js +0 -11
  294. package/src/cli-apps/watchdog.d/connect_close.js +0 -17
  295. package/src/cli-apps/watchdog.d/connect_close2.js +0 -20
  296. package/src/cli-apps/watchdog.d/connect_close3.js +0 -25
  297. package/src/cli-apps/watchdog.d/connect_no_ping.js +0 -13
  298. package/src/cli-apps/watchdog.d/early_exit.js +0 -7
  299. package/src/cli-apps/watchdog.d/flood.js +0 -13
  300. package/src/cli-apps/watchdog.d/flood2.js +0 -15
  301. package/src/cli-apps/watchdog.d/hang.js +0 -8
  302. package/src/cli-apps/watchdog.d/huge_delay.js +0 -18
  303. package/src/cli-apps/watchdog.d/ignore_sigterm.js +0 -29
  304. package/src/cli-apps/watchdog.d/late_heartbeat.js +0 -21
  305. package/src/cli-apps/watchdog.d/my-exit-after-2-pings.js +0 -16
  306. package/src/cli-apps/watchdog.d/my-happy-path.js +0 -16
  307. package/src/cli-apps/watchdog.d/my-ignore-sigterm.js +0 -22
  308. package/src/cli-apps/watchdog.d/my-never-ping.js +0 -15
  309. package/src/cli-apps/watchdog.d/no_heartbeat.js +0 -5
  310. package/src/cli-apps/watchdog.d/ok.js +0 -17
  311. package/src/cli-apps/watchdog.d/quick-exit-after-2-pings.js +0 -15
  312. package/src/cli-apps/watchdog.d/quick-hang-after-1-ping.js +0 -15
  313. package/src/cli-apps/watchdog.d/spawn_children.js +0 -14
  314. package/src/cli-apps/watchdog.test.js +0 -48
  315. package/src/crypto_encrypt_aes256.test.js +0 -10
  316. package/src/crypto_hash_sha256.test.js +0 -10
  317. package/src/date_add_months.test.js +0 -40
  318. package/src/date_is_leap_year.test.js +0 -14
  319. package/src/escape_content_disposition.test.js +0 -22
  320. package/src/fcmp_from_spec.test.js +0 -91
  321. package/src/fcmpx.test.js +0 -173
  322. package/src/filter1_from_spec.test.js +0 -31
  323. package/src/format_bytes.test.js +0 -22
  324. package/src/format_error_report.d/src/1.js +0 -26
  325. package/src/format_error_report.d/src/2.js +0 -29
  326. package/src/format_kilo.test.js +0 -25
  327. package/src/format_ms.test.js +0 -33
  328. package/src/format_ms2.test.js +0 -33
  329. package/src/format_ms3.test.js +0 -33
  330. package/src/format_percents.test.js +0 -22
  331. package/src/format_progress_bytes.test.js +0 -19
  332. package/src/format_progress_kilo.test.js +0 -19
  333. package/src/format_seconds.test.js +0 -42
  334. package/src/format_thousands.test.js +0 -28
  335. package/src/fs_file_md5.test.js +0 -11
  336. package/src/fs_mkdirp.test.js +0 -24
  337. package/src/fs_path_extname.test.js +0 -22
  338. package/src/gettype.test.js +0 -27
  339. package/src/object_schema.test.js +0 -15
  340. package/src/object_walk_preorder.test.js +0 -17
  341. package/src/parallel.test.js +0 -11
  342. package/src/parse_bytes.test.js +0 -21
  343. package/src/parse_hms.test.js +0 -32
  344. package/src/parse_http_content_range.test.js +0 -14
  345. package/src/pid_kill_grace.d/ignore-sigterm-for-10ms.js +0 -33
  346. package/src/pid_kill_grace.d/ignore-sigterm.js +0 -22
  347. package/src/pid_kill_grace.test.js +0 -63
  348. package/src/ping_socket.test.js +0 -58
  349. package/src/plural.test.js +0 -20
  350. package/src/range.test.js +0 -41
  351. package/src/redis_lshift.test.js +0 -57
  352. package/src/redis_zshift.test.js +0 -58
  353. package/src/round.test.js +0 -56
  354. package/src/sanitize_dash_name.test.js +0 -59
  355. package/src/sanitize_var_name.test.js +0 -39
  356. package/src/shell_spawn.test.js +0 -30
  357. package/src/str_camel_to_snake.test.js +0 -17
  358. package/src/stream_curl_progress.d/basic +0 -3
  359. package/src/stream_curl_progress.d/basic.json +0 -42
  360. package/src/stream_curl_progress.test.js +0 -17
  361. package/src/stream_data_ln.test.js +0 -24
  362. package/src/stream_map_parallel.test.js +0 -26
  363. package/src/stream_multiplex.test.js +0 -67
  364. package/src/stream_strpbrk.test.js +0 -29
  365. package/src/urlclean.test.js +0 -18
  366. package/src/urlmod.test.js +0 -40
  367. package/src/urlparts.test.js +0 -294
@@ -0,0 +1,2 @@
1
+ Thin wrapper over node's `path.dirname(p)`, kept for the consistent `fs_*` naming.
2
+ Returns the directory portion of the path.
@@ -0,0 +1,9 @@
1
+ Thin wrapper over node's `path.extname(p)`, kept for the consistent `fs_*` naming.
2
+ Returns the extension including the dot, or `''` when there is none — note node's dotfile rules:
3
+
4
+ ```js
5
+ fs_path_extname('file.js'); // '.js'
6
+ fs_path_extname('.foo'); // ''
7
+ fs_path_extname('..foo'); // '.foo'
8
+ fs_path_extname('/a/b.js/c'); // ''
9
+ ```
@@ -0,0 +1,2 @@
1
+ Thin wrapper over node's `path.join(...parts)`, kept for the consistent `fs_*` naming.
2
+ Joins and normalizes the given path segments.
@@ -0,0 +1,2 @@
1
+ Thin wrapper over node's `path.relative(from, to)`, kept for the consistent `fs_*` naming.
2
+ Returns the relative path from `from` to `to`.
@@ -0,0 +1,2 @@
1
+ Thin wrapper over node's `path.resolve(...parts)`, kept for the consistent `fs_*` naming.
2
+ Resolves the segments right-to-left into an absolute path (against the current working directory if needed).
@@ -0,0 +1,2 @@
1
+ Thin wrapper over node's `os.tmpdir()`, kept for the consistent `fs_*` naming.
2
+ Returns the OS default directory for temporary files, e.g. `/tmp`.
@@ -0,0 +1,8 @@
1
+ Expands a leading `~` in a path to the current user's home directory (ported from sindresorhus/untildify).
2
+ Only a `~` that is the whole path or is followed by `/` or `\` is expanded; anything else is returned unchanged.
3
+ If the home directory cannot be determined, the path is returned as-is.
4
+
5
+ ```js
6
+ fs_path_untildify('~/x'); // '/home/ubuntu/x'
7
+ fs_path_untildify('~foo/x'); // '~foo/x'
8
+ ```
package/src/fs_read.md ADDED
@@ -0,0 +1,3 @@
1
+ Thin wrapper over `fs.promises.readFile(path, options)`.
2
+ Without an `encoding` in `options` it resolves with a `Buffer`; with one, a string.
3
+ Prefer the specialized variants: `fs_read_buffer`, `fs_read_utf8`, `fs_read_json`, `fs_read_lines`.
@@ -0,0 +1,5 @@
1
+ Reads the entire contents of a file into a `Buffer` (i.e. `fs_read` with no encoding).
2
+
3
+ ```js
4
+ fs_read_buffer(file) → Promise<Buffer>
5
+ ```
@@ -0,0 +1,6 @@
1
+ Reads a file as UTF-8 and parses it with `JSON.parse`.
2
+ Rejects if the file is missing or the content is not valid JSON.
3
+
4
+ ```js
5
+ fs_read_json(file) → Promise<any>
6
+ ```
@@ -0,0 +1,7 @@
1
+ Reads a file as UTF-8 and splits it on `'\n'`.
2
+ The trailing newline is NOT stripped: a file ending in `\n` yields a final `''` element.
3
+
4
+ ```js
5
+ // file content: 'one\ntwo\n'
6
+ await fs_read_lines(file); // ['one', 'two', '']
7
+ ```
@@ -0,0 +1,2 @@
1
+ Thin wrapper over `fs.createReadStream(path, options)`.
2
+ Returns a readable stream synchronously (not a Promise); open errors are emitted as `'error'` events on the stream.
@@ -0,0 +1,5 @@
1
+ Reads the entire contents of a file into a UTF-8 decoded string.
2
+
3
+ ```js
4
+ fs_read_utf8(file) → Promise<string>
5
+ ```
@@ -0,0 +1,3 @@
1
+ Thin wrapper over `fs.promises.readdir(path, options)`.
2
+ Resolves with an array of basenames (not full paths); pass `{withFileTypes: true}` for `Dirent` objects.
3
+ Rejects if `path` is missing or not a directory.
@@ -0,0 +1,6 @@
1
+ Promisified `fs.readlink`: resolves with the target a symbolic link points to.
2
+ Rejects if the path is missing or not a symlink (`EINVAL`).
3
+
4
+ ```js
5
+ fs_readlink(path) → Promise<string>
6
+ ```
@@ -0,0 +1,6 @@
1
+ Promisified `fs.rename`: moves/renames a file or directory, overwriting an existing file at the destination.
2
+ Fails across filesystem boundaries (`EXDEV`) — copy + remove in that case.
3
+
4
+ ```js
5
+ fs_rename(old_path, new_path) → Promise<void>
6
+ ```
package/src/fs_rm.md ADDED
@@ -0,0 +1,4 @@
1
+ Removes a single file (thin wrapper over `fs.promises.unlink`).
2
+ Rejects if the path is missing (`ENOENT`) or is a directory.
3
+
4
+ See also: `fs_rmf` (ignore missing), `fs_rmdir` (empty directory), `fs_rmrf` (recursive, never throws on missing).
@@ -0,0 +1,3 @@
1
+ Removes an **empty** directory (thin wrapper over `fs.promises.rmdir`).
2
+ Rejects if the path is missing (`ENOENT`), not empty (`ENOTEMPTY`), or not a directory.
3
+ Use `fs_rmrf` to remove a directory with contents.
package/src/fs_rmf.md ADDED
@@ -0,0 +1,2 @@
1
+ Removes the file if it exists: `fs_rm` that swallows `ENOENT` (like `rm -f`).
2
+ Any other error (e.g. the path is a directory) is still thrown.
package/src/fs_rmrf.md ADDED
@@ -0,0 +1,8 @@
1
+ Removes a file or directory recursively, like `rm -rf`.
2
+ A missing path is silently ignored; children of a directory are removed in parallel.
3
+ Uses `lstat`, so a symlink is removed itself — its target is never followed.
4
+
5
+ **Signature:** `fs_rmrf(path, progress = ignore) → Promise<void>`
6
+
7
+ The optional `progress` callback is called as `progress('rm', path)` before each file
8
+ and `progress('rmdir', path)` before each directory removal.
@@ -0,0 +1,16 @@
1
+ const crypto = require('crypto');
2
+ const fs = require('fs');
3
+
4
+ async function fs_sha256(path)
5
+ {
6
+ const hash = crypto.createHash('sha256');
7
+
8
+ const highWaterMark = 1024*1024;
9
+ for await (const chunk of fs.createReadStream(path, {highWaterMark})) {
10
+ hash.update(chunk);
11
+ }
12
+
13
+ return hash.digest('hex');
14
+ }
15
+
16
+ module.exports = fs_sha256;
@@ -0,0 +1,9 @@
1
+ Computes the SHA-256 of a file, streaming it in 1 MiB chunks (constant
2
+ memory regardless of file size). Returns the digest as a lowercase hex
3
+ string. Rejects with `ENOENT` for a missing file; an empty file gives the
4
+ well-known empty-input digest.
5
+
6
+ ```js
7
+ await fs_sha256('/tmp/data.txt')
8
+ // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' ('hello')
9
+ ```
package/src/fs_size.js CHANGED
@@ -1,8 +1,8 @@
1
1
  const fs_stat = require('./fs_stat');
2
2
 
3
- async function fs_size(file)
3
+ async function fs_size(path)
4
4
  {
5
- const stat = await fs_stat(file);
5
+ const stat = await fs_stat(path);
6
6
  return stat.size;
7
7
  }
8
8
 
package/src/fs_size.md ADDED
@@ -0,0 +1,6 @@
1
+ Resolves with the size of a file in bytes (`fs_stat(path)` → `stat.size`).
2
+ Rejects if the path is missing; see `fs_size_enoent` for the variant that returns 0 instead.
3
+
4
+ ```js
5
+ fs_size(path) → Promise<number>
6
+ ```
package/src/fs_stat.md ADDED
@@ -0,0 +1,2 @@
1
+ Thin wrapper over `fs.promises.stat(path, options)`: resolves with an `fs.Stats` object.
2
+ Follows symlinks (use `fs_lstat` to stat the link itself); rejects with `ENOENT` if the path is missing.
package/src/fs_tempdir.js CHANGED
@@ -11,9 +11,16 @@ const mkdtemp = util.promisify(fs.mkdtemp);
11
11
  // * http://www.tldp.org/LDP/abs/html/exitcodes.html from https://stackoverflow.com/q/14031763/1478566
12
12
  // * https://www.exratione.com/2013/05/die-child-process-die/
13
13
 
14
+ // A single shared SIGINT listener no matter how many calls are in flight:
15
+ // one listener per call trips MaxListenersExceededWarning at 10+ concurrent calls.
16
+ const cleanups = new Set();
17
+
14
18
  async function fs_tempdir(fn)
15
19
  {
16
- process.on('SIGINT', sigint);
20
+ cleanups.add(clean);
21
+ if (cleanups.size === 1) {
22
+ process.on('SIGINT', sigint);
23
+ }
17
24
 
18
25
  let d = null, out;
19
26
  try {
@@ -26,17 +33,25 @@ async function fs_tempdir(fn)
26
33
  return out;
27
34
 
28
35
  async function clean() {
29
- process.removeListener('SIGINT', sigint);
36
+ if (!cleanups.delete(clean)) {
37
+ return;
38
+ }
39
+ if (cleanups.size === 0) {
40
+ process.removeListener('SIGINT', sigint);
41
+ }
30
42
  if (d) {
31
43
  await fs_rmrf(d);
32
44
  }
33
45
  }
46
+ }
34
47
 
35
- async function sigint() {
48
+ async function sigint()
49
+ {
50
+ for (const clean of [...cleanups]) {
36
51
  await clean().catch(function () {/* ignore */});
37
- // https://stackoverflow.com/questions/14031763/doing-a-cleanup-action-just-before-node-js-exits#comment68567869_21947851
38
- process.kill(process.pid, 'SIGINT');
39
52
  }
53
+ // https://stackoverflow.com/questions/14031763/doing-a-cleanup-action-just-before-node-js-exits#comment68567869_21947851
54
+ process.kill(process.pid, 'SIGINT');
40
55
  }
41
56
 
42
57
  module.exports = fs_tempdir;
@@ -0,0 +1,6 @@
1
+ Creates the file if it does not exist, then sets its atime and mtime to now — like the `touch` command.
2
+ Rejects with `Cannot touch a directory: <file>` when the path is an existing directory.
3
+
4
+ ```js
5
+ fs_touch(file) → Promise<void>
6
+ ```
package/src/fs_walk.js CHANGED
@@ -3,7 +3,7 @@ const fs_path_join = require('./fs_path_join');
3
3
  const fs_readdir = require('./fs_readdir');
4
4
  const ignore = require('./ignore');
5
5
 
6
- async function fs_walk({path = '.', user_friendly_status = ignore, error_handler = error_handler, signal})
6
+ async function fs_walk({path = '.', user_friendly_status = ignore, error_handler = default_error_handler, signal})
7
7
  {
8
8
  const out = [];
9
9
  const queue = [path];
@@ -32,7 +32,7 @@ async function fs_walk({path = '.', user_friendly_status = ignore, error_handler
32
32
  return out;
33
33
  }
34
34
 
35
- function error_handler(error)
35
+ function default_error_handler(error)
36
36
  {
37
37
  console.log(`⚠️ ${error.message}`);
38
38
  }
package/src/fs_walk.md ADDED
@@ -0,0 +1,14 @@
1
+ Walks a directory tree and resolves with a flat array of `lstat` results,
2
+ each with a `path` property added; the root entry is included. Order is not guaranteed.
3
+
4
+ **Options:** `fs_walk({path = '.', user_friendly_status, error_handler, signal})`
5
+ - `user_friendly_status(text)` — called with `Reading <path>` per entry
6
+ - `error_handler(error)` — per-entry errors are reported here and the walk continues (default logs to console)
7
+ - `signal` — an `AbortSignal` to cancel the walk
8
+
9
+ Symlinks are not followed (`lstat`), and the contents of `/dev` and `/proc` are skipped.
10
+
11
+ ```js
12
+ await fs_walk({path: 'w'});
13
+ // [Stats {path: 'w', ...}, Stats {path: 'w/sub', ...}, Stats {path: 'w/a.txt', ...}, ...]
14
+ ```
@@ -0,0 +1,3 @@
1
+ Thin wrapper over `fs.promises.writeFile(file, data, options)`.
2
+ Creates or truncates the file; `data` may be a string (written as UTF-8 unless `options` says otherwise), a `Buffer`, or a `TypedArray`.
3
+ For objects, prefer `fs_write_json`.
@@ -0,0 +1,7 @@
1
+ Serializes `data` with `JSON.stringify(data, null, 4)` and writes it to the file as UTF-8.
2
+ The output is pretty-printed with 4-space indentation and has no trailing newline.
3
+
4
+ ```js
5
+ await fs_write_json(file, {name: 'x'});
6
+ // file content: '{\n "name": "x"\n}'
7
+ ```
@@ -0,0 +1,2 @@
1
+ Thin wrapper over `fs.createWriteStream(path, options)`.
2
+ Returns a writable stream synchronously (not a Promise); open errors are emitted as `'error'` events on the stream.
package/src/gettype.md ADDED
@@ -0,0 +1,13 @@
1
+ Returns the type of a value as a string (see `const_type`). Like `typeof`,
2
+ but with the usual annoyances fixed: `null` is `'null'` (not `'object'`),
3
+ arrays are `'array'`, and the special numbers get their own names — `'nan'`,
4
+ `'-inf'`, `'+inf'`.
5
+
6
+ ```js
7
+ gettype(null) // 'null'
8
+ gettype([]) // 'array'
9
+ gettype(NaN) // 'nan'
10
+ gettype(-Infinity) // '-inf'
11
+ gettype(1n) // 'bigint'
12
+ gettype({}) // 'object'
13
+ ```
@@ -0,0 +1,11 @@
1
+ Sends a `DELETE` request via axios with `responseType: 'json'` and resolves
2
+ with `response.data`. Extra axios options pass through (and may override the
3
+ defaults).
4
+
5
+ ```js
6
+ await http_delete('https://api.example.com/items/15')
7
+ // → parsed response body
8
+ ```
9
+
10
+ Sibling of `http_get_json`, `http_post_json`, `http_put_json`,
11
+ `http_patch_json`.
@@ -0,0 +1,8 @@
1
+ Sends a `GET` request via axios with `responseType: 'blob'` and resolves with
2
+ `response.data` — a `Blob`. Meant for browser code; in Node prefer
3
+ `http_get_buffer` (Buffer) or `http_get_stream` (readable stream). Extra axios
4
+ options pass through.
5
+
6
+ ```js
7
+ const blob = await http_get_blob('https://example.com/image.png');
8
+ ```
@@ -0,0 +1,10 @@
1
+ Sends a `GET` request via axios with `responseType: 'arraybuffer'` and
2
+ resolves with `response.data` — a `Buffer` in Node, an `ArrayBuffer` in the
3
+ browser. Extra axios options pass through.
4
+
5
+ ```js
6
+ const buf = await http_get_buffer('https://example.com/image.png');
7
+ ```
8
+
9
+ See also `http_get_utf8` (string), `http_get_json` (parsed JSON),
10
+ `http_get_blob` (browser Blob), `http_get_stream` (readable stream).
@@ -1,17 +1,17 @@
1
1
  const axios = require('axios');
2
2
  const fs = require('fs');
3
3
  const stream = require('stream');
4
- const util = require('util');
5
-
6
- const stream_finished = util.promisify(stream.finished);
7
4
 
8
5
  // https://stackoverflow.com/a/61269447
9
- function http_get_file(url, out_file, options)
6
+ async function http_get_file(url, out_file, options)
10
7
  {
11
- const ws = fs.createWriteStream(out_file);
12
- return axios.get(url, {responseType: 'stream', ...options}).then(function (response) {
13
- return stream_finished(response.data.pipe(ws));
14
- });
8
+ // Create out_file only after a successful response: a failed request
9
+ // (e.g. 404) should not leave an empty file behind.
10
+ const response = await axios.get(url, {responseType: 'stream', ...options});
11
+ // pipeline destroys both streams on any failure: an aborted download
12
+ // rejects instead of hanging forever, an unwritable out_file rejects
13
+ // instead of raising an uncaught 'error' event.
14
+ await stream.promises.pipeline(response.data, fs.createWriteStream(out_file));
15
15
  }
16
16
 
17
17
  module.exports = http_get_file;
@@ -0,0 +1,14 @@
1
+ Downloads `url` into `out_file`.
2
+
3
+ - An HTTP error status (e.g. 404) rejects and does not create `out_file` —
4
+ the file is only created after a successful response.
5
+ - Rejects when the server aborts the download mid-stream (no hanging) and
6
+ when `out_file` is not writable; both streams are destroyed on any failure.
7
+ - `options` are passed through to `axios.get` (after
8
+ `responseType: 'stream'`).
9
+
10
+ ## Signature
11
+
12
+ ```
13
+ http_get_file(url, out_file, options) → Promise<void>
14
+ ```
@@ -0,0 +1,10 @@
1
+ Sends a `GET` request via axios with `responseType: 'json'` and resolves with
2
+ `response.data` — the parsed response body. Extra axios options (headers,
3
+ params, timeout, ...) pass through.
4
+
5
+ ```js
6
+ const user = await http_get_json('https://api.example.com/users/15');
7
+ ```
8
+
9
+ See also `http_get_utf8` (raw string), `http_get_buffer`/`http_get_blob`
10
+ (binary), `http_get_stream` (readable stream).
@@ -0,0 +1,14 @@
1
+ Sends a `GET` request via axios with `responseType: 'stream'` and resolves
2
+ with the response's readable stream, decorated with two extra properties:
3
+ `headers` (the response headers) and `total` (the `Content-Length` as an
4
+ integer, or `null` when the header is missing or unparsable). Extra axios
5
+ options pass through.
6
+
7
+ ```js
8
+ const stream = await http_get_stream('https://example.com/big.bin');
9
+ stream.total // 3000000 (or null)
10
+ stream.headers // response headers
11
+ stream.pipe(dest);
12
+ ```
13
+
14
+ See also `http_get_buffer` when the whole body fits in memory.
@@ -10,21 +10,39 @@ async function http_get_stream_range(url, first, last)
10
10
  const res = await axios.get(url, {responseType: 'stream', headers});
11
11
  const out = res.data;
12
12
  out.headers = res.headers;
13
- if (res.headers['content-range']) {
14
- out.content_range = parse_http_content_range(res.headers['content-range']);
13
+ try {
14
+ if (res.headers['content-range']) {
15
+ out.content_range = parse_http_content_range(res.headers['content-range']);
16
+ }
17
+ else if (res.headers['content-length']) {
18
+ const len = +res.headers['content-length'];
19
+ out.content_range = parse_http_content_range(`bytes 0-${len - 1}/${len}`);
20
+ }
21
+ else {
22
+ // Chunked 200 response: the body starts at byte 0, size unknown
23
+ out.content_range = {type: 'bytes', first: 0, last: null, total: null};
24
+ }
15
25
  }
16
- else {
17
- const len = res.headers['content-length'];
18
- out.content_range = parse_http_content_range(`${res.headers['accept-ranges']} 0-${len}/${len}`);
26
+ catch (error) {
27
+ out.destroy(); // release the socket; nobody will ever read this stream
28
+ throw error;
19
29
  }
20
- if (a && out.content_range.first !== a) {
21
- out.destroy(new Error(`First byte of a returned range (${format_thousands(out.content_range.first)}) is not as expected: [${format_thousands(a)}]`));
30
+ // A server reaching the end of the resource returns a shorter range than
31
+ // requested: `bytes=0-999999` of a 500-byte file is `bytes 0-499/500` (RFC 7233)
32
+ const expected_last = (b === '' || out.content_range.total === null) ? b : Math.min(b, out.content_range.total - 1);
33
+ if (a !== '' && out.content_range.first !== a) {
34
+ out.destroy(new Error(`First byte of a returned range (${fmt(out.content_range.first)}) is not as expected: [${fmt(a)}]`));
22
35
  }
23
- else if (b && out.content_range.last !== b) {
24
- out.destroy(new Error(`Last byte of a returned range (${format_thousands(out.content_range.last)}) is not as expected: [${format_thousands(b)}]`));
36
+ else if (b !== '' && out.content_range.last !== expected_last) {
37
+ out.destroy(new Error(`Last byte of a returned range (${fmt(out.content_range.last)}) is not as expected: [${fmt(expected_last)}]`));
25
38
  }
26
39
  out.total = out.content_range.total;
27
40
  return out;
28
41
  }
29
42
 
43
+ function fmt(v)
44
+ {
45
+ return v === null ? 'null' : format_thousands(v);
46
+ }
47
+
30
48
  module.exports = http_get_stream_range;
@@ -0,0 +1,22 @@
1
+ GETs `url` with a `Range: bytes=first-last` header and returns the response
2
+ body as a readable stream. Either bound may be omitted (non-integer) to send
3
+ an open-ended range; with both omitted a plain `bytes=-` request degrades to
4
+ a regular GET.
5
+
6
+ The stream gets extra properties attached: `headers` (raw response headers),
7
+ `content_range` (parsed `{type, first, last, total}`), and `total`. When the
8
+ server sends no `Content-Range`, it is synthesized from `Content-Length`; a
9
+ chunked 200 response (neither header) is accepted as
10
+ `{first: 0, last: null, total: null}` — body starts at byte 0, size unknown.
11
+
12
+ The returned range is validated: a `first` or `last` that differs from the
13
+ request destroys the stream with an error (reading it rejects) — this also
14
+ catches a server that ignores the range and replies 200. A `last` clamped to
15
+ the end of the resource is accepted: `bytes=0-999999` of a 500-byte file is
16
+ `bytes 0-499/500` (RFC 7233).
17
+
18
+ ```js
19
+ const rs = await http_get_stream_range(url, 100, 199);
20
+ rs.content_range // {type: 'bytes', first: 100, last: 199, total: 1000}
21
+ rs.total // 1000 (null when unknown)
22
+ ```
@@ -0,0 +1,9 @@
1
+ Sends a `GET` request via axios with `responseType: 'text'` and
2
+ `responseEncoding: 'utf8'`, and resolves with `response.data` — the body as a
3
+ UTF-8 string. Extra axios options pass through.
4
+
5
+ ```js
6
+ const html = await http_get_utf8('https://example.com/');
7
+ ```
8
+
9
+ See also `http_get_json` (parsed JSON) and `http_get_buffer` (binary).
@@ -0,0 +1,9 @@
1
+ Sends a `HEAD` request via axios and resolves with the raw response headers
2
+ (`response.request.res.headers` — the Node http headers object, keys
3
+ lowercased), not the body. Extra axios options pass through.
4
+
5
+ ```js
6
+ const headers = await http_head('https://example.com/file.bin');
7
+ headers['content-length'] // '3000000'
8
+ headers['content-type'] // 'application/octet-stream'
9
+ ```
@@ -0,0 +1,10 @@
1
+ Sends a `PATCH` request via axios with `body` as the request payload (a plain
2
+ object is serialized as JSON by axios) and resolves with `response.data`.
3
+ Extra axios options pass through.
4
+
5
+ ```js
6
+ await http_patch_json('https://api.example.com/items/15', {title: 'New title'})
7
+ // → parsed response body
8
+ ```
9
+
10
+ Sibling of `http_post_json`, `http_put_json`, `http_delete`.
@@ -0,0 +1,11 @@
1
+ Sends a `POST` request via axios with `body` as the request payload (a plain
2
+ object is serialized as JSON by axios) and resolves with `response.data`.
3
+ Extra axios options pass through.
4
+
5
+ ```js
6
+ await http_post_json('https://api.example.com/items', {title: 'Hello'})
7
+ // → parsed response body
8
+ ```
9
+
10
+ See also `http_post_urlencoded` and `http_post_multipart` for other request
11
+ body encodings.
@@ -0,0 +1,15 @@
1
+ Sends a `POST` request with a `multipart/form-data` body built from an array
2
+ of `{name, body, options}` items and resolves with `response.data`. `body` may
3
+ be a string, Buffer, or readable stream; `options` (e.g. `{filename}`) is
4
+ passed to `FormData#append` when present. In Node the `form-data` package is
5
+ used and its boundary headers are merged into `options.headers`; in the
6
+ browser (with `form-data` webpack-externed to the native `FormData`) the form
7
+ is posted as-is. Extra axios options pass through.
8
+
9
+ ```js
10
+ const items = [
11
+ {name: 'subject', body: 'Hello'},
12
+ {name: 'attachment', body: fs_read_stream(file), options: {filename: 'hello.txt'}},
13
+ ];
14
+ await http_post_multipart(url, items, {auth});
15
+ ```
@@ -0,0 +1,11 @@
1
+ Sends a `POST` request via axios with `body` serialized by Node's
2
+ `querystring.stringify` into an `application/x-www-form-urlencoded` string,
3
+ and resolves with `response.data`. Extra axios options (e.g. a `Content-Type`
4
+ header) pass through.
5
+
6
+ ```js
7
+ await http_post_urlencoded(url, {grant_type: 'refresh_token', refresh_token})
8
+ // body sent as 'grant_type=refresh_token&refresh_token=...'
9
+ ```
10
+
11
+ See also `http_post_json` and `http_post_multipart`.
@@ -0,0 +1,9 @@
1
+ Sends a `PUT` request via axios with `body` (a Buffer, or anything axios
2
+ accepts as a request body) sent as-is, and resolves with `response.data`.
3
+ Extra axios options — e.g. a `Content-Type` header — pass through.
4
+
5
+ ```js
6
+ await http_put_buffer(upload_url, buf, {headers: {'Content-Type': 'image/png'}});
7
+ ```
8
+
9
+ Sibling of `http_put_utf8` (string body) and `http_put_json` (JSON body).
@@ -32,13 +32,11 @@ async function http_put_file(url, file, options = {})
32
32
  chunks.push(chunk);
33
33
  });
34
34
  response.on('end', function () {
35
- switch (response.statusCode) {
36
- case 200:
35
+ if (response.statusCode >= 200 && response.statusCode < 300) {
37
36
  resolve({request, response, data: Buffer.concat(chunks)});
38
- break;
39
- default:
37
+ }
38
+ else {
40
39
  reject(new Error(`${response.statusCode} ${response.statusMessage}\n\n${Buffer.concat(chunks).toString('utf8')}`));
41
- break;
42
40
  }
43
41
  });
44
42
  });
@@ -53,7 +51,12 @@ async function http_put_file(url, file, options = {})
53
51
  prev = ready;
54
52
  });
55
53
  }
56
- fs.createReadStream(file).pipe(request);
54
+ const rs = fs.createReadStream(file);
55
+ rs.on('error', function (error) {
56
+ reject(error);
57
+ request.destroy();
58
+ });
59
+ rs.pipe(request);
57
60
  });
58
61
  }
59
62
 
@@ -0,0 +1,16 @@
1
+ PUTs a local file to `url` with an explicit `Content-Length` header (some
2
+ services, e.g. S3, reject `Transfer-Encoding: chunked`). Any 2xx status
3
+ resolves with `{request, response, data}` where `data` is the response body
4
+ as a Buffer; any other status rejects with an error containing the status
5
+ line and the response body. Errors reading the file reject too, even when
6
+ the request is already in flight.
7
+
8
+ **Options:**
9
+ - `headers` — extra request headers (`Content-Length` is always set).
10
+ - `progress_upload(delta, ready, total)` — called on socket drain.
11
+ - `progress_download(delta, ready, total)` — called per response chunk;
12
+ `total` is `null` without a response `Content-Length`.
13
+
14
+ ```js
15
+ const {response, data} = await http_put_file(signed_url, '/tmp/build.zip');
16
+ ```
@@ -0,0 +1,11 @@
1
+ Sends a `PUT` request via axios with `body` as the request payload (a plain
2
+ object is serialized as JSON by axios) and resolves with `response.data`.
3
+ Extra axios options pass through.
4
+
5
+ ```js
6
+ await http_put_json('https://api.example.com/items/15', {title: 'New title'})
7
+ // → parsed response body
8
+ ```
9
+
10
+ Sibling of `http_post_json`, `http_patch_json`, `http_put_buffer`,
11
+ `http_put_utf8`.