@photostructure/fs-metadata 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +17 -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/object.d.ts +8 -3
  16. package/dist/types/options.d.ts +2 -48
  17. package/dist/types/platform.d.ts +1 -0
  18. package/dist/types/remote_info.d.ts +2 -34
  19. package/dist/types/stack_path.d.ts +2 -0
  20. package/dist/types/system_volume.d.ts +2 -2
  21. package/dist/types/types/hidden_metadata.d.ts +32 -0
  22. package/dist/types/types/mount_point.d.ts +46 -0
  23. package/dist/types/types/native_bindings.d.ts +3 -3
  24. package/dist/types/types/options.d.ts +47 -0
  25. package/dist/types/types/remote_info.d.ts +33 -0
  26. package/dist/types/types/volume_metadata.d.ts +46 -0
  27. package/dist/types/unc.d.ts +1 -1
  28. package/dist/types/units.d.ts +25 -3
  29. package/dist/types/volume_metadata.d.ts +4 -50
  30. package/dist/types/volume_mount_points.d.ts +3 -6
  31. package/jest.config.base.cjs +63 -0
  32. package/jest.config.cjs +3 -16
  33. package/package.json +13 -15
  34. package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
  35. package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
  36. package/src/async.ts +9 -0
  37. package/src/darwin/hidden.cpp +14 -0
  38. package/src/darwin/raii_utils.h +85 -0
  39. package/src/darwin/volume_metadata.cpp +35 -60
  40. package/src/darwin/volume_mount_points.cpp +31 -22
  41. package/src/debuglog.ts +6 -2
  42. package/src/defer.ts +1 -1
  43. package/src/dirname.ts +13 -0
  44. package/src/global.d.ts +1 -0
  45. package/src/hidden.ts +6 -42
  46. package/src/{exports.ts → index.ts} +75 -30
  47. package/src/linux/mount_points.ts +4 -2
  48. package/src/linux/mtab.ts +3 -3
  49. package/src/mount_point.ts +2 -53
  50. package/src/object.ts +8 -3
  51. package/src/options.ts +5 -54
  52. package/src/path.ts +12 -5
  53. package/src/platform.ts +5 -5
  54. package/src/remote_info.ts +44 -49
  55. package/src/stack_path.ts +71 -0
  56. package/src/system_volume.ts +3 -6
  57. package/src/test-utils/assert.ts +1 -1
  58. package/src/test-utils/debuglog-child.ts +15 -0
  59. package/src/types/hidden_metadata.ts +38 -0
  60. package/src/types/mount_point.ts +53 -0
  61. package/src/types/native_bindings.ts +3 -3
  62. package/src/types/options.ts +54 -0
  63. package/src/types/remote_info.ts +35 -0
  64. package/src/types/volume_metadata.ts +52 -0
  65. package/src/unc.ts +1 -1
  66. package/src/units.ts +39 -7
  67. package/src/volume_metadata.ts +9 -66
  68. package/src/volume_mount_points.ts +3 -6
  69. package/tsup.config.ts +1 -0
  70. package/dist/types/exports.d.ts +0 -99
  71. package/dist/types/setup.d.ts +0 -2
  72. package/src/index.cts +0 -15
  73. package/src/index.mts +0 -17
  74. package/src/setup.ts +0 -69
package/src/hidden.ts CHANGED
@@ -6,45 +6,9 @@ import { WrappedError } from "./error.js";
6
6
  import { canStatAsync, statAsync } from "./fs.js";
7
7
  import { isRootDirectory, normalizePath } from "./path.js";
8
8
  import { isWindows } from "./platform.js";
9
+ import type { HiddenMetadata } from "./types/hidden_metadata.js";
9
10
  import type { NativeBindingsFn } from "./types/native_bindings.js";
10
11
 
11
- /**
12
- * Represents the detailed state of a file or directory's hidden attribute
13
- */
14
- export interface HiddenMetadata {
15
- /**
16
- * Whether the item is considered hidden by any method
17
- */
18
- hidden: boolean;
19
-
20
- /**
21
- * Whether the item has a dot prefix (POSIX-style hidden). Windows doesn't
22
- * care about dot prefixes.
23
- */
24
- dotPrefix: boolean;
25
-
26
- /**
27
- * Whether the item has system hidden flags set, like via `chflags` on macOS
28
- * or on Windows via `GetFileAttributesW`
29
- */
30
- systemFlag: boolean;
31
-
32
- /**
33
- * Indicates which hiding methods are supported on the current platform
34
- */
35
- supported: {
36
- /**
37
- * Whether dot prefix hiding is supported on the current operating system
38
- */
39
- dotPrefix: boolean;
40
-
41
- /**
42
- * Whether system flag hiding is supported
43
- */
44
- systemFlag: boolean;
45
- };
46
- }
47
-
48
12
  const HiddenSupportByPlatform: Partial<
