@socketsecurity/lib 5.0.0 → 5.0.2

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.0.2](https://github.com/SocketDev/socket-lib/releases/tag/v5.0.2) - 2025-12-15
9
+
10
+ ### Changed
11
+
12
+ - **signal-exit**: `signals()` now auto-initializes its internal state
13
+ - Commit: [`8cb0576`](https://github.com/SocketDev/socket-lib/commit/8cb0576)
14
+
15
+ ## [5.0.1](https://github.com/SocketDev/socket-lib/releases/tag/v5.0.1) - 2025-12-11
16
+
17
+ ### Added
18
+
19
+ - **http-request**: Enhanced `httpDownload()` with automatic progress logging via Logger integration
20
+ - New `logger` option: Pass a Logger instance for automatic progress tracking
21
+ - New `progressInterval` option: Configure progress reporting frequency (default: 10%)
22
+ - Progress format: `Progress: XX% (Y.Y MB / Z.Z MB)`
23
+ - `onProgress` callback takes precedence over `logger` when both are provided
24
+ - Commit: [`91e5db5`](https://github.com/SocketDev/socket-lib/commit/91e5db5)
25
+
8
26
  ## [5.0.0](https://github.com/SocketDev/socket-lib/releases/tag/v5.0.0) - 2025-12-04
9
27
 
10
28
  ### Added
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Socket Badge](https://socket.dev/api/badge/npm/package/@socketsecurity/lib)](https://socket.dev/npm/package/@socketsecurity/lib)
4
4
  [![CI](https://github.com/SocketDev/socket-lib/actions/workflows/ci.yml/badge.svg)](https://github.com/SocketDev/socket-lib/actions/workflows/ci.yml)
5
- ![Coverage](https://img.shields.io/badge/coverage-83.78%25-brightgreen)
5
+ ![Coverage](https://img.shields.io/badge/coverage-84.10%25-brightgreen)
6
6
 
7
7
  [![Follow @SocketSecurity](https://img.shields.io/twitter/follow/SocketSecurity?style=social)](https://twitter.com/SocketSecurity)
8
8
  [![Follow @socket.dev on Bluesky](https://img.shields.io/badge/Follow-@socket.dev-1DA1F2?style=social&logo=bluesky)](https://bsky.app/profile/socket.dev)
@@ -11,9 +11,9 @@ var __commonJS = (cb, mod) => function __require() {
11
11
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
12
  };
13
13
 
14
- // node_modules/.pnpm/libnpmexec@10.1.8/node_modules/libnpmexec/lib/get-bin-from-manifest.js
14
+ // node_modules/.pnpm/libnpmexec@10.1.11/node_modules/libnpmexec/lib/get-bin-from-manifest.js
15
15
  var require_get_bin_from_manifest = __commonJS({
16
- "node_modules/.pnpm/libnpmexec@10.1.8/node_modules/libnpmexec/lib/get-bin-from-manifest.js"(exports2, module2) {
16
+ "node_modules/.pnpm/libnpmexec@10.1.11/node_modules/libnpmexec/lib/get-bin-from-manifest.js"(exports2, module2) {
17
17
  var getBinFromManifest2 = /* @__PURE__ */ __name((mani) => {
18
18
  const bin = mani.bin || {};
19
19
  if (new Set(Object.values(bin)).size === 1) {
@@ -51565,33 +51565,6 @@ var require_vuln = __commonJS({
51565
51565
  }
51566
51566
  });
51567
51567
 
51568
- // node_modules/.pnpm/pacote@21.0.1/node_modules/pacote/lib/index.js
51569
- var require_lib36 = __commonJS({
51570
- "node_modules/.pnpm/pacote@21.0.1/node_modules/pacote/lib/index.js"(exports2, module2) {
51571
- var { get } = require_fetcher2();
51572
- var GitFetcher = require_git();
51573
- var RegistryFetcher = require_registry();
51574
- var FileFetcher = require_file();
51575
- var DirFetcher = require_dir();
51576
- var RemoteFetcher = require_remote2();
51577
- var tarball = /* @__PURE__ */ __name((spec, opts) => get(spec, opts).tarball(), "tarball");
51578
- tarball.stream = (spec, handler, opts) => get(spec, opts).tarballStream(handler);
51579
- tarball.file = (spec, dest, opts) => get(spec, opts).tarballFile(dest);
51580
- module2.exports = {
51581
- GitFetcher,
51582
- RegistryFetcher,
51583
- FileFetcher,
51584
- DirFetcher,
51585
- RemoteFetcher,
51586
- resolve: /* @__PURE__ */ __name((spec, opts) => get(spec, opts).resolve(), "resolve"),
51587
- extract: /* @__PURE__ */ __name((spec, dest, opts) => get(spec, opts).extract(dest), "extract"),
51588
- manifest: /* @__PURE__ */ __name((spec, opts) => get(spec, opts).manifest(), "manifest"),
51589
- packument: /* @__PURE__ */ __name((spec, opts) => get(spec, opts).packument(), "packument"),
51590
- tarball
51591
- };
51592
- }
51593
- });
51594
-
51595
51568
  // node_modules/.pnpm/@npmcli+metavuln-calculator@9.0.2/node_modules/@npmcli/metavuln-calculator/lib/hash.js
51596
51569
  var require_hash = __commonJS({
51597
51570
  "node_modules/.pnpm/@npmcli+metavuln-calculator@9.0.2/node_modules/@npmcli/metavuln-calculator/lib/hash.js"(exports2, module2) {
@@ -51918,9 +51891,9 @@ var require_advisory = __commonJS({
51918
51891
  });
51919
51892
 
51920
51893
  // node_modules/.pnpm/@npmcli+metavuln-calculator@9.0.2/node_modules/@npmcli/metavuln-calculator/lib/index.js
51921
- var require_lib37 = __commonJS({
51894
+ var require_lib36 = __commonJS({
51922
51895
  "node_modules/.pnpm/@npmcli+metavuln-calculator@9.0.2/node_modules/@npmcli/metavuln-calculator/lib/index.js"(exports2, module2) {
51923
- var pacote2 = require_lib36();
51896
+ var pacote2 = require_lib32();
51924
51897
  var cacache2 = require_lib18();
51925
51898
  var { time } = require_lib2();
51926
51899
  var Advisory = require_advisory();
@@ -52037,7 +52010,7 @@ var require_audit_report = __commonJS({
52037
52010
  var npa = require_npa2();
52038
52011
  var pickManifest = require_lib30();
52039
52012
  var Vuln = require_vuln();
52040
- var Calculator = require_lib37();
52013
+ var Calculator = require_lib36();
52041
52014
  var { log, time } = require_lib2();
52042
52015
  var npmFetch = require_lib28();
52043
52016
  var AuditReport = class _AuditReport extends Map {
@@ -52535,7 +52508,7 @@ var require_tracker = __commonJS({
52535
52508
  });
52536
52509
 
52537
52510
  // node_modules/.pnpm/proggy@3.0.0/node_modules/proggy/lib/index.js
52538
- var require_lib38 = __commonJS({
52511
+ var require_lib37 = __commonJS({
52539
52512
  "node_modules/.pnpm/proggy@3.0.0/node_modules/proggy/lib/index.js"(exports2) {
52540
52513
  exports2.Client = require_client4();
52541
52514
  exports2.Tracker = require_tracker();
@@ -52557,7 +52530,7 @@ var require_lib38 = __commonJS({
52557
52530
  // node_modules/.pnpm/@npmcli+arborist@9.1.6/node_modules/@npmcli/arborist/lib/tracker.js
52558
52531
  var require_tracker2 = __commonJS({
52559
52532
  "node_modules/.pnpm/@npmcli+arborist@9.1.6/node_modules/@npmcli/arborist/lib/tracker.js"(exports2, module2) {
52560
- var proggy = require_lib38();
52533
+ var proggy = require_lib37();
52561
52534
  module2.exports = (cls) => class Tracker extends cls {
52562
52535
  static {
52563
52536
  __name(this, "Tracker");
@@ -53381,7 +53354,7 @@ var require_to_batch_syntax = __commonJS({
53381
53354
  });
53382
53355
 
53383
53356
  // node_modules/.pnpm/cmd-shim@7.0.0/node_modules/cmd-shim/lib/index.js
53384
- var require_lib39 = __commonJS({
53357
+ var require_lib38 = __commonJS({
53385
53358
  "node_modules/.pnpm/cmd-shim@7.0.0/node_modules/cmd-shim/lib/index.js"(exports2, module2) {
53386
53359
  var {
53387
53360
  chmod,
@@ -53538,7 +53511,7 @@ exit $LASTEXITCODE
53538
53511
  });
53539
53512
 
53540
53513
  // node_modules/.pnpm/read-cmd-shim@5.0.0/node_modules/read-cmd-shim/lib/index.js
53541
- var require_lib40 = __commonJS({
53514
+ var require_lib39 = __commonJS({
53542
53515
  "node_modules/.pnpm/read-cmd-shim@5.0.0/node_modules/read-cmd-shim/lib/index.js"(exports2, module2) {
53543
53516
  var fs = require("fs");
53544
53517
  var { promisify } = require("util");
@@ -53885,7 +53858,7 @@ var require_cjs2 = __commonJS({
53885
53858
  });
53886
53859
 
53887
53860
  // node_modules/.pnpm/write-file-atomic@6.0.0/node_modules/write-file-atomic/lib/index.js
53888
- var require_lib41 = __commonJS({
53861
+ var require_lib40 = __commonJS({
53889
53862
  "node_modules/.pnpm/write-file-atomic@6.0.0/node_modules/write-file-atomic/lib/index.js"(exports2, module2) {
53890
53863
  "use strict";
53891
53864
  module2.exports = writeFile;
@@ -54124,7 +54097,7 @@ var require_fix_bin = __commonJS({
54124
54097
  readFile
54125
54098
  } = require("fs/promises");
54126
54099
  var execMode = 511 & ~process.umask();
54127
- var writeFileAtomic = require_lib41();
54100
+ var writeFileAtomic = require_lib40();
54128
54101
  var isWindowsHashBang = /* @__PURE__ */ __name((buf) => buf[0] === "#".charCodeAt(0) && buf[1] === "!".charCodeAt(0) && /^#![^\n]+\r\n/.test(buf.toString()), "isWindowsHashBang");
54129
54102
  var isWindowsHashbangFile = /* @__PURE__ */ __name((file) => {
54130
54103
  const FALSE = /* @__PURE__ */ __name(() => false, "FALSE");
@@ -54156,8 +54129,8 @@ var require_shim_bin = __commonJS({
54156
54129
  throw er;
54157
54130
  }
54158
54131
  }, "throwNonEnoent");
54159
- var cmdShim = require_lib39();
54160
- var readCmdShim = require_lib40();
54132
+ var cmdShim = require_lib38();
54133
+ var readCmdShim = require_lib39();
54161
54134
  var fixBin = require_fix_bin();
54162
54135
  var seen = /* @__PURE__ */ new Set();
54163
54136
  var failEEXIST = /* @__PURE__ */ __name(({ to, from }) => Promise.reject(Object.assign(new Error("EEXIST: file already exists"), {
@@ -54389,7 +54362,7 @@ var require_check_bin = __commonJS({
54389
54362
  var isWindows = require_is_windows();
54390
54363
  var binTarget = require_bin_target();
54391
54364
  var { resolve, dirname } = require("path");
54392
- var readCmdShim = require_lib40();
54365
+ var readCmdShim = require_lib39();
54393
54366
  var { readlink } = require("fs/promises");
54394
54367
  var checkBin = /* @__PURE__ */ __name(async ({ bin, path, top, global: global2, force }) => {
54395
54368
  if (force || !global2 || !top) {
@@ -54496,7 +54469,7 @@ var require_get_paths = __commonJS({
54496
54469
  });
54497
54470
 
54498
54471
  // node_modules/.pnpm/bin-links@5.0.0/node_modules/bin-links/lib/index.js
54499
- var require_lib42 = __commonJS({
54472
+ var require_lib41 = __commonJS({
54500
54473
  "node_modules/.pnpm/bin-links@5.0.0/node_modules/bin-links/lib/index.js"(exports2, module2) {
54501
54474
  var linkBins = require_link_bins();
54502
54475
  var linkMans = require_link_mans();
@@ -59258,7 +59231,7 @@ var require_dist14 = __commonJS({
59258
59231
  });
59259
59232
 
59260
59233
  // node_modules/.pnpm/@npmcli+query@4.0.1/node_modules/@npmcli/query/lib/index.js
59261
- var require_lib43 = __commonJS({
59234
+ var require_lib42 = __commonJS({
59262
59235
  "node_modules/.pnpm/@npmcli+query@4.0.1/node_modules/@npmcli/query/lib/index.js"(exports2, module2) {
59263
59236
  "use strict";
59264
59237
  var parser = require_dist14();
@@ -59457,7 +59430,7 @@ var require_query_selector_all = __commonJS({
59457
59430
  "node_modules/.pnpm/@npmcli+arborist@9.1.6/node_modules/@npmcli/arborist/lib/query-selector-all.js"(exports2, module2) {
59458
59431
  "use strict";
59459
59432
  var { resolve } = require("node:path");
59460
- var { parser, arrayDelimiter } = require_lib43();
59433
+ var { parser, arrayDelimiter } = require_lib42();
59461
59434
  var localeCompare = require_string_locale_compare()("en");
59462
59435
  var { log } = require_lib2();
59463
59436
  var { minimatch } = require_commonjs4();
@@ -60197,7 +60170,7 @@ var require_node4 = __commonJS({
60197
60170
  var npa = require_npa2();
60198
60171
  var semver = require_semver2();
60199
60172
  var util = require("node:util");
60200
- var { getPaths: getBinPaths } = require_lib42();
60173
+ var { getPaths: getBinPaths } = require_lib41();
60201
60174
  var { log } = require_lib2();
60202
60175
  var { resolve, relative, dirname, basename } = require("node:path");
60203
60176
  var { walkUp } = require_commonjs15();
@@ -62511,7 +62484,7 @@ var require_just_diff_apply = __commonJS({
62511
62484
  });
62512
62485
 
62513
62486
  // node_modules/.pnpm/parse-conflict-json@4.0.0/node_modules/parse-conflict-json/lib/index.js
62514
- var require_lib44 = __commonJS({
62487
+ var require_lib43 = __commonJS({
62515
62488
  "node_modules/.pnpm/parse-conflict-json@4.0.0/node_modules/parse-conflict-json/lib/index.js"(exports2, module2) {
62516
62489
  var parseJSON = require_lib();
62517
62490
  var { diff } = require_just_diff();
@@ -62658,7 +62631,7 @@ var require_shrinkwrap = __commonJS({
62658
62631
  var versionFromTgz = require_version_from_tgz();
62659
62632
  var npa = require_npa2();
62660
62633
  var pkgJson = require_lib12();
62661
- var parseJSON = require_lib44();
62634
+ var parseJSON = require_lib43();
62662
62635
  var nameFromFolder = require_lib34();
62663
62636
  var stringify = require_json_stringify_nice();
62664
62637
  var swKeyOrder = [
@@ -65236,7 +65209,7 @@ var require_promise_all_reject_late = __commonJS({
65236
65209
  var require_rebuild = __commonJS({
65237
65210
  "node_modules/.pnpm/@npmcli+arborist@9.1.6/node_modules/@npmcli/arborist/lib/arborist/rebuild.js"(exports2, module2) {
65238
65211
  var PackageJson = require_lib12();
65239
- var binLinks = require_lib42();
65212
+ var binLinks = require_lib41();
65240
65213
  var localeCompare = require_string_locale_compare()("en");
65241
65214
  var promiseAllRejectLate = require_promise_all_reject_late();
65242
65215
  var runScript = require_run_script();
@@ -67540,7 +67513,7 @@ var require_arborist = __commonJS({
67540
67513
  });
67541
67514
 
67542
67515
  // node_modules/.pnpm/@npmcli+arborist@9.1.6/node_modules/@npmcli/arborist/lib/index.js
67543
- var require_lib45 = __commonJS({
67516
+ var require_lib44 = __commonJS({
67544
67517
  "node_modules/.pnpm/@npmcli+arborist@9.1.6/node_modules/@npmcli/arborist/lib/index.js"(exports2, module2) {
67545
67518
  module2.exports = require_arborist();
67546
67519
  module2.exports.Arborist = module2.exports;
@@ -67552,14 +67525,14 @@ var require_lib45 = __commonJS({
67552
67525
  });
67553
67526
 
67554
67527
  // node_modules/.pnpm/libnpmpack@9.0.9/node_modules/libnpmpack/lib/index.js
67555
- var require_lib46 = __commonJS({
67528
+ var require_lib45 = __commonJS({
67556
67529
  "node_modules/.pnpm/libnpmpack@9.0.9/node_modules/libnpmpack/lib/index.js"(exports2, module2) {
67557
67530
  "use strict";
67558
67531
  var pacote2 = require_lib32();
67559
67532
  var npa = require_npa2();
67560
67533
  var runScript = require_run_script();
67561
67534
  var path = require("node:path");
67562
- var Arborist2 = require_lib45();
67535
+ var Arborist2 = require_lib44();
67563
67536
  var { writeFile } = require("node:fs/promises");
67564
67537
  module2.exports = pack;
67565
67538
  async function pack(spec = "file:.", opts = {}) {
@@ -67607,7 +67580,7 @@ var require_lib46 = __commonJS({
67607
67580
 
67608
67581
  // src/external/npm-pack.js
67609
67582
  var { get: pacoteFetcherGet } = require_fetcher2();
67610
- var libnpmpack = require_lib46();
67583
+ var libnpmpack = require_lib45();
67611
67584
  var cacacheGet = require_get();
67612
67585
  var cacachePut = require_put();
67613
67586
  var cacacheRm = require_rm2();
package/dist/fs.d.ts CHANGED
@@ -578,51 +578,63 @@ export declare function readJson(filepath: PathLike, options?: ReadJsonOptions |
578
578
  export declare function readJsonSync(filepath: PathLike, options?: ReadJsonOptions | string | undefined): import("./json/types").JsonValue;
579
579
  /**
580
580
  * Safely delete a file or directory asynchronously with built-in protections.
581
- * Uses `del` for safer deletion that prevents removing cwd and above by default.
582
- * Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories.
581
+ *
582
+ * Uses [`del`](https://socket.dev/npm/package/del/overview/8.0.1) for safer deletion with these safety features:
583
+ * - By default, prevents deleting the current working directory (cwd) and above
584
+ * - Allows deleting within cwd (descendant paths) without force option
585
+ * - Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories
586
+ * - Protects against accidental deletion of parent directories via `../` paths
583
587
  *
584
588
  * @param filepath - Path or array of paths to delete (supports glob patterns)
585
589
  * @param options - Deletion options including force, retries, and recursion
590
+ * @param options.force - Set to true to allow deleting cwd and above (use with caution)
586
591
  * @throws {Error} When attempting to delete protected paths without force option
587
592
  *
588
593
  * @example
589
594
  * ```ts
590
- * // Delete a single file
591
- * await safeDelete('./temp-file.txt')
592
- *
593
- * // Delete a directory recursively
594
- * await safeDelete('./build', { recursive: true })
595
+ * // Delete files within cwd (safe by default)
596
+ * await safeDelete('./build')
597
+ * await safeDelete('./dist')
595
598
  *
596
- * // Delete multiple paths
597
- * await safeDelete(['./dist', './coverage'])
599
+ * // Delete with glob patterns
600
+ * await safeDelete(['./temp/**', '!./temp/keep.txt'])
598
601
  *
599
602
  * // Delete with custom retry settings
600
603
  * await safeDelete('./flaky-dir', { maxRetries: 5, retryDelay: 500 })
604
+ *
605
+ * // Force delete cwd or above (requires explicit force: true)
606
+ * await safeDelete('../parent-dir', { force: true })
601
607
  * ```
602
608
  */
603
609
  export declare function safeDelete(filepath: PathLike | PathLike[], options?: RemoveOptions | undefined): Promise<void>;
604
610
  /**
605
611
  * Safely delete a file or directory synchronously with built-in protections.
606
- * Uses `del` for safer deletion that prevents removing cwd and above by default.
607
- * Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories.
612
+ *
613
+ * Uses [`del`](https://socket.dev/npm/package/del/overview/8.0.1) for safer deletion with these safety features:
614
+ * - By default, prevents deleting the current working directory (cwd) and above
615
+ * - Allows deleting within cwd (descendant paths) without force option
616
+ * - Automatically uses force: true for temp directory, cacache, and ~/.socket subdirectories
617
+ * - Protects against accidental deletion of parent directories via `../` paths
608
618
  *
609
619
  * @param filepath - Path or array of paths to delete (supports glob patterns)
610
620
  * @param options - Deletion options including force, retries, and recursion
621
+ * @param options.force - Set to true to allow deleting cwd and above (use with caution)
611
622
  * @throws {Error} When attempting to delete protected paths without force option
612
623
  *
613
624
  * @example
614
625
  * ```ts
615
- * // Delete a single file
616
- * safeDeleteSync('./temp-file.txt')
626
+ * // Delete files within cwd (safe by default)
627
+ * safeDeleteSync('./build')
628
+ * safeDeleteSync('./dist')
617
629
  *
618
- * // Delete a directory recursively
619
- * safeDeleteSync('./build', { recursive: true })
630
+ * // Delete with glob patterns
631
+ * safeDeleteSync(['./temp/**', '!./temp/keep.txt'])
620
632
  *
621
- * // Delete multiple paths with globs
622
- * safeDeleteSync(['./dist/**', './coverage/**'])
633
+ * // Delete multiple paths
634
+ * safeDeleteSync(['./coverage', './reports'])
623
635
  *
624
- * // Force delete a protected path (use with caution)
625
- * safeDeleteSync('./important', { force: true })
636
+ * // Force delete cwd or above (requires explicit force: true)
637
+ * safeDeleteSync('../parent-dir', { force: true })
626
638
  * ```
627
639
  */
628
640
  export declare function safeDeleteSync(filepath: PathLike | PathLike[], options?: RemoveOptions | undefined): void;
@@ -1,3 +1,4 @@
1
+ import type { Logger } from './logger.js';
1
2
  /**
2
3
  * Configuration options for HTTP/HTTPS requests.
3
4
  */
@@ -270,9 +271,31 @@ export interface HttpDownloadOptions {
270
271
  * ```
271
272
  */
272
273
  headers?: Record<string, string> | undefined;
274
+ /**
275
+ * Logger instance for automatic progress logging.
276
+ * When provided with `progressInterval`, will automatically log download progress.
277
+ * If both `onProgress` and `logger` are provided, `onProgress` takes precedence.
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * import { getDefaultLogger } from '@socketsecurity/lib/logger'
282
+ *
283
+ * const logger = getDefaultLogger()
284
+ * await httpDownload('https://example.com/file.zip', '/tmp/file.zip', {
285
+ * logger,
286
+ * progressInterval: 10 // Log every 10%
287
+ * })
288
+ * // Output:
289
+ * // Progress: 10% (5.2 MB / 52.0 MB)
290
+ * // Progress: 20% (10.4 MB / 52.0 MB)
291
+ * // ...
292
+ * ```
293
+ */
294
+ logger?: Logger | undefined;
273
295
  /**
274
296
  * Callback for tracking download progress.
275
297
  * Called periodically as data is received.
298
+ * Takes precedence over `logger` if both are provided.
276
299
  *
277
300
  * @param downloaded - Number of bytes downloaded so far
278
301
  * @param total - Total file size in bytes (from Content-Length header)
@@ -288,6 +311,29 @@ export interface HttpDownloadOptions {
288
311
  * ```
289
312
  */
290
313
  onProgress?: ((downloaded: number, total: number) => void) | undefined;
314
+ /**
315
+ * Progress reporting interval as a percentage (0-100).
316
+ * Only used when `logger` is provided.
317
+ * Progress will be logged each time the download advances by this percentage.
318
+ *
319
+ * @default 10
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * // Log every 10%
324
+ * await httpDownload('https://example.com/file.zip', '/tmp/file.zip', {
325
+ * logger: getDefaultLogger(),
326
+ * progressInterval: 10
327
+ * })
328
+ *
329
+ * // Log every 25%
330
+ * await httpDownload('https://example.com/file.zip', '/tmp/file.zip', {
331
+ * logger: getDefaultLogger(),
332
+ * progressInterval: 25
333
+ * })
334
+ * ```
335
+ */
336
+ progressInterval?: number | undefined;
291
337
  /**
292
338
  * Number of retry attempts for failed downloads.
293
339
  * Uses exponential backoff: delay = `retryDelay` * 2^attempt.
@@ -184,17 +184,34 @@ async function httpRequestAttempt(url, options) {
184
184
  async function httpDownload(url, destPath, options) {
185
185
  const {
186
186
  headers = {},
187
+ logger,
187
188
  onProgress,
189
+ progressInterval = 10,
188
190
  retries = 0,
189
191
  retryDelay = 1e3,
190
192
  timeout = 12e4
191
193
  } = { __proto__: null, ...options };
194
+ let progressCallback;
195
+ if (onProgress) {
196
+ progressCallback = onProgress;
197
+ } else if (logger) {
198
+ let lastPercent = 0;
199
+ progressCallback = (downloaded, total) => {
200
+ const percent = Math.floor(downloaded / total * 100);
201
+ if (percent >= lastPercent + progressInterval) {
202
+ logger.log(
203
+ ` Progress: ${percent}% (${(downloaded / 1024 / 1024).toFixed(1)} MB / ${(total / 1024 / 1024).toFixed(1)} MB)`
204
+ );
205
+ lastPercent = percent;
206
+ }
207
+ };
208
+ }
192
209
  let lastError;
193
210
  for (let attempt = 0; attempt <= retries; attempt++) {
194
211
  try {
195
212
  return await httpDownloadAttempt(url, destPath, {
196
213
  headers,
197
- onProgress,
214
+ onProgress: progressCallback,
198
215
  timeout
199
216
  });
200
217
  } catch (e) {
package/dist/json/edit.js CHANGED
@@ -22,6 +22,7 @@ __export(edit_exports, {
22
22
  getEditableJsonClass: () => getEditableJsonClass
23
23
  });
24
24
  module.exports = __toCommonJS(edit_exports);
25
+ var import_promises = require("node:timers/promises");
25
26
  var import_format = require("./format");
26
27
  const identSymbol = import_format.INDENT_SYMBOL;
27
28
  const newlineSymbol = import_format.NEWLINE_SYMBOL;
@@ -40,15 +41,31 @@ async function retryWrite(filepath, content, retries = 3, baseDelay = 10) {
40
41
  for (let attempt = 0; attempt <= retries; attempt++) {
41
42
  try {
42
43
  await fsPromises.writeFile(filepath, content);
44
+ if (process.platform === "win32") {
45
+ await (0, import_promises.setTimeout)(50);
46
+ let accessRetries = 0;
47
+ const maxAccessRetries = 5;
48
+ while (accessRetries < maxAccessRetries) {
49
+ try {
50
+ await fsPromises.access(filepath);
51
+ await (0, import_promises.setTimeout)(10);
52
+ break;
53
+ } catch {
54
+ const delay = 20 * (accessRetries + 1);
55
+ await (0, import_promises.setTimeout)(delay);
56
+ accessRetries++;
57
+ }
58
+ }
59
+ }
43
60
  return;
44
61
  } catch (err) {
45
62
  const isLastAttempt = attempt === retries;
46
- const isEperm = err instanceof Error && "code" in err && (err.code === "EPERM" || err.code === "EBUSY");
47
- if (!isEperm || isLastAttempt) {
63
+ const isRetriableError = err instanceof Error && "code" in err && (err.code === "EPERM" || err.code === "EBUSY" || err.code === "ENOENT");
64
+ if (!isRetriableError || isLastAttempt) {
48
65
  throw err;
49
66
  }
50
67
  const delay = baseDelay * 2 ** attempt;
51
- await new Promise((resolve) => setTimeout(resolve, delay));
68
+ await (0, import_promises.setTimeout)(delay);
52
69
  }
53
70
  }
54
71
  }
@@ -57,7 +74,21 @@ function parseJson(content) {
57
74
  }
58
75
  async function readFile(filepath) {
59
76
  const { promises: fsPromises } = /* @__PURE__ */ getFs();
60
- return await fsPromises.readFile(filepath, "utf8");
77
+ const maxRetries = process.platform === "win32" ? 5 : 1;
78
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
79
+ try {
80
+ return await fsPromises.readFile(filepath, "utf8");
81
+ } catch (err) {
82
+ const isLastAttempt = attempt === maxRetries;
83
+ const isEnoent = err instanceof Error && "code" in err && err.code === "ENOENT";
84
+ if (!isEnoent || isLastAttempt) {
85
+ throw err;
86
+ }
87
+ const delay = process.platform === "win32" ? 50 * (attempt + 1) : 20;
88
+ await (0, import_promises.setTimeout)(delay);
89
+ }
90
+ }
91
+ throw new Error("Unreachable code");
61
92
  }
62
93
  // @__NO_SIDE_EFFECTS__
63
94
  function getEditableJsonClass() {
@@ -15,7 +15,7 @@ export declare function onExit(cb: (code: number | null, signal: string | null)
15
15
  * Get the list of signals that are currently being monitored.
16
16
  */
17
17
  /*@__NO_SIDE_EFFECTS__*/
18
- export declare function signals(): string[] | undefined;
18
+ export declare function signals(): string[];
19
19
  /**
20
20
  * Unload signal handlers and restore original process behavior.
21
21
  */
@@ -64,7 +64,7 @@ function getSignalListeners() {
64
64
  if (_sigListeners === void 0) {
65
65
  _sigListeners = { __proto__: null };
66
66
  const emitter = /* @__PURE__ */ getEmitter();
67
- const sigs = /* @__PURE__ */ getSignals();
67
+ const sigs = /* @__PURE__ */ signals();
68
68
  for (const sig of sigs) {
69
69
  _sigListeners[sig] = function listener() {
70
70
  const listeners = globalProcess?.listeners(sig) || [];
@@ -80,32 +80,6 @@ function getSignalListeners() {
80
80
  }
81
81
  return _sigListeners;
82
82
  }
83
- let _signals;
84
- // @__NO_SIDE_EFFECTS__
85
- function getSignals() {
86
- if (_signals === void 0) {
87
- _signals = ["SIGABRT", "SIGALRM", "SIGHUP", "SIGINT", "SIGTERM"];
88
- if (!WIN32) {
89
- _signals.push(
90
- "SIGVTALRM",
91
- "SIGXCPU",
92
- "SIGXFSZ",
93
- "SIGUSR2",
94
- "SIGTRAP",
95
- "SIGSYS",
96
- "SIGQUIT",
97
- "SIGIOT"
98
- // should detect profiler and enable/disable accordingly.
99
- // see #21
100
- // 'SIGPROF'
101
- );
102
- }
103
- if (platform === "linux") {
104
- _signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT", "SIGUNUSED");
105
- }
106
- }
107
- return _signals;
108
- }
109
83
  // @__NO_SIDE_EFFECTS__
110
84
  function emit(event, code, signal) {
111
85
  const emitter = /* @__PURE__ */ getEmitter();
@@ -128,7 +102,7 @@ function load() {
128
102
  if (emitter.count !== void 0) {
129
103
  emitter.count += 1;
130
104
  }
131
- const sigs = /* @__PURE__ */ getSignals();
105
+ const sigs = /* @__PURE__ */ signals();
132
106
  const sigListeners = /* @__PURE__ */ getSignalListeners();
133
107
  _signals = sigs.filter((sig) => {
134
108
  try {
@@ -211,8 +185,30 @@ function onExit(cb, options) {
211
185
  }
212
186
  };
213
187
  }
188
+ let _signals;
214
189
  // @__NO_SIDE_EFFECTS__
215
190
  function signals() {
191
+ if (_signals === void 0) {
192
+ _signals = ["SIGABRT", "SIGALRM", "SIGHUP", "SIGINT", "SIGTERM"];
193
+ if (!WIN32) {
194
+ _signals.push(
195
+ "SIGVTALRM",
196
+ "SIGXCPU",
197
+ "SIGXFSZ",
198
+ "SIGUSR2",
199
+ "SIGTRAP",
200
+ "SIGSYS",
201
+ "SIGQUIT",
202
+ "SIGIOT"
203
+ // should detect profiler and enable/disable accordingly.
204
+ // see #21
205
+ // 'SIGPROF'
206
+ );
207
+ }
208
+ if (platform === "linux") {
209
+ _signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT", "SIGUNUSED");
210
+ }
211
+ }
216
212
  return _signals;
217
213
  }
218
214
  // @__NO_SIDE_EFFECTS__
@@ -221,7 +217,7 @@ function unload() {
221
217
  return;
222
218
  }
223
219
  loaded = false;
224
- const sigs = /* @__PURE__ */ getSignals();
220
+ const sigs = /* @__PURE__ */ signals();
225
221
  const sigListeners = /* @__PURE__ */ getSignalListeners();
226
222
  for (const sig of sigs) {
227
223
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@socketsecurity/lib",
3
- "version": "5.0.0",
4
- "packageManager": "pnpm@10.22.0",
3
+ "version": "5.0.2",
4
+ "packageManager": "pnpm@10.26.0",
5
5
  "license": "MIT",
6
6
  "description": "Core utilities and infrastructure for Socket.dev security tools",
7
7
  "keywords": [
@@ -677,7 +677,7 @@
677
677
  ],
678
678
  "engines": {
679
679
  "node": ">=22",
680
- "pnpm": ">=10.22.0"
680
+ "pnpm": ">=10.25.0"
681
681
  },
682
682
  "sideEffects": false,
683
683
  "scripts": {
@@ -714,7 +714,7 @@
714
714
  "@socketregistry/is-unicode-supported": "1.0.5",
715
715
  "@socketregistry/packageurl-js": "1.3.5",
716
716
  "@socketregistry/yocto-spinner": "1.0.25",
717
- "@socketsecurity/lib-stable": "npm:@socketsecurity/lib@4.3.0",
717
+ "@socketsecurity/lib-stable": "npm:@socketsecurity/lib@5.0.1",
718
718
  "@types/node": "24.9.2",
719
719
  "@typescript/native-preview": "7.0.0-dev.20250920.1",
720
720
  "@vitest/coverage-v8": "4.0.3",
@@ -737,11 +737,12 @@
737
737
  "get-east-asian-width": "1.3.0",
738
738
  "globals": "16.4.0",
739
739
  "husky": "9.1.7",
740
- "libnpmexec": "^10.1.8",
740
+ "libnpmexec": "^10.1.11",
741
741
  "libnpmpack": "9.0.9",
742
742
  "lint-staged": "15.2.11",
743
743
  "magic-string": "0.30.17",
744
744
  "make-fetch-happen": "15.0.2",
745
+ "nock": "14.0.10",
745
746
  "normalize-package-data": "8.0.0",
746
747
  "npm-package-arg": "13.0.0",
747
748
  "pacote": "21.0.1",
@@ -750,7 +751,7 @@
750
751
  "spdx-correct": "3.2.0",
751
752
  "spdx-expression-parse": "4.0.0",
752
753
  "streaming-iterables": "8.0.1",
753
- "taze": "19.6.0",
754
+ "taze": "19.9.2",
754
755
  "trash": "10.0.0",
755
756
  "type-coverage": "2.29.7",
756
757
  "typescript": "5.9.2",
@@ -775,12 +776,12 @@
775
776
  "overrides": {
776
777
  "@npmcli/arborist": "9.1.6",
777
778
  "@npmcli/run-script": "10.0.0",
778
- "semver": "7.7.2",
779
779
  "ansi-regex": "6.2.2",
780
- "strip-ansi": "7.1.2",
780
+ "lru-cache": "11.2.2",
781
+ "semver": "7.7.2",
781
782
  "string-width": "8.1.0",
782
- "wrap-ansi": "9.0.2",
783
- "lru-cache": "11.2.2"
783
+ "strip-ansi": "7.1.2",
784
+ "wrap-ansi": "9.0.2"
784
785
  },
785
786
  "patchedDependencies": {
786
787
  "@npmcli/run-script@10.0.0": "patches/@npmcli__run-script@10.0.0.patch",