@photostructure/fs-metadata 0.3.2 → 0.3.3

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 (68) hide show
  1. package/CHANGELOG.md +13 -3
  2. package/README.md +3 -3
  3. package/dist/index.cjs +324 -215
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.mjs +329 -215
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/types/debuglog.d.ts +2 -6
  8. package/dist/types/defer.d.ts +1 -2
  9. package/dist/types/dirname.d.ts +1 -0
  10. package/dist/types/hidden.d.ts +5 -42
  11. package/dist/types/index.d.ts +91 -2
  12. package/dist/types/linux/mount_points.d.ts +2 -2
  13. package/dist/types/linux/mtab.d.ts +2 -2
  14. package/dist/types/mount_point.d.ts +1 -46
  15. package/dist/types/options.d.ts +1 -47
  16. package/dist/types/platform.d.ts +1 -0
  17. package/dist/types/remote_info.d.ts +2 -34
  18. package/dist/types/stack_path.d.ts +2 -0
  19. package/dist/types/system_volume.d.ts +2 -2
  20. package/dist/types/types/hidden_metadata.d.ts +32 -0
  21. package/dist/types/types/mount_point.d.ts +46 -0
  22. package/dist/types/types/native_bindings.d.ts +3 -3
  23. package/dist/types/types/options.d.ts +47 -0
  24. package/dist/types/types/remote_info.d.ts +33 -0
  25. package/dist/types/types/volume_metadata.d.ts +46 -0
  26. package/dist/types/unc.d.ts +1 -1
  27. package/dist/types/units.d.ts +25 -3
  28. package/dist/types/volume_metadata.d.ts +4 -50
  29. package/dist/types/volume_mount_points.d.ts +3 -6
  30. package/jest.config.base.cjs +63 -0
  31. package/jest.config.cjs +3 -16
  32. package/package.json +12 -15
  33. package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  34. package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
  35. package/src/async.ts +9 -0
  36. package/src/debuglog.ts +6 -2
  37. package/src/defer.ts +1 -1
  38. package/src/dirname.ts +13 -0
  39. package/src/global.d.ts +1 -0
  40. package/src/hidden.ts +6 -42
  41. package/src/{exports.ts → index.ts} +75 -30
  42. package/src/linux/mount_points.ts +4 -2
  43. package/src/linux/mtab.ts +3 -3
  44. package/src/mount_point.ts +2 -53
  45. package/src/options.ts +4 -53
  46. package/src/path.ts +12 -5
  47. package/src/platform.ts +5 -5
  48. package/src/remote_info.ts +44 -49
  49. package/src/stack_path.ts +71 -0
  50. package/src/system_volume.ts +3 -6
  51. package/src/test-utils/assert.ts +1 -1
  52. package/src/test-utils/debuglog-child.ts +15 -0
  53. package/src/types/hidden_metadata.ts +38 -0
  54. package/src/types/mount_point.ts +53 -0
  55. package/src/types/native_bindings.ts +3 -3
  56. package/src/types/options.ts +54 -0
  57. package/src/types/remote_info.ts +35 -0
  58. package/src/types/volume_metadata.ts +52 -0
  59. package/src/unc.ts +1 -1
  60. package/src/units.ts +39 -7
  61. package/src/volume_metadata.ts +9 -66
  62. package/src/volume_mount_points.ts +3 -6
  63. package/tsup.config.ts +1 -0
  64. package/dist/types/exports.d.ts +0 -99
  65. package/dist/types/setup.d.ts +0 -2
  66. package/src/index.cts +0 -15
  67. package/src/index.mts +0 -17
  68. package/src/setup.ts +0 -69
package/dist/index.cjs CHANGED
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  ));
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
- // src/index.cts
30
+ // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  IncludeSystemVolumesDefault: () => IncludeSystemVolumesDefault,
@@ -37,41 +37,24 @@ __export(index_exports, {
37
37
  SystemPathPatternsDefault: () => SystemPathPatternsDefault,
38
38
  TimeoutMsDefault: () => TimeoutMsDefault,
39
39
  VolumeHealthStatuses: () => VolumeHealthStatuses,
40
- getAllVolumeMetadata: () => getAllVolumeMetadata2,
41
- getHiddenMetadata: () => getHiddenMetadata2,
42
- getVolumeMetadata: () => getVolumeMetadata2,
43
- getVolumeMountPoints: () => getVolumeMountPoints2,
44
- isHidden: () => isHidden2,
45
- isHiddenRecursive: () => isHiddenRecursive2,
40
+ getAllVolumeMetadata: () => getAllVolumeMetadata,
41
+ getHiddenMetadata: () => getHiddenMetadata,
42
+ getVolumeMetadata: () => getVolumeMetadata,
43
+ getVolumeMountPoints: () => getVolumeMountPoints,
44
+ isHidden: () => isHidden,
45
+ isHiddenRecursive: () => isHiddenRecursive,
46
46
  optionsWithDefaults: () => optionsWithDefaults,
47
- setHidden: () => setHidden2
47
+ setHidden: () => setHidden
48
48
  });
49
49
  module.exports = __toCommonJS(index_exports);
50
-
51
- // src/setup.ts
52
50
  var import_node_gyp_build = __toESM(require("node-gyp-build"));
53
51
 
54
52
  // src/debuglog.ts
55
53
  var import_node_util = require("util");
56
-
57
- // src/defer.ts
58
54
  function defer(thunk) {
59
- let computed = false;
60
- let value;
61
- const fn = () => {
62
- if (!computed) {
63
- computed = true;
64
- value = thunk();
65
- }
66
- return value;
67
- };
68
- fn.reset = () => {
69
- computed = false;
70
- };
71
- return fn;
55
+ let t;
56
+ return () => t ??= thunk();
72
57
  }