49
13
  Record<NodeJS.Platform, Pick<HiddenMetadata, "supported">>
50
14
  > = {
@@ -79,7 +43,7 @@ export const LocalSupport = HiddenSupportByPlatform[process.platform]
79
43
  * @returns A boolean indicating if the item is hidden
80
44
  * @throws {Error} If the file doesn't exist or permissions are insufficient
81
45
  */
82
- export async function isHidden(
46
+ export async function isHiddenImpl(
83
47
  pathname: string,
84
48
  nativeFn: NativeBindingsFn,
85
49
  ): Promise<boolean> {
@@ -93,7 +57,7 @@ export async function isHidden(
93
57
  );
94
58
  }
95
59
 
96
- export async function isHiddenRecursive(
60
+ export async function isHiddenRecursiveImpl(
97
61
  path: string,
98
62
  nativeFn: NativeBindingsFn,
99
63
  ): Promise<boolean> {
@@ -102,7 +66,7 @@ export async function isHiddenRecursive(
102
66
  throw new Error("Invalid path: " + JSON.stringify(path));
103
67
  }
104
68
  while (!isRootDirectory(norm)) {
105
- if (await isHidden(norm, nativeFn)) {
69
+ if (await isHiddenImpl(norm, nativeFn)) {
106
70
  return true;
107
71
  }
108
72
  norm = dirname(norm);
@@ -165,7 +129,7 @@ async function isSystemHidden(
165
129
  * @returns An object containing detailed hidden state information
166
130
  * @throws {Error} If the file doesn't exist or permissions are insufficient
167
131
  */
168
- export async function getHiddenMetadata(
132
+ export async function getHiddenMetadataImpl(
169
133
  pathname: string,
170
134
  nativeFn: NativeBindingsFn,
171
135
  ): Promise<HiddenMetadata> {
@@ -193,7 +157,7 @@ export type SetHiddenResult = {
193
157
  };
194
158
  };
195
159
 
196
- export async function setHidden(
160
+ export async function setHiddenImpl(
197
161
  pathname: string,
198
162
  hide: boolean,
199
163
  method: HideMethod,
@@ -1,8 +1,17 @@
1
- // src/exports.ts
1
+ // src/index.ts
2
2
 
3
- import type { HiddenMetadata, HideMethod, SetHiddenResult } from "./hidden.js";
4
- import type { MountPoint } from "./mount_point.js";
5
- import type { Options } from "./options.js";
3
+ import NodeGypBuild from "node-gyp-build";
4
+ import { debug, debugLogContext, isDebugEnabled } from "./debuglog.js";
5
+ import { defer } from "./defer.js";
6
+ import { _dirname } from "./dirname.js";
7
+ import { findAncestorDir } from "./fs.js";
8
+ import type { HideMethod, SetHiddenResult } from "./hidden.js";
9
+ import {
10
+ getHiddenMetadataImpl,
11
+ isHiddenImpl,
12
+ isHiddenRecursiveImpl,
13
+ setHiddenImpl,
14
+ } from "./hidden.js";
6
15
  import {
7
16
  IncludeSystemVolumesDefault,
8
17
  LinuxMountTablePathsDefault,
@@ -18,10 +27,19 @@ import type {
18
27
  StringEnumType,
19
28
  } from "./string_enum.js";
20
29
  import type { SystemVolumeConfig } from "./system_volume.js";
30
+ import type { HiddenMetadata } from "./types/hidden_metadata.js";
31
+ import type { MountPoint } from "./types/mount_point.js";
32
+ import { NativeBindings } from "./types/native_bindings.js";
33
+ import type { Options } from "./types/options.js";
34
+ import type { VolumeMetadata } from "./types/volume_metadata.js";
21
35
  import type { VolumeHealthStatus } from "./volume_health_status.js";
22
36
  import { VolumeHealthStatuses } from "./volume_health_status.js";
23
- import type { VolumeMetadata } from "./volume_metadata.js";
37
+ import {
38
+ getAllVolumeMetadataImpl,
39
+ getVolumeMetadataImpl,
40
+ } from "./volume_metadata.js";
24
41
  import type { GetVolumeMountPointOptions } from "./volume_mount_points.js";
42
+ import { getVolumeMountPointsImpl } from "./volume_mount_points.js";
25
43
 
26
44
  export type {
27
45
  GetVolumeMountPointOptions,
@@ -38,6 +56,28 @@ export type {
38
56
  VolumeMetadata,
39
57
  };
40
58
 
59
+ const nativeFn = defer<Promise<NativeBindings>>(async () => {
60
+ const start = Date.now();
61
+ try {
62
+ const dirname = _dirname();
63
+ const dir = await findAncestorDir(dirname, "binding.gyp");
64
+ if (dir == null) {
65
+ throw new Error(
66
+ "Could not find bindings.gyp in any ancestor directory of " + dirname,
67
+ );
68
+ }
69
+ const bindings = NodeGypBuild(dir) as NativeBindings;
70
+ bindings.setDebugLogging(isDebugEnabled());
71
+ bindings.setDebugPrefix(debugLogContext() + ":native");
72
+ return bindings;
73
+ } catch (error) {
74
+ debug("Loading native bindings failed: %s", error);
75
+ throw error;
76
+ } finally {
77
+ debug(`Native bindings took %d ms to load`, Date.now() - start);
78
+ }
79
+ });
80
+
41
81
  /**
42
82
  * List all active local and remote mount points on the system.
43
83
  *
@@ -48,9 +88,11 @@ export type {
48
88
  *
49
89
  * @param opts Optional filesystem operation settings to override default values
50
90
  */
51
- export declare function getVolumeMountPoints(
91
+ export function getVolumeMountPoints(
52
92
  opts?: Partial<GetVolumeMountPointOptions>,
53
- ): Promise<MountPoint[]>;
93
+ ): Promise<MountPoint[]> {
94
+ return getVolumeMountPointsImpl(optionsWithDefaults(opts), nativeFn);
95
+ }
54
96
 
55
97
  /**
56
98
  * Get metadata for the volume at the given mount point.
@@ -58,10 +100,15 @@ export declare function getVolumeMountPoints(
58
100
  * @param mountPoint Must be a non-blank string
59
101
  * @param opts Optional filesystem operation settings
60
102
  */
61
- export declare function getVolumeMetadata(
103
+ export function getVolumeMetadata(
62
104
  mountPoint: string,
63
105
  opts?: Partial<Pick<Options, "timeoutMs">>,
64
- ): Promise<VolumeMetadata>;
106
+ ): Promise<VolumeMetadata> {
107
+ return getVolumeMetadataImpl(
108
+ { ...optionsWithDefaults(opts), mountPoint },
109
+ nativeFn,
110
+ );
111
+ }
65
112
 
66
113
  /**
67
114
  * Retrieves metadata for all mounted volumes with optional filtering and
@@ -74,15 +121,17 @@ export declare function getVolumeMetadata(
74
121
  * Defaults to the system's available parallelism: see
75
122
  * {@link https://nodejs.org/api/os.html#osavailableparallelism | os.availableParallelism()}
76
123
  * @param opts.timeoutMs - Maximum time to wait for
77
- * {@link getVolumeMountPoints}, as well as **each** {@link getVolumeMetadata}
124
+ * {@link getVolumeMountPointsImpl}, as well as **each** {@link getVolumeMetadataImpl}
78
125
  * to complete. Defaults to {@link TimeoutMsDefault}
79
126
  * @returns Promise that resolves to an array of either VolumeMetadata objects
80
127
  * or error objects containing the mount point and error
81
128
  * @throws Never - errors are caught and returned as part of the result array
82
129
  */
83
- export declare function getAllVolumeMetadata(
130
+ export function getAllVolumeMetadata(
84
131
  opts?: Partial<Options> & { includeSystemVolumes?: boolean },
85
- ): Promise<VolumeMetadata[]>;
132
+ ): Promise<VolumeMetadata[]> {
133
+ return getAllVolumeMetadataImpl(optionsWithDefaults(opts), nativeFn);
134
+ }
86
135
 
87
136
  /**
88
137
  * Check if a file or directory is hidden.
@@ -93,7 +142,9 @@ export declare function getAllVolumeMetadata(
93
142
  * @param pathname Path to file or directory
94
143
  * @returns Promise resolving to boolean indicating hidden state
95
144
  */
96
- export declare function isHidden(pathname: string): Promise<boolean>;
145
+ export function isHidden(pathname: string): Promise<boolean> {
146
+ return isHiddenImpl(pathname, nativeFn);
147
+ }
97
148
 
98
149
  /**
99
150
  * Check if a file or directory is hidden, or if any of its ancestor
@@ -102,7 +153,9 @@ export declare function isHidden(pathname: string): Promise<boolean>;
102
153
  * @param pathname Path to file or directory
103
154
  * @returns Promise resolving to boolean indicating hidden state
104
155
  */
105
- export declare function isHiddenRecursive(pathname: string): Promise<boolean>;
156
+ export function isHiddenRecursive(pathname: string): Promise<boolean> {
157
+ return isHiddenRecursiveImpl(pathname, nativeFn);
158
+ }
106
159
 
107
160
  /**
108
161
  * Get detailed metadata about the hidden state of a file or directory.
@@ -110,9 +163,9 @@ export declare function isHiddenRecursive(pathname: string): Promise<boolean>;
110
163
  * @param pathname Path to file or directory
111
164
  * @returns Promise resolving to metadata about the hidden state
112
165
  */
113
- export declare function getHiddenMetadata(
114
- pathname: string,
115
- ): Promise<HiddenMetadata>;
166
+ export function getHiddenMetadata(pathname: string): Promise<HiddenMetadata> {
167
+ return getHiddenMetadataImpl(pathname, nativeFn);
168
+ }
116
169
 
117
170
  /**
118
171
  * Set the hidden state of a file or directory
@@ -128,21 +181,13 @@ export declare function getHiddenMetadata(
128
181
  * @throws {Error} If the file doesn't exist, permissions are insufficient, or
129
182
  * the requested method is unsupported
130
183
  */
131
- export declare function setHidden(
184
+ export function setHidden(
132
185
  pathname: string,
133
186
  hidden: boolean,
134
- method?: HideMethod,
135
- ): Promise<SetHiddenResult>;
136
-
137
- export type ExportedFunctions = {
138
- getVolumeMountPoints: typeof getVolumeMountPoints;
139
- getVolumeMetadata: typeof getVolumeMetadata;
140
- getAllVolumeMetadata: typeof getAllVolumeMetadata;
141
- isHidden: typeof isHidden;
142
- isHiddenRecursive: typeof isHiddenRecursive;
143
- getHiddenMetadata: typeof getHiddenMetadata;
144
- setHidden: typeof setHidden;
145
- };
187
+ method: HideMethod = "auto",
188
+ ): Promise<SetHiddenResult> {
189
+ return setHiddenImpl(pathname, hidden, method, nativeFn);
190
+ }
146
191
 
147
192
  export {
148
193
  IncludeSystemVolumesDefault,
@@ -2,10 +2,12 @@
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { debug } from "../debuglog.js";
4
4
  import { toError, WrappedError } from "../error.js";
5
- import { isMountPoint, type MountPoint } from "../mount_point.js";
5
+ import { isMountPoint } from "../mount_point.js";
6
6
  import { compactValues } from "../object.js";
7
- import { optionsWithDefaults, type Options } from "../options.js";
7
+ import { optionsWithDefaults } from "../options.js";
8
+ import { type MountPoint } from "../types/mount_point.js";
8
9
  import type { NativeBindingsFn } from "../types/native_bindings.js";
10
+ import type { Options } from "../types/options.js";
9
11
  import { MountEntry, mountEntryToMountPoint, parseMtab } from "./mtab.js";
10
12
 
11
13
  export async function getLinuxMountPoints(
package/src/linux/mtab.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  // src/linux/mtab.ts
2
2
 
3
- import { MountPoint } from "../mount_point.js";
4
3
  import { toInt } from "../number.js";
5
4
  import { normalizePosixPath } from "../path.js";
6
5
  import { extractRemoteInfo } from "../remote_info.js";
@@ -11,7 +10,8 @@ import {
11
10
  toNotBlank,
12
11
  } from "../string.js";
13
12
  import { isSystemVolume, SystemVolumeConfig } from "../system_volume.js";
14
- import { VolumeMetadata } from "../volume_metadata.js";
13
+ import type { MountPoint } from "../types/mount_point.js";
14
+ import type { VolumeMetadata } from "../types/volume_metadata.js";
15
15
 
16
16
  /**
17
17
  * Represents an entry in the mount table.
@@ -70,7 +70,7 @@ export function mountEntryToPartialVolumeMetadata(
70
70
  fstype: entry.fs_vfstype,
71
71
  mountFrom: entry.fs_spec,
72
72
  isSystemVolume: isSystemVolume(entry.fs_file, entry.fs_vfstype, options),
73
- remote: false, // < default to false
73
+ remote: false, // < default to false, but it may be overridden by extractRemoteInfo
74
74
  ...extractRemoteInfo(entry.fs_spec),
75
75
  };
76
76
  }
@@ -1,58 +1,7 @@
1
1
  import { isObject } from "./object";
2
2
  import { isNotBlank } from "./string";
3
- import { VolumeHealthStatus } from "./volume_health_status";
4
-
5
- /**
6
- * A mount point is a location in the file system where a volume is mounted.
7
- *
8
- * @see https://en.wikipedia.org/wiki/Mount_(computing)
9
- */
10
- export interface MountPoint {
11
- /**
12
- * Mount location (like "/" or "C:\").
13
- */
14
- mountPoint: string;
15
-
16
- /**
17
- * The type of file system on the volume, like `ext4`, `apfs`, or `ntfs`.
18
- *
19
- * Note: on Windows this may show as "ntfs" for remote filesystems, as that
20
- * is how the filesystem is presented to the OS.
21
- */
22
- fstype?: string;
23
-
24
- /**
25
- * On Windows, returns the health status of the volume.
26
- *
27
- * Note that this is only available on Windows, as both Linux and macOS are
28
- * prohibitively expensive, requiring forking `fsck -N` or `diskutil
29
- * verifyVolume`.
30
- *
31
- * If there are non-critical errors while extracting metadata, those error
32
- * messages may be added to this field (say, from blkid or gio).
33
- *
34
- * @see {@link VolumeHealthStatuses} for values returned by Windows.
35
- */
36
- status?: VolumeHealthStatus | string;
37
-
38
- /**
39
- * Indicates if this volume is primarily for system use (e.g., swap, snap
40
- * loopbacks, EFI boot, or only system directories).
41
- *
42
- * Note: This is a best-effort classification and is not 100% accurate.
43
- *
44
- * @see {@link Options.systemPathPatterns} and {@link Options.systemFsTypes}
45
- */
46
- isSystemVolume?: boolean;
47
-
48
- /**
49
- * If there are non-critical errors while extracting metadata, those errors
50
- * may be added to this field.
51
- */
52
- error?: Error | string;
53
- }
3
+ import { MountPoint } from "./types/mount_point";
54
4
 
55
5
  export function isMountPoint(obj: unknown): obj is MountPoint {
56
- if (!isObject(obj)) return false;
57
- return "mountPoint" in obj && isNotBlank(obj.mountPoint);
6
+ return isObject(obj) && "mountPoint" in obj && isNotBlank(obj.mountPoint);
58
7
  }
package/src/object.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  import { isNotBlank, isString } from "./string.js";
4
4
 
5
5
  /**
6
- * Check if a value is an object
6
+ * @return true iff value is an object and not an array
7
7
  */
8
8
  export function isObject(value: unknown): value is object {
9
9
  // typeof null is 'object', so we need to check for that case YAY JAVASCRIPT
@@ -11,7 +11,8 @@ export function isObject(value: unknown): value is object {
11
11
  }
12
12
 
13
13
  /**
14
- * Map a value to another value, or undefined if the value is undefined
14
+ * @return undefined if `obj` is nullish, or the return value of `fn` applied
15
+ * against `obj` if `obj` is defined
15
16
  */
16
17
  export function map<T, U>(
17
18
  obj: T | undefined,
@@ -21,7 +22,7 @@ export function map<T, U>(
21
22
  }
22
23
 
23
24
  /**
24
- * Omit the specified fields from an object
25
+ * @return a shallow copy of `obj` that omits the specified `keys`
25
26
  */
26
27
  export function omit<T extends object, K extends keyof T>(
27
28
  obj: T,
@@ -40,6 +41,10 @@ export function omit<T extends object, K extends keyof T>(
40
41
  return result;
41
42
  }
42
43
 
44
+ /**
45
+ * @return a shallow copy of `obj` that only includes fields that are defined
46
+ * and not nullish or blank.
47
+ */
43
48
  export function compactValues<T extends object>(
44
49
  obj: T | undefined,
45
50
  ): Partial<T> {
package/src/options.ts CHANGED
@@ -3,59 +3,7 @@
3
3
  import { availableParallelism } from "node:os";
4
4
  import { compactValues, isObject } from "./object.js";
5
5
  import { isWindows } from "./platform.js";
6
-
7
- /**
8
- * Configuration options for filesystem operations.
9
- *
10
- * @see {@link optionsWithDefaults} for creating an options object with default values
11
- * @see {@link OptionsDefault} for the default values
12
- */
13
- export interface Options {
14
- /**
15
- * Timeout in milliseconds for filesystem operations.
16
- *
17
- * Disable timeouts by setting this to 0.
18
- *
19
- * @see {@link TimeoutMsDefault}.
20
- */
21
- timeoutMs: number;
22
-
23
- /**
24
- * Maximum number of concurrent filesystem operations.
25
- *
26
- * Defaults to {@link https://nodejs.org/api/os.html#osavailableparallelism | availableParallelism}.
27
- */
28
- maxConcurrency: number;
29
-
30
- /**
31
- * On Linux and macOS, mount point pathnames that matches any of these glob
32
- * patterns will have {@link MountPoint.isSystemVolume} set to true.
33
- *
34
- * @see {@link SystemPathPatternsDefault} for the default value
35
- */
36
- systemPathPatterns: string[];
37
-
38
- /**
39
- * On Linux and macOS, volumes whose filesystem matches any of these strings
40
- * will have {@link MountPoint.isSystemVolume} set to true.
41
- *
42
- * @see {@link SystemFsTypesDefault} for the default value
43
- */
44
- systemFsTypes: string[];
45
-
46
- /**
47
- * On Linux, use the first mount point table in this array that is readable.
48
- *
49
- * @see {@link LinuxMountTablePathsDefault} for the default values
50
- */
51
- linuxMountTablePaths: string[];
52
-
53
- /**
54
- * Should system volumes be included in result arrays? Defaults to true on
55
- * Windows and false elsewhere.
56
- */
57
- includeSystemVolumes: boolean;
58
- }
6
+ import type { Options } from "./types/options.js";
59
7
 
60
8
  /**
61
9
  * Default timeout in milliseconds for {@link Options.timeoutMs}.
@@ -82,6 +30,9 @@ export const SystemPathPatternsDefault = [
82
30
  "/run/user/*/gvfs",
83
31
  "/snap/**",
84
32
  "/sys/**",
33
+ "/tmp",
34
+ "/var/tmp",
35
+ // we aren't including /tmp/**, as some people temporarily mount volumes there, like /tmp/project.
85
36
  "**/#snapshot", // Synology and Kubernetes volume snapshots
86
37
 
87
38
  // windows for linux:
@@ -100,7 +51,7 @@ export const SystemPathPatternsDefault = [
100
51
  "/System/Volumes/Update",
101
52
  "/System/Volumes/VM",
102
53
  "/System/Volumes/xarts",
103
- ];
54
+ ] as const;
104
55
 
105
56
  /**
106
57
  * Filesystem types that indicate system volumes
package/src/path.ts CHANGED
@@ -24,11 +24,18 @@ export function normalizePath(
24
24
  export function normalizePosixPath(
25
25
  mountPoint: string | undefined,
26
26
  ): string | undefined {
27
- return isBlank(mountPoint)
28
- ? undefined
29
- : mountPoint === "/"
30
- ? mountPoint
31
- : mountPoint.replace(/\/+$/, "");
27
+ if (isBlank(mountPoint)) return undefined;
28
+ if (mountPoint === "/") return mountPoint;
29
+
30
+ // Fast path: check last char only if no trailing slash
31
+ if (mountPoint[mountPoint.length - 1] !== "/") return mountPoint;
32
+
33
+ // Slower path: trim trailing slashes
34
+ let end = mountPoint.length - 1;
35
+ while (end > 0 && mountPoint[end] === "/") {
36
+ end--;
37
+ }
38
+ return mountPoint.slice(0, end + 1);
32
39
  }
33
40
 
34
41
  /**
package/src/platform.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/platform.ts
2
2
 
3
- import { platform } from "node:os";
3
+ import { arch, platform } from "node:process";
4
4
 
5
- const p = platform();
5
+ export const isLinux = platform === "linux";
6
+ export const isWindows = platform === "win32";
7
+ export const isMacOS = platform === "darwin";
6
8
 
7
- export const isLinux = p === "linux";
8
- export const isWindows = p === "win32";
9
- export const isMacOS = p === "darwin";
9
+ export const isArm = isLinux && arch.startsWith("arm");