@zenfs/core 1.3.3 → 1.3.5
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/dist/backends/port/fs.d.ts +1 -2
- package/dist/backends/port/fs.js +0 -3
- package/dist/config.d.ts +5 -3
- package/dist/config.js +4 -6
- package/dist/credentials.d.ts +14 -2
- package/dist/credentials.js +11 -9
- package/dist/emulation/cache.d.ts +10 -6
- package/dist/emulation/cache.js +14 -9
- package/dist/emulation/promises.js +25 -18
- package/dist/emulation/shared.js +3 -0
- package/dist/emulation/sync.js +28 -20
- package/dist/emulation/watchers.js +9 -9
- package/dist/file.d.ts +1 -13
- package/dist/file.js +7 -23
- package/dist/stats.d.ts +11 -7
- package/dist/stats.js +14 -15
- package/package.json +4 -2
- package/readme.md +2 -0
- package/scripts/make-index.js +24 -7
- package/src/backends/port/fs.ts +1 -5
- package/src/config.ts +9 -9
- package/src/credentials.ts +24 -9
- package/src/emulation/cache.ts +15 -10
- package/src/emulation/promises.ts +24 -18
- package/src/emulation/shared.ts +3 -0
- package/src/emulation/sync.ts +27 -20
- package/src/emulation/watchers.ts +10 -9
- package/src/file.ts +7 -37
- package/src/stats.ts +21 -18
- package/tests/fs/links.test.ts +4 -0
- package/tests/fs/watch.test.ts +36 -4
|
@@ -4,7 +4,7 @@ import { type MountConfiguration } from '../../config.js';
|
|
|
4
4
|
import { File } from '../../file.js';
|
|
5
5
|
import { FileSystem, type FileSystemMetadata } from '../../filesystem.js';
|
|
6
6
|
import { Async } from '../../mixins/async.js';
|
|
7
|
-
import { Stats
|
|
7
|
+
import { Stats } from '../../stats.js';
|
|
8
8
|
import type { Backend, FilesystemOf } from '../backend.js';
|
|
9
9
|
import * as RPC from './rpc.js';
|
|
10
10
|
type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;
|
|
@@ -37,7 +37,6 @@ export declare class PortFile extends File {
|
|
|
37
37
|
chmodSync(): void;
|
|
38
38
|
utimes(atime: Date, mtime: Date): Promise<void>;
|
|
39
39
|
utimesSync(): void;
|
|
40
|
-
_setType(type: FileType): Promise<void>;
|
|
41
40
|
_setTypeSync(): void;
|
|
42
41
|
close(): Promise<void>;
|
|
43
42
|
closeSync(): void;
|
package/dist/backends/port/fs.js
CHANGED
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
|
|
2
|
-
import type { AbsolutePath } from './emulation/path.js';
|
|
3
2
|
/**
|
|
4
3
|
* Configuration for a specific mount point
|
|
5
4
|
*/
|
|
@@ -9,8 +8,11 @@ export type MountConfiguration<T extends Backend> = FilesystemOf<T> | BackendCon
|
|
|
9
8
|
* @see MountConfiguration
|
|
10
9
|
*/
|
|
11
10
|
export declare function resolveMountConfig<T extends Backend>(configuration: MountConfiguration<T>, _depth?: number): Promise<FilesystemOf<T>>;
|
|
11
|
+
/**
|
|
12
|
+
* An object mapping mount points to backends
|
|
13
|
+
*/
|
|
12
14
|
export interface ConfigMounts {
|
|
13
|
-
[K:
|
|
15
|
+
[K: string]: Backend;
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* Configuration
|
|
@@ -20,7 +22,7 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
|
|
|
20
22
|
* An object mapping mount points to mount configuration
|
|
21
23
|
*/
|
|
22
24
|
mounts: {
|
|
23
|
-
[K in keyof T
|
|
25
|
+
[K in keyof T]: MountConfiguration<T[K]>;
|
|
24
26
|
};
|
|
25
27
|
/**
|
|
26
28
|
* The uid to use
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
|
|
2
|
-
import {
|
|
2
|
+
import { useCredentials } from './credentials.js';
|
|
3
3
|
import { DeviceFS } from './devices.js';
|
|
4
4
|
import * as cache from './emulation/cache.js';
|
|
5
5
|
import { config } from './emulation/config.js';
|
|
@@ -87,7 +87,7 @@ async function mount(path, mount) {
|
|
|
87
87
|
export async function configure(configuration) {
|
|
88
88
|
const uid = 'uid' in configuration ? configuration.uid || 0 : 0;
|
|
89
89
|
const gid = 'gid' in configuration ? configuration.gid || 0 : 0;
|
|
90
|
-
|
|
90
|
+
useCredentials({ uid, gid });
|
|
91
91
|
cache.stats.isEnabled = configuration.cacheStats ?? false;
|
|
92
92
|
cache.paths.isEnabled = configuration.cachePaths ?? false;
|
|
93
93
|
config.checkAccess = !configuration.disableAccessChecks;
|
|
@@ -96,10 +96,8 @@ export async function configure(configuration) {
|
|
|
96
96
|
if (configuration.mounts) {
|
|
97
97
|
const toMount = [];
|
|
98
98
|
let unmountRoot = false;
|
|
99
|
-
for (const [
|
|
100
|
-
|
|
101
|
-
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
|
|
102
|
-
}
|
|
99
|
+
for (const [_point, mountConfig] of Object.entries(configuration.mounts)) {
|
|
100
|
+
const point = _point.startsWith('/') ? _point : '/' + _point;
|
|
103
101
|
if (isBackendConfig(mountConfig)) {
|
|
104
102
|
mountConfig.disableAsyncCache ?? (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);
|
|
105
103
|
}
|
package/dist/credentials.d.ts
CHANGED
|
@@ -10,9 +10,21 @@ export interface Credentials {
|
|
|
10
10
|
sgid: number;
|
|
11
11
|
euid: number;
|
|
12
12
|
egid: number;
|
|
13
|
+
/**
|
|
14
|
+
* List of group IDs.
|
|
15
|
+
*/
|
|
16
|
+
groups: number[];
|
|
13
17
|
}
|
|
14
18
|
export declare const credentials: Credentials;
|
|
19
|
+
export interface CredentialInit {
|
|
20
|
+
uid: number;
|
|
21
|
+
gid: number;
|
|
22
|
+
suid?: number;
|
|
23
|
+
sgid?: number;
|
|
24
|
+
euid?: number;
|
|
25
|
+
egid?: number;
|
|
26
|
+
}
|
|
15
27
|
/**
|
|
16
|
-
*
|
|
28
|
+
* Uses credentials from the provided uid and gid.
|
|
17
29
|
*/
|
|
18
|
-
export declare
|
|
30
|
+
export declare function useCredentials(source: CredentialInit): void;
|
package/dist/credentials.js
CHANGED
|
@@ -5,15 +5,17 @@ export const credentials = {
|
|
|
5
5
|
sgid: 0,
|
|
6
6
|
euid: 0,
|
|
7
7
|
egid: 0,
|
|
8
|
+
groups: [],
|
|
8
9
|
};
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Uses credentials from the provided uid and gid.
|
|
11
12
|
*/
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
13
|
+
export function useCredentials(source) {
|
|
14
|
+
Object.assign(credentials, {
|
|
15
|
+
suid: source.uid,
|
|
16
|
+
sgid: source.gid,
|
|
17
|
+
euid: source.uid,
|
|
18
|
+
egid: source.gid,
|
|
19
|
+
...source,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -7,26 +7,30 @@ export declare class Cache<T> {
|
|
|
7
7
|
isEnabled: boolean;
|
|
8
8
|
protected sync: Map<string, T>;
|
|
9
9
|
protected async: Map<string, Promise<T>>;
|
|
10
|
+
/**
|
|
11
|
+
* Whether the data exists in the cache
|
|
12
|
+
*/
|
|
13
|
+
has(path: string): boolean;
|
|
10
14
|
/**
|
|
11
15
|
* Gets data from the cache, if is exists and the cache is enabled.
|
|
12
16
|
*/
|
|
13
|
-
|
|
17
|
+
get(path: string): T | undefined;
|
|
14
18
|
/**
|
|
15
19
|
* Adds data if the cache is enabled
|
|
16
20
|
*/
|
|
17
|
-
|
|
21
|
+
set(path: string, value: T): void;
|
|
18
22
|
/**
|
|
19
|
-
*
|
|
23
|
+
* Whether the data exists in the cache
|
|
20
24
|
*/
|
|
21
|
-
|
|
25
|
+
hasAsync(path: string): boolean;
|
|
22
26
|
/**
|
|
23
27
|
* Gets data from the cache, if it exists and the cache is enabled.
|
|
24
28
|
*/
|
|
25
|
-
|
|
29
|
+
getAsync(path: string): Promise<T> | undefined;
|
|
26
30
|
/**
|
|
27
31
|
* Adds data if the cache is enabled
|
|
28
32
|
*/
|
|
29
|
-
|
|
33
|
+
setAsync(path: string, value: Promise<T>): void;
|
|
30
34
|
/**
|
|
31
35
|
* Clears the cache if it is enabled
|
|
32
36
|
*/
|
package/dist/emulation/cache.js
CHANGED
|
@@ -9,10 +9,16 @@ export class Cache {
|
|
|
9
9
|
this.sync = new Map();
|
|
10
10
|
this.async = new Map();
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Whether the data exists in the cache
|
|
14
|
+
*/
|
|
15
|
+
has(path) {
|
|
16
|
+
return this.isEnabled && this.sync.has(path);
|
|
17
|
+
}
|
|
12
18
|
/**
|
|
13
19
|
* Gets data from the cache, if is exists and the cache is enabled.
|
|
14
20
|
*/
|
|
15
|
-
|
|
21
|
+
get(path) {
|
|
16
22
|
if (!this.isEnabled)
|
|
17
23
|
return;
|
|
18
24
|
return this.sync.get(path);
|
|
@@ -20,24 +26,22 @@ export class Cache {
|
|
|
20
26
|
/**
|
|
21
27
|
* Adds data if the cache is enabled
|
|
22
28
|
*/
|
|
23
|
-
|
|
29
|
+
set(path, value) {
|
|
24
30
|
if (!this.isEnabled)
|
|
25
31
|
return;
|
|
26
32
|
this.sync.set(path, value);
|
|
27
33
|
this.async.set(path, Promise.resolve(value));
|
|
28
34
|
}
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
36
|
+
* Whether the data exists in the cache
|
|
31
37
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return;
|
|
35
|
-
this.sync.clear();
|
|
38
|
+
hasAsync(path) {
|
|
39
|
+
return this.isEnabled && this.async.has(path);
|
|
36
40
|
}
|
|
37
41
|
/**
|
|
38
42
|
* Gets data from the cache, if it exists and the cache is enabled.
|
|
39
43
|
*/
|
|
40
|
-
|
|
44
|
+
getAsync(path) {
|
|
41
45
|
if (!this.isEnabled)
|
|
42
46
|
return;
|
|
43
47
|
return this.async.get(path);
|
|
@@ -45,7 +49,7 @@ export class Cache {
|
|
|
45
49
|
/**
|
|
46
50
|
* Adds data if the cache is enabled
|
|
47
51
|
*/
|
|
48
|
-
|
|
52
|
+
setAsync(path, value) {
|
|
49
53
|
if (!this.isEnabled)
|
|
50
54
|
return;
|
|
51
55
|
this.async.set(path, value);
|
|
@@ -57,6 +61,7 @@ export class Cache {
|
|
|
57
61
|
clear() {
|
|
58
62
|
if (!this.isEnabled)
|
|
59
63
|
return;
|
|
64
|
+
this.sync.clear();
|
|
60
65
|
this.async.clear();
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -56,7 +56,7 @@ import * as cache from './cache.js';
|
|
|
56
56
|
import { config } from './config.js';
|
|
57
57
|
import * as constants from './constants.js';
|
|
58
58
|
import { Dir, Dirent } from './dir.js';
|
|
59
|
-
import { dirname, join, parse } from './path.js';
|
|
59
|
+
import { dirname, join, parse, resolve } from './path.js';
|
|
60
60
|
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
|
|
61
61
|
import { ReadStream, WriteStream } from './streams.js';
|
|
62
62
|
import { FSWatcher, emitChange } from './watchers.js';
|
|
@@ -360,6 +360,7 @@ export async function rename(oldPath, newPath) {
|
|
|
360
360
|
if (src.mountPoint == dst.mountPoint) {
|
|
361
361
|
await src.fs.rename(src.path, dst.path);
|
|
362
362
|
emitChange('rename', oldPath.toString());
|
|
363
|
+
emitChange('change', newPath.toString());
|
|
363
364
|
return;
|
|
364
365
|
}
|
|
365
366
|
await writeFile(newPath, await readFile(oldPath));
|
|
@@ -435,7 +436,7 @@ export async function unlink(path) {
|
|
|
435
436
|
path = normalizePath(path);
|
|
436
437
|
const { fs, path: resolved } = resolveMount(path);
|
|
437
438
|
try {
|
|
438
|
-
if (config.checkAccess && !(await (cache.stats.
|
|
439
|
+
if (config.checkAccess && !(await (cache.stats.getAsync(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
|
|
439
440
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
440
441
|
}
|
|
441
442
|
await fs.unlink(resolved);
|
|
@@ -583,7 +584,7 @@ export async function rmdir(path) {
|
|
|
583
584
|
path = await realpath(path);
|
|
584
585
|
const { fs, path: resolved } = resolveMount(path);
|
|
585
586
|
try {
|
|
586
|
-
const stats = await (cache.stats.
|
|
587
|
+
const stats = await (cache.stats.getAsync(path) || fs.stat(resolved));
|
|
587
588
|
if (!stats) {
|
|
588
589
|
throw ErrnoError.With('ENOENT', path, 'rmdir');
|
|
589
590
|
}
|
|
@@ -642,8 +643,8 @@ export async function readdir(path, options) {
|
|
|
642
643
|
throw fixError(e, { [resolved]: path });
|
|
643
644
|
};
|
|
644
645
|
const { fs, path: resolved } = resolveMount(path);
|
|
645
|
-
const _stats = cache.stats.
|
|
646
|
-
cache.stats.
|
|
646
|
+
const _stats = cache.stats.getAsync(path) || fs.stat(resolved).catch(handleError);
|
|
647
|
+
cache.stats.setAsync(path, _stats);
|
|
647
648
|
const stats = await _stats;
|
|
648
649
|
if (!stats) {
|
|
649
650
|
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
@@ -659,8 +660,8 @@ export async function readdir(path, options) {
|
|
|
659
660
|
const addEntry = async (entry) => {
|
|
660
661
|
let entryStats;
|
|
661
662
|
if (options?.recursive || options?.withFileTypes) {
|
|
662
|
-
const _entryStats = cache.stats.
|
|
663
|
-
cache.stats.
|
|
663
|
+
const _entryStats = cache.stats.getAsync(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
|
|
664
|
+
cache.stats.setAsync(join(path, entry), _entryStats);
|
|
664
665
|
entryStats = await _entryStats;
|
|
665
666
|
}
|
|
666
667
|
if (options?.withFileTypes) {
|
|
@@ -737,7 +738,7 @@ export async function symlink(target, path, type = 'file') {
|
|
|
737
738
|
}
|
|
738
739
|
const handle = __addDisposableResource(env_5, await _open(path, 'w+', 0o644, false), true);
|
|
739
740
|
await handle.writeFile(target.toString());
|
|
740
|
-
await handle.file.
|
|
741
|
+
await handle.file.chmod(constants.S_IFLNK);
|
|
741
742
|
}
|
|
742
743
|
catch (e_5) {
|
|
743
744
|
env_5.error = e_5;
|
|
@@ -880,17 +881,23 @@ export async function lutimes(path, atime, mtime) {
|
|
|
880
881
|
lutimes;
|
|
881
882
|
export async function realpath(path, options) {
|
|
882
883
|
path = normalizePath(path);
|
|
884
|
+
if (cache.paths.hasAsync(path))
|
|
885
|
+
return cache.paths.getAsync(path);
|
|
883
886
|
const { base, dir } = parse(path);
|
|
884
|
-
const
|
|
885
|
-
const
|
|
887
|
+
const realDir = dir == '/' ? '/' : await (cache.paths.getAsync(dir) || realpath(dir));
|
|
888
|
+
const lpath = join(realDir, base);
|
|
889
|
+
const { fs, path: resolvedPath } = resolveMount(lpath);
|
|
886
890
|
try {
|
|
887
|
-
const _stats = cache.stats.
|
|
888
|
-
cache.stats.
|
|
891
|
+
const _stats = cache.stats.getAsync(lpath) || fs.stat(resolvedPath);
|
|
892
|
+
cache.stats.setAsync(lpath, _stats);
|
|
889
893
|
if (!(await _stats).isSymbolicLink()) {
|
|
894
|
+
cache.paths.set(path, lpath);
|
|
890
895
|
return lpath;
|
|
891
896
|
}
|
|
892
|
-
const target =
|
|
893
|
-
|
|
897
|
+
const target = resolve(realDir, await readlink(lpath));
|
|
898
|
+
const real = cache.paths.getAsync(target) || realpath(target);
|
|
899
|
+
cache.paths.setAsync(path, real);
|
|
900
|
+
return await real;
|
|
894
901
|
}
|
|
895
902
|
catch (e) {
|
|
896
903
|
if (e.code == 'ENOENT') {
|
|
@@ -945,7 +952,7 @@ access;
|
|
|
945
952
|
*/
|
|
946
953
|
export async function rm(path, options) {
|
|
947
954
|
path = normalizePath(path);
|
|
948
|
-
const stats = await (cache.stats.
|
|
955
|
+
const stats = await (cache.stats.getAsync(path) ||
|
|
949
956
|
stat(path).catch((error) => {
|
|
950
957
|
if (error.code == 'ENOENT' && options?.force)
|
|
951
958
|
return undefined;
|
|
@@ -954,7 +961,7 @@ export async function rm(path, options) {
|
|
|
954
961
|
if (!stats) {
|
|
955
962
|
return;
|
|
956
963
|
}
|
|
957
|
-
cache.stats.
|
|
964
|
+
cache.stats.set(path, stats);
|
|
958
965
|
switch (stats.mode & constants.S_IFMT) {
|
|
959
966
|
case constants.S_IFDIR:
|
|
960
967
|
if (options?.recursive) {
|
|
@@ -966,10 +973,10 @@ export async function rm(path, options) {
|
|
|
966
973
|
break;
|
|
967
974
|
case constants.S_IFREG:
|
|
968
975
|
case constants.S_IFLNK:
|
|
969
|
-
await unlink(path);
|
|
970
|
-
break;
|
|
971
976
|
case constants.S_IFBLK:
|
|
972
977
|
case constants.S_IFCHR:
|
|
978
|
+
await unlink(path);
|
|
979
|
+
break;
|
|
973
980
|
case constants.S_IFIFO:
|
|
974
981
|
case constants.S_IFSOCK:
|
|
975
982
|
default:
|
package/dist/emulation/shared.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Errno, ErrnoError } from '../error.js';
|
|
|
4
4
|
import { normalizePath } from '../utils.js';
|
|
5
5
|
import { resolve } from './path.js';
|
|
6
6
|
import { size_max } from './constants.js';
|
|
7
|
+
import { paths as pathCache } from './cache.js';
|
|
7
8
|
// descriptors
|
|
8
9
|
export const fdMap = new Map();
|
|
9
10
|
let nextFd = 100;
|
|
@@ -37,6 +38,7 @@ export function mount(mountPoint, fs) {
|
|
|
37
38
|
throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use.');
|
|
38
39
|
}
|
|
39
40
|
mounts.set(mountPoint, fs);
|
|
41
|
+
pathCache.clear();
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
44
|
* Unmounts the file system at `mountPoint`.
|
|
@@ -50,6 +52,7 @@ export function umount(mountPoint) {
|
|
|
50
52
|
throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already unmounted.');
|
|
51
53
|
}
|
|
52
54
|
mounts.delete(mountPoint);
|
|
55
|
+
pathCache.clear();
|
|
53
56
|
}
|
|
54
57
|
/**
|
|
55
58
|
* Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
|
package/dist/emulation/sync.js
CHANGED
|
@@ -54,7 +54,7 @@ import * as cache from './cache.js';
|
|
|
54
54
|
import { config } from './config.js';
|
|
55
55
|
import * as constants from './constants.js';
|
|
56
56
|
import { Dir, Dirent } from './dir.js';
|
|
57
|
-
import { dirname, join, parse } from './path.js';
|
|
57
|
+
import { dirname, join, parse, resolve } from './path.js';
|
|
58
58
|
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
|
|
59
59
|
import { emitChange } from './watchers.js';
|
|
60
60
|
export function renameSync(oldPath, newPath) {
|
|
@@ -69,6 +69,7 @@ export function renameSync(oldPath, newPath) {
|
|
|
69
69
|
if (oldMount === newMount) {
|
|
70
70
|
oldMount.fs.renameSync(oldMount.path, newMount.path);
|
|
71
71
|
emitChange('rename', oldPath.toString());
|
|
72
|
+
emitChange('change', newPath.toString());
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
75
|
writeFileSync(newPath, readFileSync(oldPath));
|
|
@@ -147,7 +148,7 @@ export function unlinkSync(path) {
|
|
|
147
148
|
path = normalizePath(path);
|
|
148
149
|
const { fs, path: resolved } = resolveMount(path);
|
|
149
150
|
try {
|
|
150
|
-
if (config.checkAccess && !(cache.stats.
|
|
151
|
+
if (config.checkAccess && !(cache.stats.get(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
|
|
151
152
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
152
153
|
}
|
|
153
154
|
fs.unlinkSync(resolved);
|
|
@@ -393,7 +394,7 @@ export function rmdirSync(path) {
|
|
|
393
394
|
path = normalizePath(path);
|
|
394
395
|
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
395
396
|
try {
|
|
396
|
-
const stats = cache.stats.
|
|
397
|
+
const stats = cache.stats.get(path) || fs.statSync(resolved);
|
|
397
398
|
if (!stats.isDirectory()) {
|
|
398
399
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
399
400
|
}
|
|
@@ -446,8 +447,8 @@ export function readdirSync(path, options) {
|
|
|
446
447
|
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
447
448
|
let entries;
|
|
448
449
|
try {
|
|
449
|
-
const stats = cache.stats.
|
|
450
|
-
cache.stats.
|
|
450
|
+
const stats = cache.stats.get(path) || fs.statSync(resolved);
|
|
451
|
+
cache.stats.set(path, stats);
|
|
451
452
|
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
|
|
452
453
|
throw ErrnoError.With('EACCES', resolved, 'readdir');
|
|
453
454
|
}
|
|
@@ -462,8 +463,8 @@ export function readdirSync(path, options) {
|
|
|
462
463
|
// Iterate over entries and handle recursive case if needed
|
|
463
464
|
const values = [];
|
|
464
465
|
for (const entry of entries) {
|
|
465
|
-
const entryStat = cache.stats.
|
|
466
|
-
cache.stats.
|
|
466
|
+
const entryStat = cache.stats.get(join(path, entry)) || fs.statSync(join(resolved, entry));
|
|
467
|
+
cache.stats.set(join(path, entry), entryStat);
|
|
467
468
|
if (options?.withFileTypes) {
|
|
468
469
|
values.push(new Dirent(entry, entryStat));
|
|
469
470
|
}
|
|
@@ -489,7 +490,7 @@ export function readdirSync(path, options) {
|
|
|
489
490
|
}
|
|
490
491
|
}
|
|
491
492
|
if (!options?._isIndirect) {
|
|
492
|
-
cache.stats.
|
|
493
|
+
cache.stats.clear();
|
|
493
494
|
}
|
|
494
495
|
return values;
|
|
495
496
|
}
|
|
@@ -535,7 +536,7 @@ export function symlinkSync(target, path, type = 'file') {
|
|
|
535
536
|
}
|
|
536
537
|
writeFileSync(path, target.toString());
|
|
537
538
|
const file = _openSync(path, 'r+', 0o644, false);
|
|
538
|
-
file.
|
|
539
|
+
file.chmodSync(constants.S_IFLNK);
|
|
539
540
|
}
|
|
540
541
|
symlinkSync;
|
|
541
542
|
export function readlinkSync(path, options) {
|
|
@@ -592,16 +593,23 @@ export function lutimesSync(path, atime, mtime) {
|
|
|
592
593
|
lutimesSync;
|
|
593
594
|
export function realpathSync(path, options) {
|
|
594
595
|
path = normalizePath(path);
|
|
596
|
+
if (cache.paths.has(path))
|
|
597
|
+
return cache.paths.get(path);
|
|
595
598
|
const { base, dir } = parse(path);
|
|
596
|
-
const
|
|
597
|
-
const
|
|
599
|
+
const realDir = dir == '/' ? '/' : cache.paths.get(dir) || realpathSync(dir);
|
|
600
|
+
const lpath = join(realDir, base);
|
|
601
|
+
const { fs, path: resolvedPath } = resolveMount(lpath);
|
|
598
602
|
try {
|
|
599
|
-
const stats = fs.statSync(resolvedPath);
|
|
603
|
+
const stats = cache.stats.get(lpath) || fs.statSync(resolvedPath);
|
|
604
|
+
cache.stats.set(lpath, stats);
|
|
600
605
|
if (!stats.isSymbolicLink()) {
|
|
606
|
+
cache.paths.set(path, lpath);
|
|
601
607
|
return lpath;
|
|
602
608
|
}
|
|
603
|
-
const target =
|
|
604
|
-
|
|
609
|
+
const target = resolve(realDir, readlinkSync(lpath, options).toString());
|
|
610
|
+
const real = cache.paths.get(target) || realpathSync(target);
|
|
611
|
+
cache.paths.set(path, real);
|
|
612
|
+
return real;
|
|
605
613
|
}
|
|
606
614
|
catch (e) {
|
|
607
615
|
if (e.code == 'ENOENT') {
|
|
@@ -627,7 +635,7 @@ export function rmSync(path, options) {
|
|
|
627
635
|
path = normalizePath(path);
|
|
628
636
|
let stats;
|
|
629
637
|
try {
|
|
630
|
-
stats = cache.stats.
|
|
638
|
+
stats = cache.stats.get(path) || statSync(path);
|
|
631
639
|
}
|
|
632
640
|
catch (error) {
|
|
633
641
|
if (error.code != 'ENOENT' || !options?.force)
|
|
@@ -636,7 +644,7 @@ export function rmSync(path, options) {
|
|
|
636
644
|
if (!stats) {
|
|
637
645
|
return;
|
|
638
646
|
}
|
|
639
|
-
cache.stats.
|
|
647
|
+
cache.stats.set(path, stats);
|
|
640
648
|
switch (stats.mode & constants.S_IFMT) {
|
|
641
649
|
case constants.S_IFDIR:
|
|
642
650
|
if (options?.recursive) {
|
|
@@ -648,18 +656,18 @@ export function rmSync(path, options) {
|
|
|
648
656
|
break;
|
|
649
657
|
case constants.S_IFREG:
|
|
650
658
|
case constants.S_IFLNK:
|
|
651
|
-
unlinkSync(path);
|
|
652
|
-
break;
|
|
653
659
|
case constants.S_IFBLK:
|
|
654
660
|
case constants.S_IFCHR:
|
|
661
|
+
unlinkSync(path);
|
|
662
|
+
break;
|
|
655
663
|
case constants.S_IFIFO:
|
|
656
664
|
case constants.S_IFSOCK:
|
|
657
665
|
default:
|
|
658
|
-
cache.stats.
|
|
666
|
+
cache.stats.clear();
|
|
659
667
|
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
660
668
|
}
|
|
661
669
|
if (!options?._isIndirect) {
|
|
662
|
-
cache.stats.
|
|
670
|
+
cache.stats.clear();
|
|
663
671
|
}
|
|
664
672
|
}
|
|
665
673
|
rmSync;
|
|
@@ -2,7 +2,7 @@ import { EventEmitter } from 'eventemitter3';
|
|
|
2
2
|
import { ErrnoError } from '../error.js';
|
|
3
3
|
import { isStatsEqual } from '../stats.js';
|
|
4
4
|
import { normalizePath } from '../utils.js';
|
|
5
|
-
import {
|
|
5
|
+
import { basename, dirname } from './path.js';
|
|
6
6
|
import { statSync } from './sync.js';
|
|
7
7
|
/**
|
|
8
8
|
* Base class for file system watchers.
|
|
@@ -130,22 +130,22 @@ export function removeWatcher(path, watcher) {
|
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
export function emitChange(eventType, filename) {
|
|
133
|
-
|
|
133
|
+
filename = normalizePath(filename);
|
|
134
134
|
// Notify watchers on the specific file
|
|
135
|
-
if (watchers.has(
|
|
136
|
-
for (const watcher of watchers.get(
|
|
135
|
+
if (watchers.has(filename)) {
|
|
136
|
+
for (const watcher of watchers.get(filename)) {
|
|
137
137
|
watcher.emit('change', eventType, basename(filename));
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
// Notify watchers on parent directories if they are watching recursively
|
|
141
|
-
let parent =
|
|
142
|
-
while (parent !== normalizedFilename
|
|
141
|
+
let parent = filename, normalizedFilename;
|
|
142
|
+
while (parent !== normalizedFilename) {
|
|
143
|
+
normalizedFilename = parent;
|
|
144
|
+
parent = dirname(parent);
|
|
143
145
|
if (watchers.has(parent)) {
|
|
144
146
|
for (const watcher of watchers.get(parent)) {
|
|
145
|
-
watcher.emit('change', eventType,
|
|
147
|
+
watcher.emit('change', eventType, filename.slice(parent.length + (parent == '/' ? 0 : 1)));
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
|
-
normalizedFilename = parent;
|
|
149
|
-
parent = dirname(parent);
|
|
150
150
|
}
|
|
151
151
|
}
|
package/dist/file.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { FileReadResult } from 'node:fs/promises';
|
|
2
2
|
import type { FileSystem } from './filesystem.js';
|
|
3
3
|
import './polyfills.js';
|
|
4
|
-
import { Stats
|
|
4
|
+
import { Stats } from './stats.js';
|
|
5
5
|
/**
|
|
6
6
|
Typescript does not include a type declaration for resizable array buffers.
|
|
7
7
|
It has been standardized into ECMAScript though
|
|
@@ -123,16 +123,6 @@ export declare abstract class File<FS extends FileSystem = FileSystem> {
|
|
|
123
123
|
* Change the file timestamps of the file.
|
|
124
124
|
*/
|
|
125
125
|
abstract utimesSync(atime: Date, mtime: Date): void;
|
|
126
|
-
/**
|
|
127
|
-
* Set the file type
|
|
128
|
-
* @internal
|
|
129
|
-
*/
|
|
130
|
-
abstract _setType(type: FileType): Promise<void>;
|
|
131
|
-
/**
|
|
132
|
-
* Set the file type
|
|
133
|
-
* @internal
|
|
134
|
-
*/
|
|
135
|
-
abstract _setTypeSync(type: FileType): void;
|
|
136
126
|
}
|
|
137
127
|
/**
|
|
138
128
|
* An implementation of `File` that operates completely in-memory.
|
|
@@ -243,8 +233,6 @@ export declare class PreloadFile<FS extends FileSystem> extends File<FS> {
|
|
|
243
233
|
chownSync(uid: number, gid: number): void;
|
|
244
234
|
utimes(atime: Date, mtime: Date): Promise<void>;
|
|
245
235
|
utimesSync(atime: Date, mtime: Date): void;
|
|
246
|
-
_setType(type: FileType): Promise<void>;
|
|
247
|
-
_setTypeSync(type: FileType): void;
|
|
248
236
|
}
|
|
249
237
|
/**
|
|
250
238
|
* For the file systems which do not sync to anything.
|