@photostructure/fs-metadata 0.3.1 → 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 (70) hide show
  1. package/CHANGELOG.md +29 -3
  2. package/README.md +3 -3
  3. package/dist/index.cjs +326 -223
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.mjs +331 -223
  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/fs.ts +3 -15
  40. package/src/global.d.ts +1 -0
  41. package/src/hidden.ts +6 -42
  42. package/src/{exports.ts → index.ts} +75 -30
  43. package/src/linux/mount_points.ts +4 -2
  44. package/src/linux/mtab.ts +3 -3
  45. package/src/mount_point.ts +2 -53
  46. package/src/options.ts +4 -53
  47. package/src/path.ts +12 -5
  48. package/src/platform.ts +5 -5
  49. package/src/remote_info.ts +44 -49
  50. package/src/stack_path.ts +71 -0
  51. package/src/system_volume.ts +3 -6
  52. package/src/test-utils/assert.ts +1 -1
  53. package/src/test-utils/debuglog-child.ts +15 -0
  54. package/src/test-utils/platform.ts +8 -0
  55. package/src/types/hidden_metadata.ts +38 -0
  56. package/src/types/mount_point.ts +53 -0
  57. package/src/types/native_bindings.ts +3 -3
  58. package/src/types/options.ts +54 -0
  59. package/src/types/remote_info.ts +35 -0
  60. package/src/types/volume_metadata.ts +52 -0
  61. package/src/unc.ts +1 -1
  62. package/src/units.ts +39 -7
  63. package/src/volume_metadata.ts +9 -66
  64. package/src/volume_mount_points.ts +3 -6
  65. package/tsup.config.ts +1 -0
  66. package/dist/types/exports.d.ts +0 -99
  67. package/dist/types/setup.d.ts +0 -2
  68. package/src/index.cts +0 -15
  69. package/src/index.mts +0 -17
  70. package/src/setup.ts +0 -69
package/dist/index.mjs CHANGED
@@ -1,31 +1,19 @@
1
- // src/index.mts
2
- import { dirname as dirname3 } from "node:path";
3
- import { fileURLToPath } from "node:url";
1
+ // node_modules/tsup/assets/esm_shims.js
2
+ import { fileURLToPath } from "url";
3
+ import path from "path";
4
+ var getFilename = () => fileURLToPath(import.meta.url);
5
+ var getDirname = () => path.dirname(getFilename());
6
+ var __dirname = /* @__PURE__ */ getDirname();
4
7
 
5
- // src/setup.ts
8
+ // src/index.ts
6
9
  import NodeGypBuild from "node-gyp-build";
7
10
 
8
11
  // src/debuglog.ts
9
12
  import { debuglog, format } from "node:util";
10
-
11
- // src/defer.ts
12
13
  function defer(thunk) {
13
- let computed = false;
14
- let value;
15
- const fn = () => {
16
- if (!computed) {
17
- computed = true;
18
- value = thunk();
19
- }
20
- return value;
21
- };
22
- fn.reset = () => {
23
- computed = false;
24
- };
25
- return fn;
14
+ let t;
15
+ return () => t ??= thunk();
26
16
  }
