@photostructure/fs-metadata 0.1.6 → 0.2.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.
- package/CHANGELOG.md +30 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +46 -0
- package/README.md +4 -65
- package/SECURITY.md +9 -0
- package/dist/index.cjs +234 -225
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +232 -223
- package/dist/index.mjs.map +1 -0
- package/dist/types/array.d.ts +0 -5
- package/dist/types/debuglog.d.ts +0 -1
- package/dist/types/exports.d.ts +2 -1
- package/dist/types/mount_point.d.ts +1 -9
- package/dist/types/number.d.ts +0 -4
- package/dist/types/object.d.ts +0 -4
- package/dist/types/volume_metadata.d.ts +3 -3
- package/dist/types/volume_mount_points.d.ts +9 -0
- package/package.json +9 -9
- package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/src/array.ts +44 -0
- package/src/async.ts +145 -0
- package/src/debuglog.ts +30 -0
- package/src/defer.ts +30 -0
- package/src/error.ts +79 -0
- package/src/exports.ts +156 -0
- package/src/fs.ts +89 -0
- package/src/glob.ts +127 -0
- package/src/hidden.ts +249 -0
- package/src/index.cts +15 -0
- package/src/index.mts +17 -0
- package/src/linux/dev_disk.ts +77 -0
- package/src/linux/mount_points.ts +91 -0
- package/src/linux/mtab.ts +136 -0
- package/src/mount_point.ts +58 -0
- package/src/number.ts +21 -0
- package/src/object.ts +55 -0
- package/src/options.ts +179 -0
- package/src/path.ts +54 -0
- package/src/platform.ts +9 -0
- package/src/random.ts +40 -0
- package/src/remote_info.ts +161 -0
- package/src/setup.ts +69 -0
- package/src/string.ts +99 -0
- package/src/string_enum.ts +41 -0
- package/src/system_volume.ts +67 -0
- package/src/test-utils/assert.ts +69 -0
- package/src/test-utils/hidden-tests.ts +33 -0
- package/src/test-utils/jest-matchers.ts +29 -0
- package/src/test-utils/platform.ts +39 -0
- package/src/types/native_bindings.ts +64 -0
- package/src/types/node-gyp-build.d.ts +6 -0
- package/src/unc.ts +63 -0
- package/src/units.ts +31 -0
- package/src/uuid.ts +24 -0
- package/src/volume_health_status.ts +58 -0
- package/src/volume_metadata.ts +294 -0
- package/src/volume_mount_points.ts +109 -0
- package/tsup.config.ts +8 -0
- package/dist/types/cache.d.ts +0 -4
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// src/volume_metadata.ts
|
|
2
|
+
|
|
3
|
+
import { mapConcurrent, withTimeout } from "./async.js";
|
|
4
|
+
import { debug } from "./debuglog.js";
|
|
5
|
+
import { WrappedError } from "./error.js";
|
|
6
|
+
import { getLabelFromDevDisk, getUuidFromDevDisk } from "./linux/dev_disk.js";
|
|
7
|
+
import { getLinuxMtabMetadata } from "./linux/mount_points.js";
|
|
8
|
+
import {
|
|
9
|
+
type MtabVolumeMetadata,
|
|
10
|
+
mountEntryToPartialVolumeMetadata,
|
|
11
|
+
} from "./linux/mtab.js";
|
|
12
|
+
import type { MountPoint } from "./mount_point.js";
|
|
13
|
+
import { compactValues } from "./object.js";
|
|
14
|
+
import {
|
|
15
|
+
IncludeSystemVolumesDefault,
|
|
16
|
+
type Options,
|
|
17
|
+
optionsWithDefaults,
|
|
18
|
+
} from "./options.js";
|
|
19
|
+
import { normalizePath } from "./path.js";
|
|
20
|
+
import { isLinux, isWindows } from "./platform.js";
|
|
21
|
+
import {
|
|
22
|
+
type RemoteInfo,
|
|
23
|
+
extractRemoteInfo,
|
|
24
|
+
isRemoteFsType,
|
|
25
|
+
} from "./remote_info.js";
|
|
26
|
+
import { isBlank, isNotBlank } from "./string.js";
|
|
27
|
+
import { assignSystemVolume } from "./system_volume.js";
|
|
28
|
+
import type {
|
|
29
|
+
GetVolumeMetadataOptions,
|
|
30
|
+
NativeBindingsFn,
|
|
31
|
+
} from "./types/native_bindings.js";
|
|
32
|
+
import { parseUNCPath } from "./unc.js";
|
|
33
|
+
import { extractUUID } from "./uuid.js";
|
|
34
|
+
import {
|
|
35
|
+
VolumeHealthStatuses,
|
|
36
|
+
directoryStatus,
|
|
37
|
+
} from "./volume_health_status.js";
|
|
38
|
+
import { getVolumeMountPoints } from "./volume_mount_points.js";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Metadata associated to a volume.
|
|
42
|
+
*
|
|
43
|
+
* @see https://en.wikipedia.org/wiki/Volume_(computing)
|
|
44
|
+
*/
|
|
45
|
+
export interface VolumeMetadata extends RemoteInfo, MountPoint {
|
|
46
|
+
/**
|
|
47
|
+
* The name of the partition
|
|
48
|
+
*/
|
|
49
|
+
label?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Total size in bytes
|
|
52
|
+
*/
|
|
53
|
+
size?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Used size in bytes
|
|
56
|
+
*/
|
|
57
|
+
used?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Available size in bytes
|
|
60
|
+
*/
|
|
61
|
+
available?: number;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Path to the device or service that the mountpoint is from.
|
|
65
|
+
*
|
|
66
|
+
* Examples include `/dev/sda1`, `nfs-server:/export`,
|
|
67
|
+
* `//username@remoteHost/remoteShare`, or `//cifs-server/share`.
|
|
68
|
+
*
|
|
69
|
+
* May be undefined for remote volumes.
|
|
70
|
+
*/
|
|
71
|
+
mountFrom?: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The name of the mount. This may match the resolved mountPoint.
|
|
75
|
+
*/
|
|
76
|
+
mountName?: string;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* UUID for the volume, like "c9b08f6e-b392-11ef-bf19-4b13bb7db4b4".
|
|
80
|
+
*
|
|
81
|
+
* On windows, this _may_ be the 128-bit volume UUID, but if that is not
|
|
82
|
+
* available, like in the case of remote volumes, we fallback to the 32-bit
|
|
83
|
+
* volume serial number, rendered in lowercase hexadecimal.
|
|
84
|
+
*/
|
|
85
|
+
uuid?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function getVolumeMetadata(
|
|
89
|
+
o: GetVolumeMetadataOptions & Options,
|
|
90
|
+
nativeFn: NativeBindingsFn,
|
|
91
|
+
): Promise<VolumeMetadata> {
|
|
92
|
+
if (isBlank(o.mountPoint)) {
|
|
93
|
+
throw new TypeError(
|
|
94
|
+
"Invalid mountPoint: got " + JSON.stringify(o.mountPoint),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const p = _getVolumeMetadata(o, nativeFn);
|
|
99
|
+
// we rely on the native bindings on Windows to do proper timeouts
|
|
100
|
+
return isWindows
|
|
101
|
+
? p
|
|
102
|
+
: withTimeout({
|
|
103
|
+
desc: "getVolumeMetadata()",
|
|
104
|
+
timeoutMs: o.timeoutMs,
|
|
105
|
+
promise: p,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function _getVolumeMetadata(
|
|
110
|
+
o: GetVolumeMetadataOptions & Options,
|
|
111
|
+
nativeFn: NativeBindingsFn,
|
|
112
|
+
): Promise<VolumeMetadata> {
|
|
113
|
+
o = optionsWithDefaults(o);
|
|
114
|
+
const norm = normalizePath(o.mountPoint);
|
|
115
|
+
if (norm == null) {
|
|
116
|
+
throw new Error("Invalid mountPoint: " + JSON.stringify(o.mountPoint));
|
|
117
|
+
}
|
|
118
|
+
o.mountPoint = norm;
|
|
119
|
+
|
|
120
|
+
debug(
|
|
121
|
+
"[getVolumeMetadata] starting metadata collection for %s",
|
|
122
|
+
o.mountPoint,
|
|
123
|
+
);
|
|
124
|
+
debug("[getVolumeMetadata] options: %o", o);
|
|
125
|
+
|
|
126
|
+
const { status, error } = await directoryStatus(o.mountPoint, o.timeoutMs);
|
|
127
|
+
if (status !== VolumeHealthStatuses.healthy) {
|
|
128
|
+
debug("[getVolumeMetadata] directoryStatus error: %s", error);
|
|
129
|
+
throw error ?? new Error("Volume not healthy: " + status);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
debug("[getVolumeMetadata] readdir status: %s", status);
|
|
133
|
+
|
|
134
|
+
let remote: boolean = false;
|
|
135
|
+
// Get filesystem info from mtab first on Linux
|
|
136
|
+
let mtabInfo: undefined | MtabVolumeMetadata;
|
|
137
|
+
let device: undefined | string;
|
|
138
|
+
if (isLinux) {
|
|
139
|
+
debug("[getVolumeMetadata] collecting Linux mtab info");
|
|
140
|
+
try {
|
|
141
|
+
const m = await getLinuxMtabMetadata(o.mountPoint, o);
|
|
142
|
+
mtabInfo = mountEntryToPartialVolumeMetadata(m, o);
|
|
143
|
+
debug("[getVolumeMetadata] mtab info: %o", mtabInfo);
|
|
144
|
+
if (mtabInfo.remote) {
|
|
145
|
+
remote = true;
|
|
146
|
+
}
|
|
147
|
+
if (isNotBlank(m.fs_spec)) {
|
|
148
|
+
device = m.fs_spec;
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
debug("[getVolumeMetadata] failed to get mtab info: " + err);
|
|
152
|
+
// this may be a GIO mount. Ignore the error and continue.
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (isNotBlank(device)) {
|
|
157
|
+
o.device = device;
|
|
158
|
+
debug("[getVolumeMetadata] using device: %s", device);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
debug("[getVolumeMetadata] requesting native metadata");
|
|
162
|
+
const metadata = (await (
|
|
163
|
+
await nativeFn()
|
|
164
|
+
).getVolumeMetadata(o)) as VolumeMetadata;
|
|
165
|
+
debug("[getVolumeMetadata] native metadata: %o", metadata);
|
|
166
|
+
|
|
167
|
+
// Some OS implementations leave it up to us to extract remote info:
|
|
168
|
+
const remoteInfo =
|
|
169
|
+
mtabInfo ??
|
|
170
|
+
extractRemoteInfo(metadata.uri) ??
|
|
171
|
+
extractRemoteInfo(metadata.mountFrom) ??
|
|
172
|
+
(isWindows ? parseUNCPath(o.mountPoint) : undefined);
|
|
173
|
+
|
|
174
|
+
debug("[getVolumeMetadata] extracted remote info: %o", remoteInfo);
|
|
175
|
+
|
|
176
|
+
remote ||=
|
|
177
|
+
isRemoteFsType(metadata.fstype) ||
|
|
178
|
+
(remoteInfo?.remote ?? metadata.remote ?? false);
|
|
179
|
+
|
|
180
|
+
debug("[getVolumeMetadata] assembling: %o", {
|
|
181
|
+
status,
|
|
182
|
+
mtabInfo,
|
|
183
|
+
remoteInfo,
|
|
184
|
+
metadata,
|
|
185
|
+
mountPoint: o.mountPoint,
|
|
186
|
+
remote,
|
|
187
|
+
});
|
|
188
|
+
const result = compactValues({
|
|
189
|
+
status, // < let the implementation's status win by having this first
|
|
190
|
+
...compactValues(remoteInfo),
|
|
191
|
+
...compactValues(metadata),
|
|
192
|
+
...compactValues(mtabInfo),
|
|
193
|
+
mountPoint: o.mountPoint,
|
|
194
|
+
remote,
|
|
195
|
+
}) as VolumeMetadata;
|
|
196
|
+
|
|
197
|
+
// Backfill if blkid or gio failed us:
|
|
198
|
+
if (isLinux && isNotBlank(device)) {
|
|
199
|
+
// Sometimes blkid doesn't have the UUID in cache. Try to get it from
|
|
200
|
+
// /dev/disk/by-uuid:
|
|
201
|
+
result.uuid ??= (await getUuidFromDevDisk(device)) ?? "";
|
|
202
|
+
result.label ??= (await getLabelFromDevDisk(device)) ?? "";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
assignSystemVolume(result, o);
|
|
206
|
+
|
|
207
|
+
// Fix microsoft's UUID format:
|
|
208
|
+
result.uuid = extractUUID(result.uuid) ?? result.uuid ?? "";
|
|
209
|
+
|
|
210
|
+
debug("[getVolumeMetadata] final result for %s: %o", o.mountPoint, result);
|
|
211
|
+
return compactValues(result) as VolumeMetadata;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function getAllVolumeMetadata(
|
|
215
|
+
opts: Required<Options> & {
|
|
216
|
+
includeSystemVolumes?: boolean;
|
|
217
|
+
maxConcurrency?: number;
|
|
218
|
+
},
|
|
219
|
+
nativeFn: NativeBindingsFn,
|
|
220
|
+
): Promise<VolumeMetadata[]> {
|
|
221
|
+
const o = optionsWithDefaults(opts);
|
|
222
|
+
debug("[getAllVolumeMetadata] starting with options: %o", o);
|
|
223
|
+
|
|
224
|
+
const arr = await getVolumeMountPoints(o, nativeFn);
|
|
225
|
+
debug("[getAllVolumeMetadata] found %d mount points", arr.length);
|
|
226
|
+
|
|
227
|
+
const unhealthyMountPoints = arr
|
|
228
|
+
.filter(
|
|
229
|
+
(ea) => ea.status != null && ea.status !== VolumeHealthStatuses.healthy,
|
|
230
|
+
)
|
|
231
|
+
.map((ea) => ({
|
|
232
|
+
mountPoint: ea.mountPoint,
|
|
233
|
+
error: new WrappedError("volume not healthy: " + ea.status, {
|
|
234
|
+
name: "Skipped",
|
|
235
|
+
}),
|
|
236
|
+
}));
|
|
237
|
+
|
|
238
|
+
const includeSystemVolumes =
|
|
239
|
+
opts?.includeSystemVolumes ?? IncludeSystemVolumesDefault;
|
|
240
|
+
|
|
241
|
+
const systemMountPoints = includeSystemVolumes
|
|
242
|
+
? []
|
|
243
|
+
: arr
|
|
244
|
+
.filter((ea) => ea.isSystemVolume)
|
|
245
|
+
.map((ea) => ({
|
|
246
|
+
mountPoint: ea.mountPoint,
|
|
247
|
+
error: new WrappedError("system volume", { name: "Skipped" }),
|
|
248
|
+
}));
|
|
249
|
+
|
|
250
|
+
const healthy = arr.filter(
|
|
251
|
+
(ea) => ea.status == null || ea.status === VolumeHealthStatuses.healthy,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
debug("[getAllVolumeMetadata] ", {
|
|
255
|
+
allMountPoints: arr.map((ea) => ea.mountPoint),
|
|
256
|
+
healthyMountPoints: healthy.map((ea) => ea.mountPoint),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
debug(
|
|
260
|
+
"[getAllVolumeMetadata] processing %d healthy volumes with max concurrency %d",
|
|
261
|
+
healthy.length,
|
|
262
|
+
o.maxConcurrency,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const results = await (mapConcurrent({
|
|
266
|
+
maxConcurrency: o.maxConcurrency,
|
|
267
|
+
items:
|
|
268
|
+
(opts?.includeSystemVolumes ?? IncludeSystemVolumesDefault)
|
|
269
|
+
? healthy
|
|
270
|
+
: healthy.filter((ea) => !ea.isSystemVolume),
|
|
271
|
+
fn: async (mp) =>
|
|
272
|
+
getVolumeMetadata({ ...mp, ...o }, nativeFn).catch((error) => ({
|
|
273
|
+
mountPoint: mp.mountPoint,
|
|
274
|
+
error,
|
|
275
|
+
})),
|
|
276
|
+
}) as Promise<(VolumeMetadata | { mountPoint: string; error: Error })[]>);
|
|
277
|
+
|
|
278
|
+
debug("[getAllVolumeMetadata] completed processing all volumes");
|
|
279
|
+
return arr.map(
|
|
280
|
+
(result) =>
|
|
281
|
+
(results.find((ea) => ea.mountPoint === result.mountPoint) ??
|
|
282
|
+
unhealthyMountPoints.find(
|
|
283
|
+
(ea) => ea.mountPoint === result.mountPoint,
|
|
284
|
+
) ??
|
|
285
|
+
systemMountPoints.find((ea) => ea.mountPoint === result.mountPoint) ?? {
|
|
286
|
+
...result,
|
|
287
|
+
error: new WrappedError("Mount point metadata not retrieved", {
|
|
288
|
+
name: "NotApplicableError",
|
|
289
|
+
}),
|
|
290
|
+
}) as VolumeMetadata,
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export const _ = undefined;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/mount_point.ts
|
|
2
|
+
|
|
3
|
+
import { uniqBy } from "./array.js";
|
|
4
|
+
import { mapConcurrent, withTimeout } from "./async.js";
|
|
5
|
+
import { debug } from "./debuglog.js";
|
|
6
|
+
import { getLinuxMountPoints } from "./linux/mount_points.js";
|
|
7
|
+
import { MountPoint } from "./mount_point.js";
|
|
8
|
+
import { compactValues } from "./object.js";
|
|
9
|
+
import { Options } from "./options.js";
|
|
10
|
+
import { isMacOS, isWindows } from "./platform.js";
|
|
11
|
+
import {
|
|
12
|
+
isBlank,
|
|
13
|
+
isNotBlank,
|
|
14
|
+
sortObjectsByLocale,
|
|
15
|
+
toNotBlank,
|
|
16
|
+
} from "./string.js";
|
|
17
|
+
import { assignSystemVolume, SystemVolumeConfig } from "./system_volume.js";
|
|
18
|
+
import type { NativeBindingsFn } from "./types/native_bindings.js";
|
|
19
|
+
import { directoryStatus } from "./volume_health_status.js";
|
|
20
|
+
|
|
21
|
+
export type GetVolumeMountPointOptions = Partial<
|
|
22
|
+
Pick<
|
|
23
|
+
Options,
|
|
24
|
+
| "timeoutMs"
|
|
25
|
+
| "linuxMountTablePaths"
|
|
26
|
+
| "maxConcurrency"
|
|
27
|
+
| "includeSystemVolumes"
|
|
28
|
+
> &
|
|
29
|
+
SystemVolumeConfig
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Helper function for {@link getVolumeMountPoints}.
|
|
34
|
+
*/
|
|
35
|
+
export async function getVolumeMountPoints(
|
|
36
|
+
opts: Required<GetVolumeMountPointOptions>,
|
|
37
|
+
nativeFn: NativeBindingsFn,
|
|
38
|
+
): Promise<MountPoint[]> {
|
|
39
|
+
const p = _getVolumeMountPoints(opts, nativeFn);
|
|
40
|
+
// we rely on the native bindings on Windows to do proper timeouts
|
|
41
|
+
return isWindows
|
|
42
|
+
? p
|
|
43
|
+
: withTimeout({ desc: "getVolumeMountPoints", ...opts, promise: p });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function _getVolumeMountPoints(
|
|
47
|
+
o: Required<GetVolumeMountPointOptions>,
|
|
48
|
+
nativeFn: NativeBindingsFn,
|
|
49
|
+
): Promise<MountPoint[]> {
|
|
50
|
+
debug("[getVolumeMountPoints] gathering mount points with options: %o", o);
|
|
51
|
+
|
|
52
|
+
const raw = await (isWindows || isMacOS
|
|
53
|
+
? (async () => {
|
|
54
|
+
debug("[getVolumeMountPoints] using native implementation");
|
|
55
|
+
const points = await (await nativeFn()).getVolumeMountPoints(o);
|
|
56
|
+
debug(
|
|
57
|
+
"[getVolumeMountPoints] native returned %d mount points",
|
|
58
|
+
points.length,
|
|
59
|
+
);
|
|
60
|
+
return points;
|
|
61
|
+
})()
|
|
62
|
+
: getLinuxMountPoints(nativeFn, o));
|
|
63
|
+
|
|
64
|
+
debug("[getVolumeMountPoints] raw mount points: %o", raw);
|
|
65
|
+
|
|
66
|
+
const compacted = raw
|
|
67
|
+
.map((ea) => compactValues(ea) as MountPoint)
|
|
68
|
+
.filter((ea) => isNotBlank(ea.mountPoint));
|
|
69
|
+
|
|
70
|
+
for (const ea of compacted) {
|
|
71
|
+
assignSystemVolume(ea, o);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const filtered = o.includeSystemVolumes
|
|
75
|
+
? compacted
|
|
76
|
+
: compacted.filter((ea) => !ea.isSystemVolume);
|
|
77
|
+
|
|
78
|
+
const uniq = uniqBy(filtered, (ea) => toNotBlank(ea.mountPoint));
|
|
79
|
+
debug("[getVolumeMountPoints] found %d unique mount points", uniq.length);
|
|
80
|
+
|
|
81
|
+
const results = sortObjectsByLocale(uniq, (ea) => ea.mountPoint);
|
|
82
|
+
debug(
|
|
83
|
+
"[getVolumeMountPoints] getting status for %d mount points",
|
|
84
|
+
results.length,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
await mapConcurrent({
|
|
88
|
+
maxConcurrency: o.maxConcurrency,
|
|
89
|
+
items: results.filter(
|
|
90
|
+
// trust but verify
|
|
91
|
+
(ea) => isBlank(ea.status) || ea.status === "healthy",
|
|
92
|
+
),
|
|
93
|
+
fn: async (mp) => {
|
|
94
|
+
debug("[getVolumeMountPoints] checking status of %s", mp.mountPoint);
|
|
95
|
+
mp.status = (await directoryStatus(mp.mountPoint, o.timeoutMs)).status;
|
|
96
|
+
debug(
|
|
97
|
+
"[getVolumeMountPoints] status for %s: %s",
|
|
98
|
+
mp.mountPoint,
|
|
99
|
+
mp.status,
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
debug(
|
|
105
|
+
"[getVolumeMountPoints] completed with %d mount points",
|
|
106
|
+
results.length,
|
|
107
|
+
);
|
|
108
|
+
return results;
|
|
109
|
+
}
|
package/tsup.config.ts
ADDED
package/dist/types/cache.d.ts
DELETED