@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.
- package/CHANGELOG.md +13 -3
- package/README.md +3 -3
- package/dist/index.cjs +324 -215
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +329 -215
- package/dist/index.mjs.map +1 -1
- package/dist/types/debuglog.d.ts +2 -6
- package/dist/types/defer.d.ts +1 -2
- package/dist/types/dirname.d.ts +1 -0
- package/dist/types/hidden.d.ts +5 -42
- package/dist/types/index.d.ts +91 -2
- package/dist/types/linux/mount_points.d.ts +2 -2
- package/dist/types/linux/mtab.d.ts +2 -2
- package/dist/types/mount_point.d.ts +1 -46
- package/dist/types/options.d.ts +1 -47
- package/dist/types/platform.d.ts +1 -0
- package/dist/types/remote_info.d.ts +2 -34
- package/dist/types/stack_path.d.ts +2 -0
- package/dist/types/system_volume.d.ts +2 -2
- package/dist/types/types/hidden_metadata.d.ts +32 -0
- package/dist/types/types/mount_point.d.ts +46 -0
- package/dist/types/types/native_bindings.d.ts +3 -3
- package/dist/types/types/options.d.ts +47 -0
- package/dist/types/types/remote_info.d.ts +33 -0
- package/dist/types/types/volume_metadata.d.ts +46 -0
- package/dist/types/unc.d.ts +1 -1
- package/dist/types/units.d.ts +25 -3
- package/dist/types/volume_metadata.d.ts +4 -50
- package/dist/types/volume_mount_points.d.ts +3 -6
- package/jest.config.base.cjs +63 -0
- package/jest.config.cjs +3 -16
- package/package.json +12 -15
- package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/src/async.ts +9 -0
- package/src/debuglog.ts +6 -2
- package/src/defer.ts +1 -1
- package/src/dirname.ts +13 -0
- package/src/global.d.ts +1 -0
- package/src/hidden.ts +6 -42
- package/src/{exports.ts → index.ts} +75 -30
- package/src/linux/mount_points.ts +4 -2
- package/src/linux/mtab.ts +3 -3
- package/src/mount_point.ts +2 -53
- package/src/options.ts +4 -53
- package/src/path.ts +12 -5
- package/src/platform.ts +5 -5
- package/src/remote_info.ts +44 -49
- package/src/stack_path.ts +71 -0
- package/src/system_volume.ts +3 -6
- package/src/test-utils/assert.ts +1 -1
- package/src/test-utils/debuglog-child.ts +15 -0
- package/src/types/hidden_metadata.ts +38 -0
- package/src/types/mount_point.ts +53 -0
- package/src/types/native_bindings.ts +3 -3
- package/src/types/options.ts +54 -0
- package/src/types/remote_info.ts +35 -0
- package/src/types/volume_metadata.ts +52 -0
- package/src/unc.ts +1 -1
- package/src/units.ts +39 -7
- package/src/volume_metadata.ts +9 -66
- package/src/volume_mount_points.ts +3 -6
- package/tsup.config.ts +1 -0
- package/dist/types/exports.d.ts +0 -99
- package/dist/types/setup.d.ts +0 -2
- package/src/index.cts +0 -15
- package/src/index.mts +0 -17
- package/src/setup.ts +0 -69
package/src/mount_point.ts
CHANGED
|
@@ -1,58 +1,7 @@
|
|
|
1
1
|
import { isObject } from "./object";
|
|
2
2
|
import { isNotBlank } from "./string";
|
|
3
|
-
import {
|
|
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
|
-
|
|
57
|
-
return "mountPoint" in obj && isNotBlank(obj.mountPoint);
|
|
6
|
+
return isObject(obj) && "mountPoint" in obj && isNotBlank(obj.mountPoint);
|
|
58
7
|
}
|
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:
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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:
|
|
3
|
+
import { arch, platform } from "node:process";
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
export const isLinux = platform === "linux";
|
|
6
|
+
export const isWindows = platform === "win32";
|
|
7
|
+
export const isMacOS = platform === "darwin";
|
|
6
8
|
|
|
7
|
-
export const
|
|
8
|
-
export const isWindows = p === "win32";
|
|
9
|
-
export const isMacOS = p === "darwin";
|
|
9
|
+
export const isArm = isLinux && arch.startsWith("arm");
|
package/src/remote_info.ts
CHANGED
|
@@ -3,41 +3,8 @@
|
|
|
3
3
|
import { debug } from "./debuglog.js";
|
|
4
4
|
import { compactValues, isObject } from "./object.js";
|
|
5
5
|
import { isWindows } from "./platform.js";
|
|
6
|
-
import { isBlank, isNotBlank } from "./string.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Represents remote filesystem information.
|
|
10
|
-
*/
|
|
11
|
-
export interface RemoteInfo {
|
|
12
|
-
/**
|
|
13
|
-
* We can sometimes fetch a URI of the resource (like "smb://server/share" or
|
|
14
|
-
* "file:///media/user/usb")
|
|
15
|
-
*/
|
|
16
|
-
uri?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Protocol used to access the share.
|
|
19
|
-
*/
|
|
20
|
-
protocol?: string;
|
|
21
|
-
/**
|
|
22
|
-
* Does the protocol seem to be a remote filesystem?
|
|
23
|
-
*/
|
|
24
|
-
remote: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* If remote, may include the username used to access the share.
|
|
27
|
-
*
|
|
28
|
-
* This will be undefined on NFS and other remote filesystem types that do
|
|
29
|
-
* authentication out of band.
|
|
30
|
-
*/
|
|
31
|
-
remoteUser?: string;
|
|
32
|
-
/**
|
|
33
|
-
* If remote, the ip or hostname hosting the share (like "rusty" or "10.1.1.3")
|
|
34
|
-
*/
|
|
35
|
-
remoteHost?: string;
|
|
36
|
-
/**
|
|
37
|
-
* If remote, the name of the share (like "homes")
|
|
38
|
-
*/
|
|
39
|
-
remoteShare?: string;
|
|
40
|
-
}
|
|
6
|
+
import { isBlank, isNotBlank, toS } from "./string.js";
|
|
7
|
+
import { RemoteInfo } from "./types/remote_info.js";
|
|
41
8
|
|
|
42
9
|
export function isRemoteInfo(obj: unknown): obj is RemoteInfo {
|
|
43
10
|
if (!isObject(obj)) return false;
|
|
@@ -45,7 +12,7 @@ export function isRemoteInfo(obj: unknown): obj is RemoteInfo {
|
|
|
45
12
|
return isNotBlank(remoteHost) && isNotBlank(remoteShare);
|
|
46
13
|
}
|
|
47
14
|
|
|
48
|
-
const
|
|
15
|
+
const NETWORK_FS_TYPE_ARRAY = [
|
|
49
16
|
"9p",
|
|
50
17
|
"afp",
|
|
51
18
|
"afs",
|
|
@@ -53,9 +20,6 @@ const NETWORK_FS_TYPES = new Set([
|
|
|
53
20
|
"ceph",
|
|
54
21
|
"cifs",
|
|
55
22
|
"ftp",
|
|
56
|
-
"fuse.cephfs",
|
|
57
|
-
"fuse.glusterfs",
|
|
58
|
-
"fuse.sshfs",
|
|
59
23
|
"fuse",
|
|
60
24
|
"gfs2",
|
|
61
25
|
"glusterfs",
|
|
@@ -67,14 +31,40 @@ const NETWORK_FS_TYPES = new Set([
|
|
|
67
31
|
"smbfs",
|
|
68
32
|
"sshfs",
|
|
69
33
|
"webdav",
|
|
70
|
-
]
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
type NetworkFsType = (typeof NETWORK_FS_TYPE_ARRAY)[number];
|
|
37
|
+
|
|
38
|
+
const NETWORK_FS_TYPES = new Set<NetworkFsType>(NETWORK_FS_TYPE_ARRAY);
|
|
39
|
+
|
|
40
|
+
const FS_TYPE_ALIASES = new Map<string, NetworkFsType>([
|
|
41
|
+
["nfs1", "nfs"],
|
|
42
|
+
["nfs2", "nfs"],
|
|
43
|
+
["nfs3", "nfs"],
|
|
44
|
+
["nfs4", "nfs4"],
|
|
45
|
+
["fuse.sshfs", "sshfs"],
|
|
46
|
+
["sshfs.fuse", "sshfs"],
|
|
47
|
+
["davfs2", "webdav"],
|
|
48
|
+
["davfs", "webdav"],
|
|
49
|
+
["cifs.smb", "cifs"],
|
|
50
|
+
["smbfs", "cifs"],
|
|
51
|
+
["cephfs", "ceph"],
|
|
52
|
+
["fuse.ceph", "ceph"],
|
|
53
|
+
["fuse.cephfs", "ceph"],
|
|
54
|
+
["rbd", "ceph"],
|
|
55
|
+
["fuse.glusterfs", "glusterfs"],
|
|
56
|
+
] as const);
|
|
71
57
|
|
|
72
|
-
export function
|
|
73
|
-
|
|
58
|
+
export function normalizeFsType(fstype: string): string {
|
|
59
|
+
const norm = toS(fstype).toLowerCase().replace(/:$/, "");
|
|
60
|
+
return FS_TYPE_ALIASES.get(norm) ?? norm;
|
|
74
61
|
}
|
|
75
62
|
|
|
76
63
|
export function isRemoteFsType(fstype: string | undefined): boolean {
|
|
77
|
-
return
|
|
64
|
+
return (
|
|
65
|
+
isNotBlank(fstype) &&
|
|
66
|
+
NETWORK_FS_TYPES.has(normalizeFsType(fstype) as NetworkFsType)
|
|
67
|
+
);
|
|
78
68
|
}
|
|
79
69
|
|
|
80
70
|
export function parseURL(s: string): URL | undefined {
|
|
@@ -104,13 +94,18 @@ export function extractRemoteInfo(
|
|
|
104
94
|
}
|
|
105
95
|
|
|
106
96
|
const patterns = [
|
|
107
|
-
// CIFS/SMB pattern: //hostname/share or //user@host/share
|
|
108
97
|
{
|
|
98
|
+
// CIFS/SMB pattern: //hostname/share or //user@host/share
|
|
109
99
|
regex:
|
|
110
100
|
/^\/\/(?:(?<remoteUser>[^/@]+)@)?(?<remoteHost>[^/@]+)\/(?<remoteShare>.+)$/,
|
|
111
101
|
},
|
|
112
|
-
// NFS pattern: hostname:/share
|
|
113
102
|
{
|
|
103
|
+
// sshfs pattern: sshfs#USER@HOST:REMOTE_PATH
|
|
104
|
+
regex:
|
|
105
|
+
/^(?:(?<protocol>\w+)#)?(?<remoteUser>[^@]+)@(?<remoteHost>[^:]+):(?<remoteShare>.+)$/,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
// NFS pattern: hostname:/share
|
|
114
109
|
protocol: "nfs",
|
|
115
110
|
regex: /^(?<remoteHost>[^:]+):\/(?!\/)(?<remoteShare>.+)$/,
|
|
116
111
|
},
|
|
@@ -128,14 +123,14 @@ export function extractRemoteInfo(
|
|
|
128
123
|
}
|
|
129
124
|
}
|
|
130
125
|
|
|
131
|
-
// Let's try URL last, as nfs mounts are URI-ish
|
|
126
|
+
// Let's try URL last, as nfs and webdav mounts are URI-ish
|
|
132
127
|
try {
|
|
133
128
|
// try to parse fsSpec as a uri:
|
|
134
129
|
const parsed = new URL(fsSpec);
|
|
135
130
|
if (parsed != null) {
|
|
136
131
|
debug("[extractRemoteInfo] parsed URL: %o", parsed);
|
|
137
|
-
const
|
|
138
|
-
if (!isRemoteFsType(
|
|
132
|
+
const fstype = normalizeFsType(parsed.protocol);
|
|
133
|
+
if (!isRemoteFsType(fstype)) {
|
|
139
134
|
// don't set remoteUser, remoteHost, or remoteShare, it's not remote!
|
|
140
135
|
return {
|
|
141
136
|
uri: fsSpec,
|
|
@@ -144,7 +139,7 @@ export function extractRemoteInfo(
|
|
|
144
139
|
} else {
|
|
145
140
|
return compactValues({
|
|
146
141
|
uri: fsSpec,
|
|
147
|
-
protocol,
|
|
142
|
+
protocol: fstype,
|
|
148
143
|
remote: true,
|
|
149
144
|
remoteUser: parsed.username,
|
|
150
145
|
remoteHost: parsed.hostname,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { dirname } from "node:path";
|
|
2
|
+
import { isWindows } from "./platform";
|
|
3
|
+
import { isNotBlank, toS } from "./string";
|
|
4
|
+
|
|
5
|
+
export function getCallerDirname(): string {
|
|
6
|
+
const e = new Error();
|
|
7
|
+
if (e.stack == null) {
|
|
8
|
+
Error.captureStackTrace(e);
|
|
9
|
+
}
|
|
10
|
+
return dirname(extractCallerPath(e.stack as string));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// CURSE THE ESM MODULE SYSTEM 💩 THIS IS ALL HORRIBLE
|
|
14
|
+
|
|
15
|
+
// THANK GOODNESS for tsup shims: this should only be used when running tests.
|
|
16
|
+
|
|
17
|
+
// Comprehensive regex patterns for different stack frame formats. Note that we
|
|
18
|
+
// only expect tests to have the first standard form, but if something's worth
|
|
19
|
+
// doing, **it's worth overdoing**.
|
|
20
|
+
const patterns = isWindows
|
|
21
|
+
? [
|
|
22
|
+
// Standard: "at functionName (C:\path\file.js:1:1)"
|
|
23
|
+
/\bat\s.+?\((?<path>[A-Z]:\\.+):\d+:\d+\)$/,
|
|
24
|
+
// direct: "at C:\path\file.js:1:1"
|
|
25
|
+
/\bat\s(?<path>[A-Z]:\\.+):\d+:\d+$/,
|
|
26
|
+
// UNC: "at functionName (\\server\share\path\file.js:1:1)"
|
|
27
|
+
/\bat\s.+?\((?<path>\\\\.+):\d+:\d+\)$/,
|
|
28
|
+
// direct: "at \\server\share\path\file.js:1:1"
|
|
29
|
+
/\bat\s(?<path>\\\\.+):\d+:\d+$/,
|
|
30
|
+
]
|
|
31
|
+
: [
|
|
32
|
+
// Standard: "at functionName (/path/file.js:1:1)"
|
|
33
|
+
/\bat\s.+?\((?<path>\/.+?):\d+:\d+\)$/,
|
|
34
|
+
// Anonymous or direct: "at /path/file.js:1:1"
|
|
35
|
+
/\bat\s(.+[^/]\s)?(?<path>\/.+?):\d+:\d+$/,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const MaybeUrlRE = /^[a-z]{2,5}:\/\//i;
|
|
39
|
+
|
|
40
|
+
// only exposed for tests:
|
|
41
|
+
export function extractCallerPath(stack: string): string {
|
|
42
|
+
const frames = stack.split("\n").filter(Boolean);
|
|
43
|
+
|
|
44
|
+
// First find getCallerDirname() in the stack:
|
|
45
|
+
const callerFrame = frames.findIndex((frame) =>
|
|
46
|
+
frame.includes("getCallerDirname"),
|
|
47
|
+
);
|
|
48
|
+
if (callerFrame === -1) {
|
|
49
|
+
throw new Error("Invalid stack trace format: missing caller frame");
|
|
50
|
+
}
|
|
51
|
+
for (let i = callerFrame + 1; i < frames.length; i++) {
|
|
52
|
+
const frame = frames[i];
|
|
53
|
+
for (const pattern of patterns) {
|
|
54
|
+
const g = toS(frame).trim().match(pattern)?.groups;
|
|
55
|
+
if (g != null && isNotBlank(g["path"])) {
|
|
56
|
+
const path = g["path"];
|
|
57
|
+
// Windows requires us to check if it's a reasonable URL, as URL accepts
|
|
58
|
+
// "C:\\path\\file.txt" as valid (!!)
|
|
59
|
+
if (MaybeUrlRE.test(path)) {
|
|
60
|
+
try {
|
|
61
|
+
return new URL(path).pathname;
|
|
62
|
+
} catch {
|
|
63
|
+
// ignore
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return path;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
throw new Error("Invalid stack trace format: no parsable frames");
|
|
71
|
+
}
|
package/src/system_volume.ts
CHANGED
|
@@ -2,15 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { debug } from "./debuglog.js";
|
|
4
4
|
import { compileGlob } from "./glob.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
Options,
|
|
8
|
-
SystemFsTypesDefault,
|
|
9
|
-
SystemPathPatternsDefault,
|
|
10
|
-
} from "./options.js";
|
|
5
|
+
import { SystemFsTypesDefault, SystemPathPatternsDefault } from "./options.js";
|
|
11
6
|
import { normalizePath } from "./path.js";
|
|
12
7
|
import { isWindows } from "./platform.js";
|
|
13
8
|
import { isNotBlank } from "./string.js";
|
|
9
|
+
import type { MountPoint } from "./types/mount_point.js";
|
|
10
|
+
import type { Options } from "./types/options.js";
|
|
14
11
|
|
|
15
12
|
/**
|
|
16
13
|
* Configuration for system volume detection
|
package/src/test-utils/assert.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
2
|
// src/test-utils/assert.ts
|
|
3
3
|
import { isMacOS } from "../platform.js";
|
|
4
|
-
import type { VolumeMetadata } from "../volume_metadata.js";
|
|
4
|
+
import type { VolumeMetadata } from "../types/volume_metadata.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Asserts that the given metadata object has valid filesystem metadata
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env -S npx tsx
|
|
2
|
+
|
|
3
|
+
import { debugLogContext, isDebugEnabled } from "../debuglog.js";
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const result = {
|
|
7
|
+
isDebugEnabled: isDebugEnabled(),
|
|
8
|
+
debugLogContext: debugLogContext(),
|
|
9
|
+
};
|
|
10
|
+
console.log(JSON.stringify(result));
|
|
11
|
+
process.exit(0);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
console.error(err);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/types/hidden_metadata.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents the detailed state of a file or directory's hidden attribute
|
|
5
|
+
*/
|
|
6
|
+
export interface HiddenMetadata {
|
|
7
|
+
/**
|
|
8
|
+
* Whether the item is considered hidden by any method
|
|
9
|
+
*/
|
|
10
|
+
hidden: boolean;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Whether the item has a dot prefix (POSIX-style hidden). Windows doesn't
|
|
14
|
+
* care about dot prefixes.
|
|
15
|
+
*/
|
|
16
|
+
dotPrefix: boolean;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Whether the item has system hidden flags set, like via `chflags` on macOS
|
|
20
|
+
* or on Windows via `GetFileAttributesW`
|
|
21
|
+
*/
|
|
22
|
+
systemFlag: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Indicates which hiding methods are supported on the current platform
|
|
26
|
+
*/
|
|
27
|
+
supported: {
|
|
28
|
+
/**
|
|
29
|
+
* Whether dot prefix hiding is supported on the current operating system
|
|
30
|
+
*/
|
|
31
|
+
dotPrefix: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whether system flag hiding is supported
|
|
35
|
+
*/
|
|
36
|
+
systemFlag: boolean;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/types/mount_point.ts
|
|
2
|
+
|
|
3
|
+
import type { 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
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/types/native_bindings.ts
|
|
2
2
|
|
|
3
|
-
import { MountPoint } from "
|
|
4
|
-
import type { Options } from "
|
|
5
|
-
import type { VolumeMetadata } from "
|
|
3
|
+
import type { MountPoint } from "./mount_point.js";
|
|
4
|
+
import type { Options } from "./options.js";
|
|
5
|
+
import type { VolumeMetadata } from "./volume_metadata.js";
|
|
6
6
|
|
|
7
7
|
export interface NativeBindings {
|
|
8
8
|
/**
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/types/options.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for filesystem operations.
|
|
5
|
+
*
|
|
6
|
+
* @see {@link optionsWithDefaults} for creating an options object with default values
|
|
7
|
+
* @see {@link OptionsDefault} for the default values
|
|
8
|
+
*/
|
|
9
|
+
export interface Options {
|
|
10
|
+
/**
|
|
11
|
+
* Timeout in milliseconds for filesystem operations.
|
|
12
|
+
*
|
|
13
|
+
* Disable timeouts by setting this to 0.
|
|
14
|
+
*
|
|
15
|
+
* @see {@link TimeoutMsDefault}.
|
|
16
|
+
*/
|
|
17
|
+
timeoutMs: number;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Maximum number of concurrent filesystem operations.
|
|
21
|
+
*
|
|
22
|
+
* Defaults to {@link https://nodejs.org/api/os.html#osavailableparallelism | availableParallelism}.
|
|
23
|
+
*/
|
|
24
|
+
maxConcurrency: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* On Linux and macOS, mount point pathnames that matches any of these glob
|
|
28
|
+
* patterns will have {@link MountPoint.isSystemVolume} set to true.
|
|
29
|
+
*
|
|
30
|
+
* @see {@link SystemPathPatternsDefault} for the default value
|
|
31
|
+
*/
|
|
32
|
+
systemPathPatterns: string[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* On Linux and macOS, volumes whose filesystem matches any of these strings
|
|
36
|
+
* will have {@link MountPoint.isSystemVolume} set to true.
|
|
37
|
+
*
|
|
38
|
+
* @see {@link SystemFsTypesDefault} for the default value
|
|
39
|
+
*/
|
|
40
|
+
systemFsTypes: string[];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* On Linux, use the first mount point table in this array that is readable.
|
|
44
|
+
*
|
|
45
|
+
* @see {@link LinuxMountTablePathsDefault} for the default values
|
|
46
|
+
*/
|
|
47
|
+
linuxMountTablePaths: string[];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Should system volumes be included in result arrays? Defaults to true on
|
|
51
|
+
* Windows and false elsewhere.
|
|
52
|
+
*/
|
|
53
|
+
includeSystemVolumes: boolean;
|
|
54
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/types/remote_info.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents remote filesystem information.
|
|
5
|
+
*/
|
|
6
|
+
export interface RemoteInfo {
|
|
7
|
+
/**
|
|
8
|
+
* We can sometimes fetch a URI of the resource (like "smb://server/share" or
|
|
9
|
+
* "file:///media/user/usb")
|
|
10
|
+
*/
|
|
11
|
+
uri?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Protocol used to access the share.
|
|
14
|
+
*/
|
|
15
|
+
protocol?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Does the protocol seem to be a remote filesystem?
|
|
18
|
+
*/
|
|
19
|
+
remote: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* If remote, may include the username used to access the share.
|
|
22
|
+
*
|
|
23
|
+
* This will be undefined on NFS and other remote filesystem types that do
|
|
24
|
+
* authentication out of band.
|
|
25
|
+
*/
|
|
26
|
+
remoteUser?: string;
|
|
27
|
+
/**
|
|
28
|
+
* If remote, the ip or hostname hosting the share (like "rusty" or "10.1.1.3")
|
|
29
|
+
*/
|
|
30
|
+
remoteHost?: string;
|
|
31
|
+
/**
|
|
32
|
+
* If remote, the name of the share (like "homes")
|
|
33
|
+
*/
|
|
34
|
+
remoteShare?: string;
|
|
35
|
+
}
|