27
-
28
- // src/debuglog.ts
29
17
  var debugLogContext = defer(() => {
30
18
  for (const ea of ["fs-metadata", "fs-meta"]) {
31
19
  if (debuglog(ea).enabled) {
@@ -47,31 +35,32 @@ function debug(msg, ...args) {
47
35
  process.stderr.write(timestamp + format(msg, ...args) + "\n");
48
36
  }
49
37
 
50
- // src/fs.ts
51
- import { opendir, stat } from "node:fs/promises";
52
- import { join, resolve } from "node:path";
38
+ // src/defer.ts
39
+ function defer2(thunk) {
40
+ let computed = false;
41
+ let value;
42
+ const fn = () => {
43
+ if (!computed) {
44
+ computed = true;
45
+ value = thunk();
46
+ }
47
+ return value;
48
+ };
49
+ fn.reset = () => {
50
+ computed = false;
51
+ };
52
+ return fn;
53
+ }
53
54
 
54
- // src/async.ts
55
- import { availableParallelism } from "node:os";
56
- import { env } from "node:process";
55
+ // src/stack_path.ts
56
+ import { dirname } from "node:path";
57
57
 
58
- // src/number.ts
59
- function isNumber(value) {
60
- return typeof value === "number" && isFinite(value);
61
- }
62
- var INTEGER_REGEX = /^-?\d+$/;
63
- function toInt(value) {
64
- try {
65
- if (value == null) return;
66
- const s = String(value).trim();
67
- return INTEGER_REGEX.test(s) ? parseInt(s) : void 0;
68
- } catch {
69
- return;
70
- }
71
- }
72
- function gt0(value) {
73
- return isNumber(value) && value > 0;
74
- }
58
+ // src/platform.ts
59
+ import { arch, platform } from "node:process";
60
+ var isLinux = platform === "linux";
61
+ var isWindows = platform === "win32";
62
+ var isMacOS = platform === "darwin";
63
+ var isArm = isLinux && arch.startsWith("arm");
75
64
 
76
65
  // src/string.ts
77
66
  function isString(input) {
@@ -105,6 +94,103 @@ function sortObjectsByLocale(arr, fn, locales, options) {
105
94
  return arr.sort((a, b) => fn(a).localeCompare(fn(b), locales, options));
106
95
  }
107
96
 
97
+ // src/stack_path.ts
98
+ function getCallerDirname() {
99
+ const e = new Error();
100
+ if (e.stack == null) {
101
+ Error.captureStackTrace(e);
102
+ }
103
+ return dirname(extractCallerPath(e.stack));
104
+ }
105
+ var patterns = isWindows ? [
106
+ // Standard: "at functionName (C:\path\file.js:1:1)"
107
+ /\bat\s.+?\((?<path>[A-Z]:\\.+):\d+:\d+\)$/,
108
+ // direct: "at C:\path\file.js:1:1"
109
+ /\bat\s(?<path>[A-Z]:\\.+):\d+:\d+$/,
110
+ // UNC: "at functionName (\\server\share\path\file.js:1:1)"
111
+ /\bat\s.+?\((?<path>\\\\.+):\d+:\d+\)$/,
112
+ // direct: "at \\server\share\path\file.js:1:1"
113
+ /\bat\s(?<path>\\\\.+):\d+:\d+$/
114
+ ] : [
115
+ // Standard: "at functionName (/path/file.js:1:1)"
116
+ /\bat\s.+?\((?<path>\/.+?):\d+:\d+\)$/,
117
+ // Anonymous or direct: "at /path/file.js:1:1"
118
+ /\bat\s(.+[^/]\s)?(?<path>\/.+?):\d+:\d+$/
119
+ ];
120
+ var MaybeUrlRE = /^[a-z]{2,5}:\/\//i;
121
+ function extractCallerPath(stack) {
122
+ const frames = stack.split("\n").filter(Boolean);
123
+ const callerFrame = frames.findIndex(
124
+ (frame) => frame.includes("getCallerDirname")
125
+ );
126
+ if (callerFrame === -1) {
127
+ throw new Error("Invalid stack trace format: missing caller frame");
128
+ }
129
+ for (let i = callerFrame + 1; i < frames.length; i++) {
130
+ const frame = frames[i];
131
+ for (const pattern of patterns) {
132
+ const g = toS(frame).trim().match(pattern)?.groups;
133
+ if (g != null && isNotBlank(g["path"])) {
134
+ const path2 = g["path"];
135
+ if (MaybeUrlRE.test(path2)) {
136
+ try {
137
+ return new URL(path2).pathname;
138
+ } catch {
139
+ }
140
+ }
141
+ return path2;
142
+ }
143
+ }
144
+ }
145
+ throw new Error("Invalid stack trace format: no parsable frames");
146
+ }
147
+
148
+ // src/dirname.ts
149
+ function _dirname() {
150
+ try {
151
+ if (typeof __dirname !== "undefined") return __dirname;
152
+ } catch {
153
+ }
154
+ return getCallerDirname();
155
+ }
156
+
157
+ // src/fs.ts
158
+ import { opendir, stat } from "node:fs/promises";
159
+ import { join, resolve } from "node:path";
160
+
161
+ // src/async.ts
162
+ import { availableParallelism } from "node:os";
163
+ import { env } from "node:process";
164
+
165
+ // src/number.ts
166
+ function isNumber(value) {
167
+ return typeof value === "number" && isFinite(value);
168
+ }
169
+ var INTEGER_REGEX = /^-?\d+$/;
170
+ function toInt(value) {
171
+ try {
172
+ if (value == null) return;
173
+ const s = String(value).trim();
174
+ return INTEGER_REGEX.test(s) ? parseInt(s) : void 0;
175
+ } catch {
176
+ return;
177
+ }
178
+ }
179
+ function gt0(value) {
180
+ return isNumber(value) && value > 0;
181
+ }
182
+
183
+ // src/units.ts
184
+ var SecondMs = 1e3;
185
+ var MinuteMs = 60 * SecondMs;
186
+ var HourMs = 60 * MinuteMs;
187
+ var DayMs = 24 * HourMs;
188
+ var KiB = 1024;
189
+ var MiB = 1024 * KiB;
190
+ var GiB = 1024 * MiB;
191
+ var TiB = 1024 * GiB;
192
+ var f = 1023.995 / 1024;
193
+
108
194
  // src/async.ts
109
195
  var TimeoutError = class extends Error {
110
196
  constructor(message, captureStackTrace = true) {
@@ -128,6 +214,11 @@ async function withTimeout(opts) {
128
214
  desc + ": Expected timeoutMs to be > 0, but got " + timeoutMs
129
215
  );
130
216
  }
217
+ if (timeoutMs > DayMs) {
218
+ throw new TypeError(
219
+ desc + ": Invalid timeoutMs is too large: must be less than one day, but got " + timeoutMs
220
+ );
221
+ }
131
222
  if (timeoutMs === 0) {
132
223
  return opts.promise;
133
224
  }
@@ -178,20 +269,20 @@ async function mapConcurrent({
178
269
  while (executing.size >= maxConcurrency) {
179
270
  await Promise.race(executing);
180
271
  }
181
- const p2 = results[index] = fn(item).catch((error) => error);
182
- executing.add(p2);
183
- p2.finally(() => executing.delete(p2));
272
+ const p = results[index] = fn(item).catch((error) => error);
273
+ executing.add(p);
274
+ p.finally(() => executing.delete(p));
184
275
  }
185
276
  return Promise.all(results);
186
277
  }
187
278
 
188
279
  // src/fs.ts
189
- async function statAsync(path, options) {
190
- return stat(path, options);
280
+ async function statAsync(path2, options) {
281
+ return stat(path2, options);
191
282
  }
192
- async function canStatAsync(path) {
283
+ async function canStatAsync(path2) {
193
284
  try {
194
- return null != await statAsync(path);
285
+ return null != await statAsync(path2);
195
286
  } catch {
196
287
  return false;
197
288
  }
@@ -214,19 +305,13 @@ async function canReaddir(dir, timeoutMs) {
214
305
  });
215
306
  }
216
307
  async function _canReaddir(dir) {
217
- let d = void 0;
218
- try {
219
- d = await opendir(dir);
220
- await d.read();
221
- return true;
222
- } finally {
223
- if (d != null) void d.close();
224
- }
308
+ await (await opendir(dir)).close();
309
+ return true;
225
310
  }
226
311
 
227
312
  // src/hidden.ts
228
313
  import { rename } from "node:fs/promises";
229
- import { basename, dirname as dirname2, join as join2 } from "node:path";
314
+ import { basename, dirname as dirname3, join as join2 } from "node:path";
230
315
 
231
316
  // src/object.ts
232
317
  function isObject(value) {
@@ -307,30 +392,28 @@ function toError(cause) {
307
392
  }
308
393
 
309
394
  // src/path.ts
310
- import { dirname, resolve as resolve2 } from "node:path";
311
-
312
- // src/platform.ts
313
- import { platform } from "node:os";
314
- var p = platform();
315
- var isLinux = p === "linux";
316
- var isWindows = p === "win32";
317
- var isMacOS = p === "darwin";
318
-
319
- // src/path.ts
395
+ import { dirname as dirname2, resolve as resolve2 } from "node:path";
320
396
  function normalizePath(mountPoint) {
321
397
  if (isBlank(mountPoint)) return void 0;
322
398
  const result = isWindows ? normalizeWindowsPath(mountPoint) : normalizePosixPath(mountPoint);
323
399
  return result != null ? resolve2(result) : void 0;
324
400
  }
325
401
  function normalizePosixPath(mountPoint) {
326
- return isBlank(mountPoint) ? void 0 : mountPoint === "/" ? mountPoint : mountPoint.replace(/\/+$/, "");
402
+ if (isBlank(mountPoint)) return void 0;
403
+ if (mountPoint === "/") return mountPoint;
404
+ if (mountPoint[mountPoint.length - 1] !== "/") return mountPoint;
405
+ let end = mountPoint.length - 1;
406
+ while (end > 0 && mountPoint[end] === "/") {
407
+ end--;
408
+ }
409
+ return mountPoint.slice(0, end + 1);
327
410
  }
328
411
  function normalizeWindowsPath(mountPoint) {
329
412
  return /^[a-z]:$/i.test(mountPoint) ? mountPoint.toUpperCase() + "\\" : mountPoint;
330
413
  }
331
- function isRootDirectory(path) {
332
- const n = normalizePath(path);
333
- return n == null ? false : isWindows ? dirname(n) === n : n === "/";
414
+ function isRootDirectory(path2) {
415
+ const n = normalizePath(path2);
416
+ return n == null ? false : isWindows ? dirname2(n) === n : n === "/";
334
417
  }
335
418
 
336
419
  // src/hidden.ts
@@ -358,23 +441,23 @@ var LocalSupport = HiddenSupportByPlatform[process.platform]?.supported ?? {
358
441
  dotPrefix: false,
359
442
  systemFlag: false
360
443
  };
361
- async function isHidden(pathname, nativeFn) {
444
+ async function isHiddenImpl(pathname, nativeFn2) {
362
445
  const norm = normalizePath(pathname);
363
446
  if (norm == null) {
364
447
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
365
448
  }
366
- return LocalSupport.dotPrefix && isPosixHidden(norm) || LocalSupport.systemFlag && isSystemHidden(norm, nativeFn);
449
+ return LocalSupport.dotPrefix && isPosixHidden(norm) || LocalSupport.systemFlag && isSystemHidden(norm, nativeFn2);
367
450
  }
368
- async function isHiddenRecursive(path, nativeFn) {
369
- let norm = normalizePath(path);
451
+ async function isHiddenRecursiveImpl(path2, nativeFn2) {
452
+ let norm = normalizePath(path2);
370
453
  if (norm == null) {
371
- throw new Error("Invalid path: " + JSON.stringify(path));
454
+ throw new Error("Invalid path: " + JSON.stringify(path2));
372
455
  }
373
456
  while (!isRootDirectory(norm)) {
374
- if (await isHidden(norm, nativeFn)) {
457
+ if (await isHiddenImpl(norm, nativeFn2)) {
375
458
  return true;
376
459
  }
377
- norm = dirname2(norm);
460
+ norm = dirname3(norm);
378
461
  }
379
462
  return false;
380
463
  }
@@ -383,7 +466,7 @@ function createHiddenPosixPath(pathname, hidden) {
383
466
  if (norm == null) {
384
467
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
385
468
  }
386
- const dir = dirname2(norm);
469
+ const dir = dirname3(norm);
387
470
  const srcBase = basename(norm).replace(/^\./, "");
388
471
  const dest = join2(dir, (hidden ? "." : "") + srcBase);
389
472
  return dest;
@@ -401,22 +484,22 @@ function isPosixHidden(pathname) {
401
484
  const b = basename(pathname);
402
485
  return b.startsWith(".") && b !== "." && b !== "..";
403
486
  }
404
- async function isSystemHidden(pathname, nativeFn) {
487
+ async function isSystemHidden(pathname, nativeFn2) {
405
488
  if (!LocalSupport.systemFlag) {
406
489
  return false;
407
490
  }
408
491
  if (isWindows && isRootDirectory(pathname)) {
409
492
  return false;
410
493
  }
411
- return await canStatAsync(pathname) && await (await nativeFn()).isHidden(pathname);
494
+ return await canStatAsync(pathname) && await (await nativeFn2()).isHidden(pathname);
412
495
  }
413
- async function getHiddenMetadata(pathname, nativeFn) {
496
+ async function getHiddenMetadataImpl(pathname, nativeFn2) {
414
497
  const norm = normalizePath(pathname);
415
498
  if (norm == null) {
416
499
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
417
500
  }
418
501
  const dotPrefix = isPosixHidden(norm);
419
- const systemFlag = await isSystemHidden(norm, nativeFn);
502
+ const systemFlag = await isSystemHidden(norm, nativeFn2);
420
503
  return {
421
504
  hidden: dotPrefix || systemFlag,
422
505
  dotPrefix,
@@ -424,7 +507,7 @@ async function getHiddenMetadata(pathname, nativeFn) {
424
507
  supported: LocalSupport
425
508
  };
426
509
  }
427
- async function setHidden(pathname, hide, method, nativeFn) {
510
+ async function setHiddenImpl(pathname, hide, method, nativeFn2) {
428
511
  let norm = normalizePath(pathname);
429
512
  if (norm == null) {
430
513
  throw new Error("Invalid pathname: " + JSON.stringify(pathname));
@@ -456,7 +539,7 @@ async function setHidden(pathname, hide, method, nativeFn) {
456
539
  acted = true;
457
540
  }
458
541
  if (LocalSupport.systemFlag && (["all", "systemFlag"].includes(method) || !acted && method === "auto")) {
459
- await (await nativeFn()).setHidden(norm, hide);
542
+ await (await nativeFn2()).setHidden(norm, hide);
460
543
  actions.systemFlag = true;
461
544
  }
462
545
  return { pathname: norm, actions };
@@ -479,6 +562,9 @@ var SystemPathPatternsDefault = [
479
562
  "/run/user/*/gvfs",
480
563
  "/snap/**",
481
564
  "/sys/**",
565
+ "/tmp",
566
+ "/var/tmp",
567
+ // we aren't including /tmp/**, as some people temporarily mount volumes there, like /tmp/project.
482
568
  "**/#snapshot",
483
569
  // Synology and Kubernetes volume snapshots
484
570
  // windows for linux:
@@ -548,6 +634,49 @@ function optionsWithDefaults(overrides = {}) {
548
634
  };
549
635
  }
550
636
 
637
+ // src/string_enum.ts
638
+ function stringEnum(...o) {
639
+ const set = new Set(o);
640
+ const dict = {};
641
+ for (const key of o) {
642
+ dict[key] = key;
643
+ }
644
+ return {
645
+ ...dict,
646
+ values: Object.freeze([...set]),
647
+ size: set.size,
648
+ get: (s) => s != null && set.has(s) ? s : void 0
649
+ };
650
+ }
651
+
652
+ // src/volume_health_status.ts
653
+ var VolumeHealthStatuses = stringEnum(
654
+ "healthy",
655
+ "timeout",
656
+ "inaccessible",
657
+ "disconnected",
658
+ "unknown"
659
+ );
660
+ async function directoryStatus(dir, timeoutMs, canReaddirImpl = canReaddir) {
661
+ try {
662
+ if (await canReaddirImpl(dir, timeoutMs)) {
663
+ return { status: VolumeHealthStatuses.healthy };
664
+ }
665
+ } catch (error) {
666
+ debug("[directoryStatus] %s: %s", dir, error);
667
+ let status = VolumeHealthStatuses.unknown;
668
+ if (error instanceof TimeoutError) {
669
+ status = VolumeHealthStatuses.timeout;
670
+ } else if (isObject(error) && error instanceof Error && "code" in error) {
671
+ if (error.code === "EPERM" || error.code === "EACCES") {
672
+ status = VolumeHealthStatuses.inaccessible;
673
+ }
674
+ }
675
+ return { status, error: toError(error) };
676
+ }
677
+ return { status: VolumeHealthStatuses.unknown };
678
+ }
679
+
551
680
  // src/linux/dev_disk.ts
552
681
  import { readdir, readlink } from "node:fs/promises";
553
682
  import { join as join3, resolve as resolve3 } from "node:path";
@@ -605,8 +734,7 @@ import { readFile } from "node:fs/promises";
605
734
 
606
735
  // src/mount_point.ts
607
736
  function isMountPoint(obj) {
608
- if (!isObject(obj)) return false;
609
- return "mountPoint" in obj && isNotBlank(obj.mountPoint);
737
+ return isObject(obj) && "mountPoint" in obj && isNotBlank(obj.mountPoint);
610
738
  }
611
739
 
612
740
  // src/remote_info.ts
@@ -615,7 +743,7 @@ function isRemoteInfo(obj) {
615
743
  const { remoteHost, remoteShare } = obj;
616
744
  return isNotBlank(remoteHost) && isNotBlank(remoteShare);
617
745
  }
618
- var NETWORK_FS_TYPES = /* @__PURE__ */ new Set([
746
+ var NETWORK_FS_TYPE_ARRAY = [
619
747
  "9p",
620
748
  "afp",
621
749
  "afs",
@@ -623,9 +751,6 @@ var NETWORK_FS_TYPES = /* @__PURE__ */ new Set([
623
751
  "ceph",
624
752
  "cifs",
625
753
  "ftp",
626
- "fuse.cephfs",
627
- "fuse.glusterfs",
628
- "fuse.sshfs",
629
754
  "fuse",
630
755
  "gfs2",
631
756
  "glusterfs",
@@ -637,12 +762,31 @@ var NETWORK_FS_TYPES = /* @__PURE__ */ new Set([
637
762
  "smbfs",
638
763
  "sshfs",
639
764
  "webdav"
765
+ ];
766
+ var NETWORK_FS_TYPES = new Set(NETWORK_FS_TYPE_ARRAY);
767
+ var FS_TYPE_ALIASES = /* @__PURE__ */ new Map([
768
+ ["nfs1", "nfs"],
769
+ ["nfs2", "nfs"],
770
+ ["nfs3", "nfs"],
771
+ ["nfs4", "nfs4"],
772
+ ["fuse.sshfs", "sshfs"],
773
+ ["sshfs.fuse", "sshfs"],
774
+ ["davfs2", "webdav"],
775
+ ["davfs", "webdav"],
776
+ ["cifs.smb", "cifs"],
777
+ ["smbfs", "cifs"],
778
+ ["cephfs", "ceph"],
779
+ ["fuse.ceph", "ceph"],
780
+ ["fuse.cephfs", "ceph"],
781
+ ["rbd", "ceph"],
782
+ ["fuse.glusterfs", "glusterfs"]
640
783
  ]);
641
- function normalizeProtocol(protocol) {
642
- return (protocol ?? "").toLowerCase().replace(/:$/, "");
784
+ function normalizeFsType(fstype) {
785
+ const norm = toS(fstype).toLowerCase().replace(/:$/, "");
786
+ return FS_TYPE_ALIASES.get(norm) ?? norm;
643
787
  }
644
788
  function isRemoteFsType(fstype) {
645
- return isNotBlank(fstype) && NETWORK_FS_TYPES.has(normalizeProtocol(fstype));
789
+ return isNotBlank(fstype) && NETWORK_FS_TYPES.has(normalizeFsType(fstype));
646
790
  }
647
791
  function parseURL(s) {
648
792
  try {
@@ -663,18 +807,22 @@ function extractRemoteInfo(fsSpec) {
663
807
  uri: fsSpec
664
808
  };
665
809
  }
666
- const patterns = [
667
- // CIFS/SMB pattern: //hostname/share or //user@host/share
810
+ const patterns2 = [
668
811
  {
812
+ // CIFS/SMB pattern: //hostname/share or //user@host/share
669
813
  regex: /^\/\/(?:(?<remoteUser>[^/@]+)@)?(?<remoteHost>[^/@]+)\/(?<remoteShare>.+)$/
670
814
  },
671
- // NFS pattern: hostname:/share
672
815
  {
816
+ // sshfs pattern: sshfs#USER@HOST:REMOTE_PATH
817
+ regex: /^(?:(?<protocol>\w+)#)?(?<remoteUser>[^@]+)@(?<remoteHost>[^:]+):(?<remoteShare>.+)$/
818
+ },
819
+ {
820
+ // NFS pattern: hostname:/share
673
821
  protocol: "nfs",
674
822
  regex: /^(?<remoteHost>[^:]+):\/(?!\/)(?<remoteShare>.+)$/
675
823
  }
676
824
  ];
677
- for (const { protocol, regex } of patterns) {
825
+ for (const { protocol, regex } of patterns2) {
678
826
  const o = compactValues({
679
827
  protocol,
680
828
  remote: true,
@@ -689,8 +837,8 @@ function extractRemoteInfo(fsSpec) {
689
837
  const parsed = new URL(fsSpec);
690
838
  if (parsed != null) {
691
839
  debug("[extractRemoteInfo] parsed URL: %o", parsed);
692
- const protocol = normalizeProtocol(parsed.protocol);
693
- if (!isRemoteFsType(protocol)) {
840
+ const fstype = normalizeFsType(parsed.protocol);
841
+ if (!isRemoteFsType(fstype)) {
694
842
  return {
695
843
  uri: fsSpec,
696
844
  remote: false
@@ -698,7 +846,7 @@ function extractRemoteInfo(fsSpec) {
698
846
  } else {
699
847
  return compactValues({
700
848
  uri: fsSpec,
701
- protocol,
849
+ protocol: fstype,
702
850
  remote: true,
703
851
  remoteUser: parsed.username,
704
852
  remoteHost: parsed.hostname,
@@ -714,18 +862,18 @@ function extractRemoteInfo(fsSpec) {
714
862
 
715
863
  // src/glob.ts
716
864
  var cache = /* @__PURE__ */ new Map();
717
- function compileGlob(patterns) {
718
- if (patterns == null || patterns.length === 0) {
865
+ function compileGlob(patterns2) {
866
+ if (patterns2 == null || patterns2.length === 0) {
719
867
  return NeverMatchRE;
720
868
  }
721
- const patternsKey = JSON.stringify(patterns);
869
+ const patternsKey = JSON.stringify(patterns2);
722
870
  {
723
871
  const prior = cache.get(patternsKey);
724
872
  if (prior != null) {
725
873
  return prior;
726
874
  }
727
875
  }
728
- const sorted = patterns.slice().filter(isNotBlank).sort();
876
+ const sorted = patterns2.slice().filter(isNotBlank).sort();
729
877
  const sortedKey = JSON.stringify(sorted);
730
878
  {
731
879
  const prior = cache.get(sortedKey);
@@ -742,8 +890,8 @@ function compileGlob(patterns) {
742
890
  cache.set(sortedKey, result);
743
891
  return result;
744
892
  }
745
- function _compileGlob(patterns) {
746
- const regexPatterns = patterns.map((pattern) => {
893
+ function _compileGlob(patterns2) {
894
+ const regexPatterns = patterns2.map((pattern) => {
747
895
  let regex = "";
748
896
  let i = 0;
749
897
  while (i < pattern.length) {
@@ -849,7 +997,7 @@ function mountEntryToPartialVolumeMetadata(entry, options = {}) {
849
997
  mountFrom: entry.fs_spec,
850
998
  isSystemVolume: isSystemVolume(entry.fs_file, entry.fs_vfstype, options),
851
999
  remote: false,
852
- // < default to false
1000
+ // < default to false, but it may be overridden by extractRemoteInfo
853
1001
  ...extractRemoteInfo(entry.fs_spec)
854
1002
  };
855
1003
  }
@@ -947,16 +1095,16 @@ async function getLinuxMtabMetadata(mountPoint, opts) {
947
1095
  }
948
1096
 
949
1097
  // src/unc.ts
950
- function parseUNCPath(path) {
951
- if (path == null || isBlank(path) || !isString(path)) {
1098
+ function parseUNCPath(path2) {
1099
+ if (path2 == null || isBlank(path2) || !isString(path2)) {
952
1100
  return;
953
1101
  }
954
- if (!path.startsWith("\\\\") && !path.startsWith("//")) {
1102
+ if (!path2.startsWith("\\\\") && !path2.startsWith("//")) {
955
1103
  return;
956
1104
  }
957
- const isForwardSlash = path.startsWith("//");
1105
+ const isForwardSlash = path2.startsWith("//");
958
1106
  const slashChar = isForwardSlash ? "/" : "\\";
959
- const parts = path.slice(2).split(slashChar);
1107
+ const parts = path2.slice(2).split(slashChar);
960
1108
  if (parts.length < 2) {
961
1109
  return;
962
1110
  }
@@ -969,7 +1117,7 @@ function parseUNCPath(path) {
969
1117
  return;
970
1118
  }
971
1119
  const wrongSlash = isForwardSlash ? "\\" : "/";
972
- if (path.includes(wrongSlash)) {
1120
+ if (path2.includes(wrongSlash)) {
973
1121
  return;
974
1122
  }
975
1123
  return { remoteHost, remoteShare, remote: true };
@@ -981,49 +1129,6 @@ function extractUUID(uuid) {
981
1129
  return toS(uuid).match(uuidRegex)?.[0];
982
1130
  }
983
1131
 
984
- // src/string_enum.ts
985
- function stringEnum(...o) {
986
- const set = new Set(o);
987
- const dict = {};
988
- for (const key of o) {
989
- dict[key] = key;
990
- }
991
- return {
992
- ...dict,
993
- values: Object.freeze([...set]),
994
- size: set.size,
995
- get: (s) => s != null && set.has(s) ? s : void 0
996
- };
997
- }
998
-
999
- // src/volume_health_status.ts
1000
- var VolumeHealthStatuses = stringEnum(
1001
- "healthy",
1002
- "timeout",
1003
- "inaccessible",
1004
- "disconnected",
1005
- "unknown"
1006
- );
1007
- async function directoryStatus(dir, timeoutMs, canReaddirImpl = canReaddir) {
1008
- try {
1009
- if (await canReaddirImpl(dir, timeoutMs)) {
1010
- return { status: VolumeHealthStatuses.healthy };
1011
- }
1012
- } catch (error) {
1013
- debug("[directoryStatus] %s: %s", dir, error);
1014
- let status = VolumeHealthStatuses.unknown;
1015
- if (error instanceof TimeoutError) {
1016
- status = VolumeHealthStatuses.timeout;
1017
- } else if (isObject(error) && error instanceof Error && "code" in error) {
1018
- if (error.code === "EPERM" || error.code === "EACCES") {
1019
- status = VolumeHealthStatuses.inaccessible;
1020
- }
1021
- }
1022
- return { status, error: toError(error) };
1023
- }
1024
- return { status: VolumeHealthStatuses.unknown };
1025
- }
1026
-
1027
1132
  // src/array.ts
1028
1133
  function uniqBy(arr, keyFn) {
1029
1134
  const seen = /* @__PURE__ */ new Set();
@@ -1036,21 +1141,21 @@ function uniqBy(arr, keyFn) {
1036
1141
  }
1037
1142
 
1038
1143
  // src/volume_mount_points.ts
1039
- async function getVolumeMountPoints(opts, nativeFn) {
1040
- const p2 = _getVolumeMountPoints(opts, nativeFn);
1041
- return isWindows ? p2 : withTimeout({ desc: "getVolumeMountPoints", ...opts, promise: p2 });
1144
+ async function getVolumeMountPointsImpl(opts, nativeFn2) {
1145
+ const p = _getVolumeMountPoints(opts, nativeFn2);
1146
+ return isWindows ? p : withTimeout({ desc: "getVolumeMountPoints", ...opts, promise: p });
1042
1147
  }
1043
- async function _getVolumeMountPoints(o, nativeFn) {
1148
+ async function _getVolumeMountPoints(o, nativeFn2) {
1044
1149
  debug("[getVolumeMountPoints] gathering mount points with options: %o", o);
1045
1150
  const raw = await (isWindows || isMacOS ? (async () => {
1046
1151
  debug("[getVolumeMountPoints] using native implementation");
1047
- const points = await (await nativeFn()).getVolumeMountPoints(o);
1152
+ const points = await (await nativeFn2()).getVolumeMountPoints(o);
1048
1153
  debug(
1049
1154
  "[getVolumeMountPoints] native returned %d mount points",
1050
1155
  points.length
1051
1156
  );
1052
1157
  return points;
1053
- })() : getLinuxMountPoints(nativeFn, o));
1158
+ })() : getLinuxMountPoints(nativeFn2, o));
1054
1159
  debug("[getVolumeMountPoints] raw mount points: %o", raw);
1055
1160
  const compacted = raw.map((ea) => compactValues(ea)).filter((ea) => isNotBlank(ea.mountPoint));
1056
1161
  for (const ea of compacted) {
@@ -1088,20 +1193,20 @@ async function _getVolumeMountPoints(o, nativeFn) {
1088
1193
  }
1089
1194
 
1090
1195
  // src/volume_metadata.ts
1091
- async function getVolumeMetadata(o, nativeFn) {
1196
+ async function getVolumeMetadataImpl(o, nativeFn2) {
1092
1197
  if (isBlank(o.mountPoint)) {
1093
1198
  throw new TypeError(
1094
1199
  "Invalid mountPoint: got " + JSON.stringify(o.mountPoint)
1095
1200
  );
1096
1201
  }
1097
- const p2 = _getVolumeMetadata(o, nativeFn);
1098
- return isWindows ? p2 : withTimeout({
1202
+ const p = _getVolumeMetadata(o, nativeFn2);
1203
+ return isWindows ? p : withTimeout({
1099
1204
  desc: "getVolumeMetadata()",
1100
1205
  timeoutMs: o.timeoutMs,
1101
- promise: p2
1206
+ promise: p
1102
1207
  });
1103
1208
  }
1104
- async function _getVolumeMetadata(o, nativeFn) {
1209
+ async function _getVolumeMetadata(o, nativeFn2) {
1105
1210
  o = optionsWithDefaults(o);
1106
1211
  const norm = normalizePath(o.mountPoint);
1107
1212
  if (norm == null) {
@@ -1143,7 +1248,7 @@ async function _getVolumeMetadata(o, nativeFn) {
1143
1248
  debug("[getVolumeMetadata] using device: %s", device);
1144
1249
  }
1145
1250
  debug("[getVolumeMetadata] requesting native metadata");
1146
- const metadata = await (await nativeFn()).getVolumeMetadata(o);
1251
+ const metadata = await (await nativeFn2()).getVolumeMetadata(o);
1147
1252
  debug("[getVolumeMetadata] native metadata: %o", metadata);
1148
1253
  const remoteInfo = mtabInfo ?? extractRemoteInfo(metadata.uri) ?? extractRemoteInfo(metadata.mountFrom) ?? (isWindows ? parseUNCPath(o.mountPoint) : void 0);
1149
1254
  debug("[getVolumeMetadata] extracted remote info: %o", remoteInfo);
@@ -1174,10 +1279,10 @@ async function _getVolumeMetadata(o, nativeFn) {
1174
1279
  debug("[getVolumeMetadata] final result for %s: %o", o.mountPoint, result);
1175
1280
  return compactValues(result);
1176
1281
  }
1177
- async function getAllVolumeMetadata(opts, nativeFn) {
1282
+ async function getAllVolumeMetadataImpl(opts, nativeFn2) {
1178
1283
  const o = optionsWithDefaults(opts);
1179
1284
  debug("[getAllVolumeMetadata] starting with options: %o", o);
1180
- const arr = await getVolumeMountPoints(o, nativeFn);
1285
+ const arr = await getVolumeMountPointsImpl(o, nativeFn2);
1181
1286
  debug("[getAllVolumeMetadata] found %d mount points", arr.length);
1182
1287
  const unhealthyMountPoints = arr.filter(
1183
1288
  (ea) => ea.status != null && ea.status !== VolumeHealthStatuses.healthy
@@ -1207,7 +1312,7 @@ async function getAllVolumeMetadata(opts, nativeFn) {
1207
1312
  const results = await mapConcurrent({
1208
1313
  maxConcurrency: o.maxConcurrency,
1209
1314
  items: opts?.includeSystemVolumes ?? IncludeSystemVolumesDefault ? healthy : healthy.filter((ea) => !ea.isSystemVolume),
1210
- fn: async (mp) => getVolumeMetadata({ ...mp, ...o }, nativeFn).catch((error) => ({
1315
+ fn: async (mp) => getVolumeMetadataImpl({ ...mp, ...o }, nativeFn2).catch((error) => ({
1211
1316
  mountPoint: mp.mountPoint,
1212
1317
  error
1213
1318
  }))
@@ -1225,49 +1330,52 @@ async function getAllVolumeMetadata(opts, nativeFn) {
1225
1330
  );
1226
1331
  }
1227
1332
 
1228
- // src/setup.ts
1229
- function setup(dirname4) {
1230
- const nativeFn = defer(async () => {
1231
- const start = Date.now();
1232
- try {
1233
- const dir = await findAncestorDir(dirname4, "binding.gyp");
1234
- if (dir == null) {
1235
- throw new Error(
1236
- "Could not find bindings.gyp in any ancestor directory of " + dirname4
1237
- );
1238
- }
1239
- const bindings = NodeGypBuild(dir);
1240
- bindings.setDebugLogging(isDebugEnabled());
1241
- bindings.setDebugPrefix(debugLogContext() + ":native");
1242
- return bindings;
1243
- } catch (error) {
1244
- debug("Loading native bindings failed: %s", error);
1245
- throw error;
1246
- } finally {
1247
- debug(`Native bindings took %d ms to load`, Date.now() - start);
1333
+ // src/index.ts
1334
+ var nativeFn = defer2(async () => {
1335
+ const start = Date.now();
1336
+ try {
1337
+ const dirname4 = _dirname();
1338
+ const dir = await findAncestorDir(dirname4, "binding.gyp");
1339
+ if (dir == null) {
1340
+ throw new Error(
1341
+ "Could not find bindings.gyp in any ancestor directory of " + dirname4
1342
+ );
1248
1343
  }
1249
- });
1250
- return {
1251
- getVolumeMountPoints: (opts = {}) => getVolumeMountPoints(optionsWithDefaults(opts), nativeFn),
1252
- getVolumeMetadata: (mountPoint, opts = {}) => getVolumeMetadata({ ...optionsWithDefaults(opts), mountPoint }, nativeFn),
1253
- getAllVolumeMetadata: (opts = {}) => getAllVolumeMetadata(optionsWithDefaults(opts), nativeFn),
1254
- isHidden: (pathname) => isHidden(pathname, nativeFn),
1255
- isHiddenRecursive: (pathname) => isHiddenRecursive(pathname, nativeFn),
1256
- getHiddenMetadata: (pathname) => getHiddenMetadata(pathname, nativeFn),
1257
- setHidden: (pathname, hidden, method = "auto") => setHidden(pathname, hidden, method, nativeFn)
1258
- };
1344
+ const bindings = NodeGypBuild(dir);
1345
+ bindings.setDebugLogging(isDebugEnabled());
1346
+ bindings.setDebugPrefix(debugLogContext() + ":native");
1347
+ return bindings;
1348
+ } catch (error) {
1349
+ debug("Loading native bindings failed: %s", error);
1350
+ throw error;
1351
+ } finally {
1352
+ debug(`Native bindings took %d ms to load`, Date.now() - start);
1353
+ }
1354
+ });
1355
+ function getVolumeMountPoints(opts) {
1356
+ return getVolumeMountPointsImpl(optionsWithDefaults(opts), nativeFn);
1357
+ }
1358
+ function getVolumeMetadata(mountPoint, opts) {
1359
+ return getVolumeMetadataImpl(
1360
+ { ...optionsWithDefaults(opts), mountPoint },
1361
+ nativeFn
1362
+ );
1363
+ }
1364
+ function getAllVolumeMetadata(opts) {
1365
+ return getAllVolumeMetadataImpl(optionsWithDefaults(opts), nativeFn);
1366
+ }
1367
+ function isHidden(pathname) {
1368
+ return isHiddenImpl(pathname, nativeFn);
1369
+ }
1370
+ function isHiddenRecursive(pathname) {
1371
+ return isHiddenRecursiveImpl(pathname, nativeFn);
1372
+ }
1373
+ function getHiddenMetadata(pathname) {
1374
+ return getHiddenMetadataImpl(pathname, nativeFn);
1375
+ }
1376
+ function setHidden(pathname, hidden, method = "auto") {
1377
+ return setHiddenImpl(pathname, hidden, method, nativeFn);
1259
1378
  }
1260
-
1261
- // src/index.mts
1262
- var {
1263
- getVolumeMountPoints: getVolumeMountPoints2,
1264
- getVolumeMetadata: getVolumeMetadata2,
1265
- getAllVolumeMetadata: getAllVolumeMetadata2,
1266
- isHidden: isHidden2,
1267
- isHiddenRecursive: isHiddenRecursive2,
1268
- getHiddenMetadata: getHiddenMetadata2,
1269
- setHidden: setHidden2
1270
- } = setup(dirname3(fileURLToPath(import.meta.url)));
1271
1379
  export {
1272
1380
  IncludeSystemVolumesDefault,
1273
1381
  LinuxMountTablePathsDefault,
@@ -1276,13 +1384,13 @@ export {
1276
1384
  SystemPathPatternsDefault,
1277
1385
  TimeoutMsDefault,
1278
1386
  VolumeHealthStatuses,
1279
- getAllVolumeMetadata2 as getAllVolumeMetadata,
1280
- getHiddenMetadata2 as getHiddenMetadata,
1281
- getVolumeMetadata2 as getVolumeMetadata,
1282
- getVolumeMountPoints2 as getVolumeMountPoints,
1283
- isHidden2 as isHidden,
1284
- isHiddenRecursive2 as isHiddenRecursive,
1387
+ getAllVolumeMetadata,
1388
+ getHiddenMetadata,
1389
+ getVolumeMetadata,
1390
+ getVolumeMountPoints,
1391
+ isHidden,
1392
+ isHiddenRecursive,
1285
1393
  optionsWithDefaults,
1286
- setHidden2 as setHidden
1394
+ setHidden
1287
1395
  };
1288
1396
  //# sourceMappingURL=index.mjs.map