73
-
74
- // src/debuglog.ts
75
58
  var debugLogContext = defer(() => {
76
59
  for (const ea of ["fs-metadata", "fs-meta"]) {
77
60
  if ((0, import_node_util.debuglog)(ea).enabled) {
@@ -93,32 +76,32 @@ function debug(msg, ...args) {
93
76
  process.stderr.write(timestamp + (0, import_node_util.format)(msg, ...args) + "\n");
94
77
  }
95
78
 
96
- // src/fs.ts
97
- var import_node_fs = require("fs");
98
- var import_promises = require("fs/promises");
79
+ // src/defer.ts
80
+ function defer2(thunk) {
81
+ let computed = false;
82
+ let value;
83
+ const fn = () => {
84
+ if (!computed) {
85
+ computed = true;
86
+ value = thunk();
87
+ }
88
+ return value;
89
+ };
90
+ fn.reset = () => {
91
+ computed = false;
92
+ };
93
+ return fn;
94
+ }
95
+
96
+ // src/stack_path.ts
99
97
  var import_node_path = require("path");
100
98
 
101
- // src/async.ts
102
- var import_node_os = require("os");
99
+ // src/platform.ts
103
100
  var import_node_process = require("process");
104
-
105
- // src/number.ts
106
- function isNumber(value) {
107
- return typeof value === "number" && isFinite(value);
108
- }
109
- var INTEGER_REGEX = /^-?\d+$/;
110
- function toInt(value) {
111
- try {
112
- if (value == null) return;
113
- const s = String(value).trim();
114
- return INTEGER_REGEX.test(s) ? parseInt(s) : void 0;
115
- } catch {
116
- return;
117
- }
118
- }
119
- function gt0(value) {
120
- return isNumber(value) && value > 0;
121
- }
101
+ var isLinux = import_node_process.platform === "linux";
102
+ var isWindows = import_node_process.platform === "win32";
103
+ var isMacOS = import_node_process.platform === "darwin";
104
+ var isArm = isLinux && import_node_process.arch.startsWith("arm");
122
105
 
123
106
  // src/string.ts
124
107
  function isString(input) {
@@ -152,6 +135,104 @@ function sortObjectsByLocale(arr, fn, locales, options) {
152
135
  return arr.sort((a, b) => fn(a).localeCompare(fn(b), locales, options));
153
136
  }
154
137
 
138
+ // src/stack_path.ts
139
+ function getCallerDirname() {
140
+ const e = new Error();
141
+ if (e.stack == null) {
142
+ Error.captureStackTrace(e);
143
+ }
144
+ return (0, import_node_path.dirname)(extractCallerPath(e.stack));
145
+ }
146
+ var patterns = isWindows ? [
147
+ // Standard: "at functionName (C:\path\file.js:1:1)"
148
+ /\bat\s.+?\((?<path>[A-Z]:\\.+):\d+:\d+\)$/,
149
+ // direct: "at C:\path\file.js:1:1"
150
+ /\bat\s(?<path>[A-Z]:\\.+):\d+:\d+$/,
151
+ // UNC: "at functionName (\\server\share\path\file.js:1:1)"
152
+ /\bat\s.+?\((?<path>\\\\.+):\d+:\d+\)$/,
153
+ // direct: "at \\server\share\path\file.js:1:1"
154
+ /\bat\s(?<path>\\\\.+):\d+:\d+$/
155
+ ] : [
156
+ // Standard: "at functionName (/path/file.js:1:1)"
157
+ /\bat\s.+?\((?<path>\/.+?):\d+:\d+\)$/,
158
+ // Anonymous or direct: "at /path/file.js:1:1"
159
+ /\bat\s(.+[^/]\s)?(?<path>\/.+?):\d+:\d+$/
160
+ ];
161
+ var MaybeUrlRE = /^[a-z]{2,5}:\/\//i;
162
+ function extractCallerPath(stack) {
163
+ const frames = stack.split("\n").filter(Boolean);
164
+ const callerFrame = frames.findIndex(
165
+ (frame) => frame.includes("getCallerDirname")
166
+ );
167
+ if (callerFrame === -1) {
168
+ throw new Error("Invalid stack trace format: missing caller frame");
169
+ }
170
+ for (let i = callerFrame + 1; i < frames.length; i++) {
171
+ const frame = frames[i];
172
+ for (const pattern of patterns) {
173
+ const g = toS(frame).trim().match(pattern)?.groups;
174
+ if (g != null && isNotBlank(g["path"])) {
175
+ const path = g["path"];
176
+ if (MaybeUrlRE.test(path)) {
177
+ try {
178
+ return new URL(path).pathname;
179
+ } catch {
180
+ }
181
+ }
182
+ return path;
183
+ }
184
+ }
185
+ }
186
+ throw new Error("Invalid stack trace format: no parsable frames");
187
+ }
188
+
189
+ // src/dirname.ts
190
+ function _dirname() {
191
+ try {
192
+ if (typeof __dirname !== "undefined") return __dirname;
193
+ } catch {
194
+ }
195
+ return getCallerDirname();
196
+ }
197
+
198
+ // src/fs.ts
199
+ var import_node_fs = require("fs");
200
+ var import_promises = require("fs/promises");
201
+ var import_node_path2 = require("path");
202
+
203
+ // src/async.ts
204
+ var import_node_os = require("os");
205
+ var import_node_process2 = require("process");
206
+
207
+ // src/number.ts
208
+ function isNumber(value) {
209
+ return typeof value === "number" && isFinite(value);
210
+ }
211
+ var INTEGER_REGEX = /^-?\d+$/;
212
+ function toInt(value) {
213
+ try {
214
+ if (value == null) return;
215
+ const s = String(value).trim();
216
+ return INTEGER_REGEX.test(s) ? parseInt(s) : void 0;
217
+ } catch {
218
+ return;
219
+ }
220
+ }
221
+ function gt0(value) {
222
+ return isNumber(value) && value > 0;
223
+ }
224
+
225
+ // src/units.ts
226
+ var SecondMs = 1e3;
227
+ var MinuteMs = 60 * SecondMs;
228
+ var HourMs = 60 * MinuteMs;
229
+ var DayMs = 24 * HourMs;
230
+ var KiB = 1024;
231
+ var MiB = 1024 * KiB;
232
+ var GiB = 1024 * MiB;
233
+ var TiB = 1024 * GiB;
234
+ var f = 1023.995 / 1024;
235
+
155
236
  // src/async.ts
156
237
  var TimeoutError = class extends Error {
157
238
  constructor(message, captureStackTrace = true) {
@@ -175,13 +256,18 @@ async function withTimeout(opts) {
175
256
  desc + ": Expected timeoutMs to be > 0, but got " + timeoutMs
176
257
  );
177
258
  }
259
+ if (timeoutMs > DayMs) {
260
+ throw new TypeError(
261
+ desc + ": Invalid timeoutMs is too large: must be less than one day, but got " + timeoutMs
262
+ );
263
+ }
178
264
  if (timeoutMs === 0) {
179
265
  return opts.promise;
180
266
  }
181
267
  const timeoutError = new TimeoutError(
182
268
  `${desc}: timeout after ${timeoutMs}ms`
183
269
  );
184
- if (import_node_process.env["NODE_ENV"] === "test" && timeoutMs === 1) {
270
+ if (import_node_process2.env["NODE_ENV"] === "test" && timeoutMs === 1) {
185
271
  timeoutError.message += "(timeout test)";
186
272
  opts.promise.catch(() => {
187
273
  });
@@ -225,9 +311,9 @@ async function mapConcurrent({
225
311
  while (executing.size >= maxConcurrency) {
226
312
  await Promise.race(executing);
227
313
  }
228
- const p2 = results[index] = fn(item).catch((error) => error);
229
- executing.add(p2);
230
- p2.finally(() => executing.delete(p2));
314
+ const p = results[index] = fn(item).catch((error) => error);
315
+ executing.add(p);
316
+ p.finally(() => executing.delete(p));
231
317
  }
232
318
  return Promise.all(results);
233
319
  }
@@ -244,13 +330,13 @@ async function canStatAsync(path) {
244
330
  }
245
331
  }
246
332
  async function findAncestorDir(dir, file) {
247
- dir = (0, import_node_path.resolve)(dir);
333
+ dir = (0, import_node_path2.resolve)(dir);
248
334
  try {
249
- const s = await statAsync((0, import_node_path.join)(dir, file));
335
+ const s = await statAsync((0, import_node_path2.join)(dir, file));
250
336
  if (s.isFile()) return dir;
251
337
  } catch {
252
338
  }
253
- const parent = (0, import_node_path.resolve)(dir, "..");
339
+ const parent = (0, import_node_path2.resolve)(dir, "..");
254
340
  return parent === dir ? void 0 : findAncestorDir(parent, file);
255
341
  }
256
342
  async function canReaddir(dir, timeoutMs) {
@@ -267,7 +353,7 @@ async function _canReaddir(dir) {
267
353
 
268
354
  // src/hidden.ts
269
355
  var import_promises2 = require("fs/promises");
270
- var import_node_path3 = require("path");
356
+ var import_node_path4 = require("path");
271
357
 
272
358
  // src/object.ts
273
359
  function isObject(value) {
@@ -348,30 +434,28 @@ function toError(cause) {
348
434
  }
349
435
 
350
436
  // src/path.ts
351
- var import_node_path2 = require("path");
352
-
353
- // src/platform.ts
354
- var import_node_os2 = require("os");
355
- var p = (0, import_node_os2.platform)();
356
- var isLinux = p === "linux";
357
- var isWindows = p === "win32";
358
- var isMacOS = p === "darwin";
359
-
360
- // src/path.ts
437
+ var import_node_path3 = require("path");
361
438
  function normalizePath(mountPoint) {
362
439
  if (isBlank(mountPoint)) return void 0;
363
440
  const result = isWindows ? normalizeWindowsPath(mountPoint) : normalizePosixPath(mountPoint);
364
- return result != null ? (0, import_node_path2.resolve)(result) : void 0;
441
+ return result != null ? (0, import_node_path3.resolve)(result) : void 0;
365
442
  }
366
443
  function normalizePosixPath(mountPoint) {
367
- return isBlank(mountPoint) ? void 0 : mountPoint === "/" ? mountPoint : mountPoint.replace(/\/+$/, "");
444
+ if (isBlank(mountPoint)) return void 0;
445
+ if (mountPoint === "/") return mountPoint;
446
+ if (mountPoint[mountPoint.length - 1] !== "/") return mountPoint;
447
+ let end = mountPoint.length - 1;
448
+ while (end > 0 && mountPoint[end] === "/") {
449
+ end--;
450
+ }
451
+ return mountPoint.slice(0, end + 1);
368
452
  }
369
453
  function normalizeWindowsPath(mountPoint) {
370
454
  return /^[a-z]:$/i.test(mountPoint) ? mountPoint.toUpperCase() + "\\" : mountPoint;
371
455
  }
372
456
  function isRootDirectory(path) {
373
457
  const n = normalizePath(path);
374
- return n == null ? false : isWindows ? (0, import_node_path2.dirname)(n) === n : n === "/";
458
+ return n == null ? false : isWindows ? (0, import_node_path3.dirname)(n) === n : n === "/";
375
459
  }
376
460
 
377
461
  // src/hidden.ts
@@ -399,23 +483,23 @@ var LocalSupport = HiddenSupportByPlatform[process.platform]?.supported ?? {
399
483
  dotPrefix: false,
400
484
  systemFlag: false
401
485
  };
402
- async function isHidden(pathname, nativeFn) {
486
+ async function isHiddenImpl(pathname, nativeFn2) {
403
487
  const norm = normalizePath(pathname);
404
488
  if (norm == null) {
405
489
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
406
490
  }
407
- return LocalSupport.dotPrefix && isPosixHidden(norm) || LocalSupport.systemFlag && isSystemHidden(norm, nativeFn);
491
+ return LocalSupport.dotPrefix && isPosixHidden(norm) || LocalSupport.systemFlag && isSystemHidden(norm, nativeFn2);
408
492
  }
409
- async function isHiddenRecursive(path, nativeFn) {
493
+ async function isHiddenRecursiveImpl(path, nativeFn2) {
410
494
  let norm = normalizePath(path);
411
495
  if (norm == null) {
412
496
  throw new Error("Invalid path: " + JSON.stringify(path));
413
497
  }
414
498
  while (!isRootDirectory(norm)) {
415
- if (await isHidden(norm, nativeFn)) {
499
+ if (await isHiddenImpl(norm, nativeFn2)) {
416
500
  return true;
417
501
  }
418
- norm = (0, import_node_path3.dirname)(norm);
502
+ norm = (0, import_node_path4.dirname)(norm);
419
503
  }
420
504
  return false;
421
505
  }
@@ -424,9 +508,9 @@ function createHiddenPosixPath(pathname, hidden) {
424
508
  if (norm == null) {
425
509
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
426
510
  }
427
- const dir = (0, import_node_path3.dirname)(norm);
428
- const srcBase = (0, import_node_path3.basename)(norm).replace(/^\./, "");
429
- const dest = (0, import_node_path3.join)(dir, (hidden ? "." : "") + srcBase);
511
+ const dir = (0, import_node_path4.dirname)(norm);
512
+ const srcBase = (0, import_node_path4.basename)(norm).replace(/^\./, "");
513
+ const dest = (0, import_node_path4.join)(dir, (hidden ? "." : "") + srcBase);
430
514
  return dest;
431
515
  }
432
516
  async function setHiddenPosix(pathname, hidden) {
@@ -439,25 +523,25 @@ async function setHiddenPosix(pathname, hidden) {
439
523
  }
440
524
  function isPosixHidden(pathname) {
441
525
  if (!LocalSupport.dotPrefix) return false;
442
- const b = (0, import_node_path3.basename)(pathname);
526
+ const b = (0, import_node_path4.basename)(pathname);
443
527
  return b.startsWith(".") && b !== "." && b !== "..";
444
528
  }
445
- async function isSystemHidden(pathname, nativeFn) {
529
+ async function isSystemHidden(pathname, nativeFn2) {
446
530
  if (!LocalSupport.systemFlag) {
447
531
  return false;
448
532
  }
449
533
  if (isWindows && isRootDirectory(pathname)) {
450
534
  return false;
451
535
  }
452
- return await canStatAsync(pathname) && await (await nativeFn()).isHidden(pathname);
536
+ return await canStatAsync(pathname) && await (await nativeFn2()).isHidden(pathname);
453
537
  }
454
- async function getHiddenMetadata(pathname, nativeFn) {
538
+ async function getHiddenMetadataImpl(pathname, nativeFn2) {
455
539
  const norm = normalizePath(pathname);
456
540
  if (norm == null) {
457
541
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
458
542
  }
459
543
  const dotPrefix = isPosixHidden(norm);
460
- const systemFlag = await isSystemHidden(norm, nativeFn);
544
+ const systemFlag = await isSystemHidden(norm, nativeFn2);
461
545
  return {
462
546
  hidden: dotPrefix || systemFlag,
463
547
  dotPrefix,
@@ -465,7 +549,7 @@ async function getHiddenMetadata(pathname, nativeFn) {
465
549
  supported: LocalSupport
466
550
  };
467
551
  }
468
- async function setHidden(pathname, hide, method, nativeFn) {
552
+ async function setHiddenImpl(pathname, hide, method, nativeFn2) {
469
553
  let norm = normalizePath(pathname);
470
554
  if (norm == null) {
471
555
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
@@ -497,14 +581,14 @@ async function setHidden(pathname, hide, method, nativeFn) {
497
581
  acted = true;
498
582
  }
499
583
  if (LocalSupport.systemFlag && (["all", "systemFlag"].includes(method) || !acted && method === "auto")) {
500
- await (await nativeFn()).setHidden(norm, hide);
584
+ await (await nativeFn2()).setHidden(norm, hide);
501
585
  actions.systemFlag = true;
502
586
  }
503
587
  return { pathname: norm, actions };
504
588
  }
505
589
 
506
590
  // src/options.ts
507
- var import_node_os3 = require("os");
591
+ var import_node_os2 = require("os");
508
592
  var TimeoutMsDefault = 5e3;
509
593
  var SystemPathPatternsDefault = [
510
594
  "/boot",
@@ -520,6 +604,9 @@ var SystemPathPatternsDefault = [
520
604
  "/run/user/*/gvfs",
521
605
  "/snap/**",
522
606
  "/sys/**",
607
+ "/tmp",
608
+ "/var/tmp",
609
+ // we aren't including /tmp/**, as some people temporarily mount volumes there, like /tmp/project.
523
610
  "**/#snapshot",
524
611
  // Synology and Kubernetes volume snapshots
525
612
  // windows for linux:
@@ -571,7 +658,7 @@ var LinuxMountTablePathsDefault = [
571
658
  var IncludeSystemVolumesDefault = isWindows;
572
659
  var OptionsDefault = {
573
660
  timeoutMs: TimeoutMsDefault,
574
- maxConcurrency: (0, import_node_os3.availableParallelism)(),
661
+ maxConcurrency: (0, import_node_os2.availableParallelism)(),
575
662
  systemPathPatterns: [...SystemPathPatternsDefault],
576
663
  systemFsTypes: [...SystemFsTypesDefault],
577
664
  linuxMountTablePaths: [...LinuxMountTablePathsDefault],
@@ -589,14 +676,57 @@ function optionsWithDefaults(overrides = {}) {
589
676
  };
590
677
  }
591
678
 
679
+ // src/string_enum.ts
680
+ function stringEnum(...o) {
681
+ const set = new Set(o);
682
+ const dict = {};
683
+ for (const key of o) {
684
+ dict[key] = key;
685
+ }
686
+ return {
687
+ ...dict,
688
+ values: Object.freeze([...set]),
689
+ size: set.size,
690
+ get: (s) => s != null && set.has(s) ? s : void 0
691
+ };
692
+ }
693
+
694
+ // src/volume_health_status.ts
695
+ var VolumeHealthStatuses = stringEnum(
696
+ "healthy",
697
+ "timeout",
698
+ "inaccessible",
699
+ "disconnected",
700
+ "unknown"
701
+ );
702
+ async function directoryStatus(dir, timeoutMs, canReaddirImpl = canReaddir) {
703
+ try {
704
+ if (await canReaddirImpl(dir, timeoutMs)) {
705
+ return { status: VolumeHealthStatuses.healthy };
706
+ }
707
+ } catch (error) {
708
+ debug("[directoryStatus] %s: %s", dir, error);
709
+ let status = VolumeHealthStatuses.unknown;
710
+ if (error instanceof TimeoutError) {
711
+ status = VolumeHealthStatuses.timeout;
712
+ } else if (isObject(error) && error instanceof Error && "code" in error) {
713
+ if (error.code === "EPERM" || error.code === "EACCES") {
714
+ status = VolumeHealthStatuses.inaccessible;
715
+ }
716
+ }
717
+ return { status, error: toError(error) };
718
+ }
719
+ return { status: VolumeHealthStatuses.unknown };
720
+ }
721
+
592
722
  // src/linux/dev_disk.ts
593
723
  var import_promises3 = require("fs/promises");
594
- var import_node_path4 = require("path");
724
+ var import_node_path5 = require("path");
595
725
  async function getUuidFromDevDisk(devicePath) {
596
726
  try {
597
727
  const result = await getBasenameLinkedTo(
598
728
  "/dev/disk/by-uuid",
599
- (0, import_node_path4.resolve)(devicePath)
729
+ (0, import_node_path5.resolve)(devicePath)
600
730
  );
601
731
  debug("[getUuidFromDevDisk] result: %o", result);
602
732
  return result;
@@ -609,7 +739,7 @@ async function getLabelFromDevDisk(devicePath) {
609
739
  try {
610
740
  const result = await getBasenameLinkedTo(
611
741
  "/dev/disk/by-label",
612
- (0, import_node_path4.resolve)(devicePath)
742
+ (0, import_node_path5.resolve)(devicePath)
613
743
  );
614
744
  debug("[getLabelFromDevDisk] result: %o", result);
615
745
  return result;
@@ -630,9 +760,9 @@ async function* readLinks(directory) {
630
760
  for (const dirent of await (0, import_promises3.readdir)(directory, { withFileTypes: true })) {
631
761
  if (dirent.isSymbolicLink()) {
632
762
  try {
633
- const linkTarget = (0, import_node_path4.resolve)(
763
+ const linkTarget = (0, import_node_path5.resolve)(
634
764
  directory,
635
- await (0, import_promises3.readlink)((0, import_node_path4.join)(directory, dirent.name))
765
+ await (0, import_promises3.readlink)((0, import_node_path5.join)(directory, dirent.name))
636
766
  );
637
767
  yield { dirent, linkTarget };
638
768
  } catch {
@@ -646,8 +776,7 @@ var import_promises4 = require("fs/promises");
646
776
 
647
777
  // src/mount_point.ts
648
778
  function isMountPoint(obj) {
649
- if (!isObject(obj)) return false;
650
- return "mountPoint" in obj && isNotBlank(obj.mountPoint);
779
+ return isObject(obj) && "mountPoint" in obj && isNotBlank(obj.mountPoint);
651
780
  }
652
781
 
653
782
  // src/remote_info.ts
@@ -656,7 +785,7 @@ function isRemoteInfo(obj) {
656
785
  const { remoteHost, remoteShare } = obj;
657
786
  return isNotBlank(remoteHost) && isNotBlank(remoteShare);
658
787
  }
659
- var NETWORK_FS_TYPES = /* @__PURE__ */ new Set([
788
+ var NETWORK_FS_TYPE_ARRAY = [
660
789
  "9p",
661
790
  "afp",
662
791
  "afs",
@@ -664,9 +793,6 @@ var NETWORK_FS_TYPES = /* @__PURE__ */ new Set([
664
793
  "ceph",
665
794
  "cifs",
666
795
  "ftp",
667
- "fuse.cephfs",
668
- "fuse.glusterfs",
669
- "fuse.sshfs",
670
796
  "fuse",
671
797
  "gfs2",
672
798
  "glusterfs",
@@ -678,12 +804,31 @@ var NETWORK_FS_TYPES = /* @__PURE__ */ new Set([
678
804
  "smbfs",
679
805
  "sshfs",
680
806
  "webdav"
807
+ ];
808
+ var NETWORK_FS_TYPES = new Set(NETWORK_FS_TYPE_ARRAY);
809
+ var FS_TYPE_ALIASES = /* @__PURE__ */ new Map([
810
+ ["nfs1", "nfs"],
811
+ ["nfs2", "nfs"],
812
+ ["nfs3", "nfs"],
813
+ ["nfs4", "nfs4"],
814
+ ["fuse.sshfs", "sshfs"],
815
+ ["sshfs.fuse", "sshfs"],
816
+ ["davfs2", "webdav"],
817
+ ["davfs", "webdav"],
818
+ ["cifs.smb", "cifs"],
819
+ ["smbfs", "cifs"],
820
+ ["cephfs", "ceph"],
821
+ ["fuse.ceph", "ceph"],
822
+ ["fuse.cephfs", "ceph"],
823
+ ["rbd", "ceph"],
824
+ ["fuse.glusterfs", "glusterfs"]
681
825
  ]);
682
- function normalizeProtocol(protocol) {
683
- return (protocol ?? "").toLowerCase().replace(/:$/, "");
826
+ function normalizeFsType(fstype) {
827
+ const norm = toS(fstype).toLowerCase().replace(/:$/, "");
828
+ return FS_TYPE_ALIASES.get(norm) ?? norm;
684
829
  }
685
830
  function isRemoteFsType(fstype) {
686
- return isNotBlank(fstype) && NETWORK_FS_TYPES.has(normalizeProtocol(fstype));
831
+ return isNotBlank(fstype) && NETWORK_FS_TYPES.has(normalizeFsType(fstype));
687
832
  }
688
833
  function parseURL(s) {
689
834
  try {
@@ -704,18 +849,22 @@ function extractRemoteInfo(fsSpec) {
704
849
  uri: fsSpec
705
850
  };
706
851
  }
707
- const patterns = [
708
- // CIFS/SMB pattern: //hostname/share or //user@host/share
852
+ const patterns2 = [
709
853
  {
854
+ // CIFS/SMB pattern: //hostname/share or //user@host/share
710
855
  regex: /^\/\/(?:(?<remoteUser>[^/@]+)@)?(?<remoteHost>[^/@]+)\/(?<remoteShare>.+)$/
711
856
  },
712
- // NFS pattern: hostname:/share
713
857
  {
858
+ // sshfs pattern: sshfs#USER@HOST:REMOTE_PATH
859
+ regex: /^(?:(?<protocol>\w+)#)?(?<remoteUser>[^@]+)@(?<remoteHost>[^:]+):(?<remoteShare>.+)$/
860
+ },
861
+ {
862
+ // NFS pattern: hostname:/share
714
863
  protocol: "nfs",
715
864
  regex: /^(?<remoteHost>[^:]+):\/(?!\/)(?<remoteShare>.+)$/
716
865
  }
717
866
  ];
718
- for (const { protocol, regex } of patterns) {
867
+ for (const { protocol, regex } of patterns2) {
719
868
  const o = compactValues({
720
869
  protocol,
721
870
  remote: true,
@@ -730,8 +879,8 @@ function extractRemoteInfo(fsSpec) {
730
879
  const parsed = new URL(fsSpec);
731
880
  if (parsed != null) {
732
881
  debug("[extractRemoteInfo] parsed URL: %o", parsed);
733
- const protocol = normalizeProtocol(parsed.protocol);
734
- if (!isRemoteFsType(protocol)) {
882
+ const fstype = normalizeFsType(parsed.protocol);
883
+ if (!isRemoteFsType(fstype)) {
735
884
  return {
736
885
  uri: fsSpec,
737
886
  remote: false
@@ -739,7 +888,7 @@ function extractRemoteInfo(fsSpec) {
739
888
  } else {
740
889
  return compactValues({
741
890
  uri: fsSpec,
742
- protocol,
891
+ protocol: fstype,
743
892
  remote: true,
744
893
  remoteUser: parsed.username,
745
894
  remoteHost: parsed.hostname,
@@ -755,18 +904,18 @@ function extractRemoteInfo(fsSpec) {
755
904
 
756
905
  // src/glob.ts
757
906
  var cache = /* @__PURE__ */ new Map();
758
- function compileGlob(patterns) {
759
- if (patterns == null || patterns.length === 0) {
907
+ function compileGlob(patterns2) {
908
+ if (patterns2 == null || patterns2.length === 0) {
760
909
  return NeverMatchRE;
761
910
  }
762
- const patternsKey = JSON.stringify(patterns);
911
+ const patternsKey = JSON.stringify(patterns2);
763
912
  {
764
913
  const prior = cache.get(patternsKey);
765
914
  if (prior != null) {
766
915
  return prior;
767
916
  }
768
917
  }
769
- const sorted = patterns.slice().filter(isNotBlank).sort();
918
+ const sorted = patterns2.slice().filter(isNotBlank).sort();
770
919
  const sortedKey = JSON.stringify(sorted);
771
920
  {
772
921
  const prior = cache.get(sortedKey);
@@ -783,8 +932,8 @@ function compileGlob(patterns) {
783
932
  cache.set(sortedKey, result);
784
933
  return result;
785
934
  }
786
- function _compileGlob(patterns) {
787
- const regexPatterns = patterns.map((pattern) => {
935
+ function _compileGlob(patterns2) {
936
+ const regexPatterns = patterns2.map((pattern) => {
788
937
  let regex = "";
789
938
  let i = 0;
790
939
  while (i < pattern.length) {
@@ -890,7 +1039,7 @@ function mountEntryToPartialVolumeMetadata(entry, options = {}) {
890
1039
  mountFrom: entry.fs_spec,
891
1040
  isSystemVolume: isSystemVolume(entry.fs_file, entry.fs_vfstype, options),
892
1041
  remote: false,
893
- // < default to false
1042
+ // < default to false, but it may be overridden by extractRemoteInfo
894
1043
  ...extractRemoteInfo(entry.fs_spec)
895
1044
  };
896
1045
  }
@@ -1022,49 +1171,6 @@ function extractUUID(uuid) {
1022
1171
  return toS(uuid).match(uuidRegex)?.[0];
1023
1172
  }
1024
1173
 
1025
- // src/string_enum.ts
1026
- function stringEnum(...o) {
1027
- const set = new Set(o);
1028
- const dict = {};
1029
- for (const key of o) {
1030
- dict[key] = key;
1031
- }
1032
- return {
1033
- ...dict,
1034
- values: Object.freeze([...set]),
1035
- size: set.size,
1036
- get: (s) => s != null && set.has(s) ? s : void 0
1037
- };
1038
- }
1039
-
1040
- // src/volume_health_status.ts
1041
- var VolumeHealthStatuses = stringEnum(
1042
- "healthy",
1043
- "timeout",
1044
- "inaccessible",
1045
- "disconnected",
1046
- "unknown"
1047
- );
1048
- async function directoryStatus(dir, timeoutMs, canReaddirImpl = canReaddir) {
1049
- try {
1050
- if (await canReaddirImpl(dir, timeoutMs)) {
1051
- return { status: VolumeHealthStatuses.healthy };
1052
- }
1053
- } catch (error) {
1054
- debug("[directoryStatus] %s: %s", dir, error);
1055
- let status = VolumeHealthStatuses.unknown;
1056
- if (error instanceof TimeoutError) {
1057
- status = VolumeHealthStatuses.timeout;
1058
- } else if (isObject(error) && error instanceof Error && "code" in error) {
1059
- if (error.code === "EPERM" || error.code === "EACCES") {
1060
- status = VolumeHealthStatuses.inaccessible;
1061
- }
1062
- }
1063
- return { status, error: toError(error) };
1064
- }
1065
- return { status: VolumeHealthStatuses.unknown };
1066
- }
1067
-
1068
1174
  // src/array.ts
1069
1175
  function uniqBy(arr, keyFn) {
1070
1176
  const seen = /* @__PURE__ */ new Set();
@@ -1077,21 +1183,21 @@ function uniqBy(arr, keyFn) {
1077
1183
  }
1078
1184
 
1079
1185
  // src/volume_mount_points.ts
1080
- async function getVolumeMountPoints(opts, nativeFn) {
1081
- const p2 = _getVolumeMountPoints(opts, nativeFn);
1082
- return isWindows ? p2 : withTimeout({ desc: "getVolumeMountPoints", ...opts, promise: p2 });
1186
+ async function getVolumeMountPointsImpl(opts, nativeFn2) {
1187
+ const p = _getVolumeMountPoints(opts, nativeFn2);
1188
+ return isWindows ? p : withTimeout({ desc: "getVolumeMountPoints", ...opts, promise: p });
1083
1189
  }
1084
- async function _getVolumeMountPoints(o, nativeFn) {
1190
+ async function _getVolumeMountPoints(o, nativeFn2) {
1085
1191
  debug("[getVolumeMountPoints] gathering mount points with options: %o", o);
1086
1192
  const raw = await (isWindows || isMacOS ? (async () => {
1087
1193
  debug("[getVolumeMountPoints] using native implementation");
1088
- const points = await (await nativeFn()).getVolumeMountPoints(o);
1194
+ const points = await (await nativeFn2()).getVolumeMountPoints(o);
1089
1195
  debug(
1090
1196
  "[getVolumeMountPoints] native returned %d mount points",
1091
1197
  points.length
1092
1198
  );
1093
1199
  return points;
1094
- })() : getLinuxMountPoints(nativeFn, o));
1200
+ })() : getLinuxMountPoints(nativeFn2, o));
1095
1201
  debug("[getVolumeMountPoints] raw mount points: %o", raw);
1096
1202
  const compacted = raw.map((ea) => compactValues(ea)).filter((ea) => isNotBlank(ea.mountPoint));
1097
1203
  for (const ea of compacted) {
@@ -1129,20 +1235,20 @@ async function _getVolumeMountPoints(o, nativeFn) {
1129
1235
  }
1130
1236
 
1131
1237
  // src/volume_metadata.ts
1132
- async function getVolumeMetadata(o, nativeFn) {
1238
+ async function getVolumeMetadataImpl(o, nativeFn2) {
1133
1239
  if (isBlank(o.mountPoint)) {
1134
1240
  throw new TypeError(
1135
1241
  "Invalid mountPoint: got " + JSON.stringify(o.mountPoint)
1136
1242
  );
1137
1243
  }
1138
- const p2 = _getVolumeMetadata(o, nativeFn);
1139
- return isWindows ? p2 : withTimeout({
1244
+ const p = _getVolumeMetadata(o, nativeFn2);
1245
+ return isWindows ? p : withTimeout({
1140
1246
  desc: "getVolumeMetadata()",
1141
1247
  timeoutMs: o.timeoutMs,
1142
- promise: p2
1248
+ promise: p
1143
1249
  });
1144
1250
  }
1145
- async function _getVolumeMetadata(o, nativeFn) {
1251
+ async function _getVolumeMetadata(o, nativeFn2) {
1146
1252
  o = optionsWithDefaults(o);
1147
1253
  const norm = normalizePath(o.mountPoint);
1148
1254
  if (norm == null) {
@@ -1184,7 +1290,7 @@ async function _getVolumeMetadata(o, nativeFn) {
1184
1290
  debug("[getVolumeMetadata] using device: %s", device);
1185
1291
  }
1186
1292
  debug("[getVolumeMetadata] requesting native metadata");
1187
- const metadata = await (await nativeFn()).getVolumeMetadata(o);
1293
+ const metadata = await (await nativeFn2()).getVolumeMetadata(o);
1188
1294
  debug("[getVolumeMetadata] native metadata: %o", metadata);
1189
1295
  const remoteInfo = mtabInfo ?? extractRemoteInfo(metadata.uri) ?? extractRemoteInfo(metadata.mountFrom) ?? (isWindows ? parseUNCPath(o.mountPoint) : void 0);
1190
1296
  debug("[getVolumeMetadata] extracted remote info: %o", remoteInfo);
@@ -1215,10 +1321,10 @@ async function _getVolumeMetadata(o, nativeFn) {
1215
1321
  debug("[getVolumeMetadata] final result for %s: %o", o.mountPoint, result);
1216
1322
  return compactValues(result);
1217
1323
  }
1218
- async function getAllVolumeMetadata(opts, nativeFn) {
1324
+ async function getAllVolumeMetadataImpl(opts, nativeFn2) {
1219
1325
  const o = optionsWithDefaults(opts);
1220
1326
  debug("[getAllVolumeMetadata] starting with options: %o", o);
1221
- const arr = await getVolumeMountPoints(o, nativeFn);
1327
+ const arr = await getVolumeMountPointsImpl(o, nativeFn2);
1222
1328
  debug("[getAllVolumeMetadata] found %d mount points", arr.length);
1223
1329
  const unhealthyMountPoints = arr.filter(
1224
1330
  (ea) => ea.status != null && ea.status !== VolumeHealthStatuses.healthy
@@ -1248,7 +1354,7 @@ async function getAllVolumeMetadata(opts, nativeFn) {
1248
1354
  const results = await mapConcurrent({
1249
1355
  maxConcurrency: o.maxConcurrency,
1250
1356
  items: opts?.includeSystemVolumes ?? IncludeSystemVolumesDefault ? healthy : healthy.filter((ea) => !ea.isSystemVolume),
1251
- fn: async (mp) => getVolumeMetadata({ ...mp, ...o }, nativeFn).catch((error) => ({
1357
+ fn: async (mp) => getVolumeMetadataImpl({ ...mp, ...o }, nativeFn2).catch((error) => ({
1252
1358
  mountPoint: mp.mountPoint,
1253
1359
  error
1254
1360
  }))
@@ -1266,49 +1372,52 @@ async function getAllVolumeMetadata(opts, nativeFn) {
1266
1372
  );
1267
1373
  }
1268
1374
 
1269
- // src/setup.ts
1270
- function setup(dirname3) {
1271
- const nativeFn = defer(async () => {
1272
- const start = Date.now();
1273
- try {
1274
- const dir = await findAncestorDir(dirname3, "binding.gyp");
1275
- if (dir == null) {
1276
- throw new Error(
1277
- "Could not find bindings.gyp in any ancestor directory of " + dirname3
1278
- );
1279
- }
1280
- const bindings = (0, import_node_gyp_build.default)(dir);
1281
- bindings.setDebugLogging(isDebugEnabled());
1282
- bindings.setDebugPrefix(debugLogContext() + ":native");
1283
- return bindings;
1284
- } catch (error) {
1285
- debug("Loading native bindings failed: %s", error);
1286
- throw error;
1287
- } finally {
1288
- debug(`Native bindings took %d ms to load`, Date.now() - start);
1375
+ // src/index.ts
1376
+ var nativeFn = defer2(async () => {
1377
+ const start = Date.now();
1378
+ try {
1379
+ const dirname4 = _dirname();
1380
+ const dir = await findAncestorDir(dirname4, "binding.gyp");
1381
+ if (dir == null) {
1382
+ throw new Error(
1383
+ "Could not find bindings.gyp in any ancestor directory of " + dirname4
1384
+ );
1289
1385
  }
1290
- });
1291
- return {
1292
- getVolumeMountPoints: (opts = {}) => getVolumeMountPoints(optionsWithDefaults(opts), nativeFn),
1293
- getVolumeMetadata: (mountPoint, opts = {}) => getVolumeMetadata({ ...optionsWithDefaults(opts), mountPoint }, nativeFn),
1294
- getAllVolumeMetadata: (opts = {}) => getAllVolumeMetadata(optionsWithDefaults(opts), nativeFn),
1295
- isHidden: (pathname) => isHidden(pathname, nativeFn),
1296
- isHiddenRecursive: (pathname) => isHiddenRecursive(pathname, nativeFn),
1297
- getHiddenMetadata: (pathname) => getHiddenMetadata(pathname, nativeFn),
1298
- setHidden: (pathname, hidden, method = "auto") => setHidden(pathname, hidden, method, nativeFn)
1299
- };
1386
+ const bindings = (0, import_node_gyp_build.default)(dir);
1387
+ bindings.setDebugLogging(isDebugEnabled());
1388
+ bindings.setDebugPrefix(debugLogContext() + ":native");
1389
+ return bindings;
1390
+ } catch (error) {
1391
+ debug("Loading native bindings failed: %s", error);
1392
+ throw error;
1393
+ } finally {
1394
+ debug(`Native bindings took %d ms to load`, Date.now() - start);
1395
+ }
1396
+ });
1397
+ function getVolumeMountPoints(opts) {
1398
+ return getVolumeMountPointsImpl(optionsWithDefaults(opts), nativeFn);
1399
+ }
1400
+ function getVolumeMetadata(mountPoint, opts) {
1401
+ return getVolumeMetadataImpl(
1402
+ { ...optionsWithDefaults(opts), mountPoint },
1403
+ nativeFn
1404
+ );
1405
+ }
1406
+ function getAllVolumeMetadata(opts) {
1407
+ return getAllVolumeMetadataImpl(optionsWithDefaults(opts), nativeFn);
1408
+ }
1409
+ function isHidden(pathname) {
1410
+ return isHiddenImpl(pathname, nativeFn);
1411
+ }
1412
+ function isHiddenRecursive(pathname) {
1413
+ return isHiddenRecursiveImpl(pathname, nativeFn);
1414
+ }
1415
+ function getHiddenMetadata(pathname) {
1416
+ return getHiddenMetadataImpl(pathname, nativeFn);
1417
+ }
1418
+ function setHidden(pathname, hidden, method = "auto") {
1419
+ return setHiddenImpl(pathname, hidden, method, nativeFn);
1300
1420
  }
1301
-
1302
- // src/index.cts
1303
- var {
1304
- getVolumeMountPoints: getVolumeMountPoints2,
1305
- getVolumeMetadata: getVolumeMetadata2,
1306
- getAllVolumeMetadata: getAllVolumeMetadata2,
1307
- isHidden: isHidden2,
1308
- isHiddenRecursive: isHiddenRecursive2,
1309
- getHiddenMetadata: getHiddenMetadata2,
1310
- setHidden: setHidden2
1311
- } = setup(__dirname);
1312
1421
  // Annotate the CommonJS export names for ESM import in node:
1313
1422
  0 && (module.exports = {
1314
1423
  IncludeSystemVolumesDefault,