@zenfs/core 1.9.3 → 1.9.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/fetch.d.ts +3 -0
- package/dist/backends/fetch.js +3 -0
- package/dist/backends/memory.d.ts +1 -1
- package/dist/backends/memory.js +1 -3
- package/dist/backends/overlay.d.ts +1 -0
- package/dist/backends/overlay.js +1 -0
- package/dist/backends/port/fs.d.ts +8 -2
- package/dist/backends/port/fs.js +7 -1
- package/dist/backends/port/rpc.d.ts +6 -2
- package/dist/backends/port/rpc.js +15 -4
- package/dist/backends/store/fs.d.ts +1 -4
- package/dist/backends/store/fs.js +1 -4
- package/dist/config.d.ts +0 -15
- package/dist/config.js +2 -5
- package/dist/internal/file_index.d.ts +1 -1
- package/dist/internal/file_index.js +1 -1
- package/dist/internal/index_fs.d.ts +1 -1
- package/dist/internal/index_fs.js +1 -1
- package/dist/utils.d.ts +1 -2
- package/dist/utils.js +10 -2
- package/dist/vfs/promises.d.ts +2 -2
- package/dist/vfs/promises.js +75 -84
- package/dist/vfs/shared.d.ts +10 -0
- package/dist/vfs/shared.js +1 -5
- package/dist/vfs/sync.d.ts +2 -2
- package/dist/vfs/sync.js +53 -53
- package/dist/vfs/types.d.ts +1 -13
- package/package.json +1 -1
- package/scripts/test.js +7 -4
- package/tests/backend/port.test.ts +11 -9
- package/tests/common/path.test.ts +12 -0
- package/tests/fs/links.test.ts +25 -0
- package/dist/vfs/cache.d.ts +0 -46
- package/dist/vfs/cache.js +0 -75
package/dist/vfs/promises.js
CHANGED
|
@@ -57,7 +57,6 @@ import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWrit
|
|
|
57
57
|
import '../polyfills.js';
|
|
58
58
|
import { BigIntStats } from '../stats.js';
|
|
59
59
|
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
60
|
-
import * as cache from './cache.js';
|
|
61
60
|
import { config } from './config.js';
|
|
62
61
|
import * as constants from './constants.js';
|
|
63
62
|
import { Dir, Dirent } from './dir.js';
|
|
@@ -90,9 +89,8 @@ export class FileHandle {
|
|
|
90
89
|
*/
|
|
91
90
|
async chmod(mode) {
|
|
92
91
|
const numMode = normalizeMode(mode, -1);
|
|
93
|
-
if (numMode < 0)
|
|
92
|
+
if (numMode < 0)
|
|
94
93
|
throw new ErrnoError(Errno.EINVAL, 'Invalid mode.');
|
|
95
|
-
}
|
|
96
94
|
await this.file.chmod(numMode);
|
|
97
95
|
this._emitChange();
|
|
98
96
|
}
|
|
@@ -459,7 +457,7 @@ export async function unlink(path) {
|
|
|
459
457
|
path = normalizePath(path);
|
|
460
458
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
461
459
|
try {
|
|
462
|
-
if (config.checkAccess && !(await
|
|
460
|
+
if (config.checkAccess && !(await fs.stat(resolved)).hasAccess(constants.W_OK, this)) {
|
|
463
461
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
464
462
|
}
|
|
465
463
|
await fs.unlink(resolved);
|
|
@@ -484,37 +482,35 @@ async function applySetId(file, uid, gid) {
|
|
|
484
482
|
* Opens a file. This helper handles the complexity of file flags.
|
|
485
483
|
* @internal
|
|
486
484
|
*/
|
|
487
|
-
async function _open(path, opt) {
|
|
485
|
+
async function _open($, path, opt) {
|
|
488
486
|
var _a;
|
|
489
487
|
path = normalizePath(path);
|
|
490
488
|
const mode = normalizeMode(opt.mode, 0o644), flag = parseFlag(opt.flag);
|
|
491
|
-
|
|
492
|
-
const { fs, path: resolved } = resolveMount(path, this);
|
|
493
|
-
const stats = await fs.stat(resolved).catch(() => null);
|
|
489
|
+
const { fullPath, fs, path: resolved, stats } = await _resolve($, path.toString(), opt.preserveSymlinks);
|
|
494
490
|
if (!stats) {
|
|
495
491
|
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
|
|
496
|
-
throw ErrnoError.With('ENOENT',
|
|
492
|
+
throw ErrnoError.With('ENOENT', fullPath, '_open');
|
|
497
493
|
}
|
|
498
494
|
// Create the file
|
|
499
495
|
const parentStats = await fs.stat(dirname(resolved));
|
|
500
|
-
if (config.checkAccess && !parentStats.hasAccess(constants.W_OK,
|
|
501
|
-
throw ErrnoError.With('EACCES', dirname(
|
|
496
|
+
if (config.checkAccess && !parentStats.hasAccess(constants.W_OK, $)) {
|
|
497
|
+
throw ErrnoError.With('EACCES', dirname(fullPath), '_open');
|
|
502
498
|
}
|
|
503
499
|
if (!parentStats.isDirectory()) {
|
|
504
|
-
throw ErrnoError.With('ENOTDIR', dirname(
|
|
500
|
+
throw ErrnoError.With('ENOTDIR', dirname(fullPath), '_open');
|
|
505
501
|
}
|
|
506
|
-
const { euid: uid, egid: gid } = (_a =
|
|
502
|
+
const { euid: uid, egid: gid } = (_a = $ === null || $ === void 0 ? void 0 : $.credentials) !== null && _a !== void 0 ? _a : credentials;
|
|
507
503
|
const file = await fs.createFile(resolved, flag, mode, { uid, gid });
|
|
508
504
|
await applySetId(file, uid, gid);
|
|
509
|
-
return new FileHandle(file,
|
|
505
|
+
return new FileHandle(file, $);
|
|
510
506
|
}
|
|
511
|
-
if (config.checkAccess && !stats.hasAccess(flagToMode(flag),
|
|
512
|
-
throw ErrnoError.With('EACCES',
|
|
507
|
+
if (config.checkAccess && !stats.hasAccess(flagToMode(flag), $)) {
|
|
508
|
+
throw ErrnoError.With('EACCES', fullPath, '_open');
|
|
513
509
|
}
|
|
514
510
|
if (isExclusive(flag)) {
|
|
515
|
-
throw ErrnoError.With('EEXIST',
|
|
511
|
+
throw ErrnoError.With('EEXIST', fullPath, '_open');
|
|
516
512
|
}
|
|
517
|
-
const handle = new FileHandle(await fs.openFile(resolved, flag),
|
|
513
|
+
const handle = new FileHandle(await fs.openFile(resolved, flag), $);
|
|
518
514
|
/*
|
|
519
515
|
In a previous implementation, we deleted the file and
|
|
520
516
|
re-created it. However, this created a race condition if another
|
|
@@ -533,7 +529,7 @@ async function _open(path, opt) {
|
|
|
533
529
|
* @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
|
|
534
530
|
*/
|
|
535
531
|
export async function open(path, flag = 'r', mode = 0o644) {
|
|
536
|
-
return await _open
|
|
532
|
+
return await _open(this, path, { flag, mode });
|
|
537
533
|
}
|
|
538
534
|
open;
|
|
539
535
|
export async function readFile(path, _options) {
|
|
@@ -625,7 +621,7 @@ export async function rmdir(path) {
|
|
|
625
621
|
path = await realpath.call(this, path);
|
|
626
622
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
627
623
|
try {
|
|
628
|
-
const stats = await
|
|
624
|
+
const stats = await fs.stat(resolved);
|
|
629
625
|
if (!stats) {
|
|
630
626
|
throw ErrnoError.With('ENOENT', path, 'rmdir');
|
|
631
627
|
}
|
|
@@ -688,9 +684,7 @@ export async function readdir(path, options) {
|
|
|
688
684
|
throw fixError(e, { [resolved]: path });
|
|
689
685
|
};
|
|
690
686
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
691
|
-
const
|
|
692
|
-
cache.stats.setAsync(path, _stats);
|
|
693
|
-
const stats = await _stats;
|
|
687
|
+
const stats = await fs.stat(resolved).catch(handleError);
|
|
694
688
|
if (!stats) {
|
|
695
689
|
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
696
690
|
}
|
|
@@ -705,9 +699,7 @@ export async function readdir(path, options) {
|
|
|
705
699
|
const addEntry = async (entry) => {
|
|
706
700
|
let entryStats;
|
|
707
701
|
if ((options === null || options === void 0 ? void 0 : options.recursive) || (options === null || options === void 0 ? void 0 : options.withFileTypes)) {
|
|
708
|
-
|
|
709
|
-
cache.stats.setAsync(join(path, entry), _entryStats);
|
|
710
|
-
entryStats = await _entryStats;
|
|
702
|
+
entryStats = await fs.stat(join(resolved, entry)).catch(handleError);
|
|
711
703
|
}
|
|
712
704
|
if (options === null || options === void 0 ? void 0 : options.withFileTypes) {
|
|
713
705
|
values.push(new Dirent(entry, entryStats));
|
|
@@ -720,7 +712,7 @@ export async function readdir(path, options) {
|
|
|
720
712
|
}
|
|
721
713
|
if (!(options === null || options === void 0 ? void 0 : options.recursive) || !(entryStats === null || entryStats === void 0 ? void 0 : entryStats.isDirectory()))
|
|
722
714
|
return;
|
|
723
|
-
for (const subEntry of await readdir.call(this, join(path, entry),
|
|
715
|
+
for (const subEntry of await readdir.call(this, join(path, entry), options)) {
|
|
724
716
|
if (subEntry instanceof Dirent) {
|
|
725
717
|
subEntry.path = join(entry, subEntry.path);
|
|
726
718
|
values.push(subEntry);
|
|
@@ -735,9 +727,6 @@ export async function readdir(path, options) {
|
|
|
735
727
|
}
|
|
736
728
|
};
|
|
737
729
|
await Promise.all(entries.map(addEntry));
|
|
738
|
-
if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
|
|
739
|
-
cache.stats.clear();
|
|
740
|
-
}
|
|
741
730
|
return values;
|
|
742
731
|
}
|
|
743
732
|
readdir;
|
|
@@ -778,11 +767,11 @@ export async function symlink(target, path, type = 'file') {
|
|
|
778
767
|
if (!['file', 'dir', 'junction'].includes(type)) {
|
|
779
768
|
throw new ErrnoError(Errno.EINVAL, 'Invalid symlink type: ' + type);
|
|
780
769
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
const handle = __addDisposableResource(env_5, await _open
|
|
785
|
-
await handle.writeFile(target
|
|
770
|
+
path = normalizePath(path);
|
|
771
|
+
if (await exists.call(this, path))
|
|
772
|
+
throw ErrnoError.With('EEXIST', path, 'symlink');
|
|
773
|
+
const handle = __addDisposableResource(env_5, await _open(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
|
|
774
|
+
await handle.writeFile(normalizePath(target, true));
|
|
786
775
|
await handle.file.chmod(constants.S_IFLNK);
|
|
787
776
|
}
|
|
788
777
|
catch (e_5) {
|
|
@@ -799,7 +788,7 @@ symlink;
|
|
|
799
788
|
export async function readlink(path, options) {
|
|
800
789
|
const env_6 = { stack: [], error: void 0, hasError: false };
|
|
801
790
|
try {
|
|
802
|
-
const handle = __addDisposableResource(env_6, await _open
|
|
791
|
+
const handle = __addDisposableResource(env_6, await _open(this, normalizePath(path), { flag: 'r', mode: 0o644, preserveSymlinks: true }), true);
|
|
803
792
|
const value = await handle.readFile();
|
|
804
793
|
const encoding = typeof options == 'object' ? options === null || options === void 0 ? void 0 : options.encoding : options;
|
|
805
794
|
// always defaults to utf-8 to avoid wrangler (cloudflare) worker "unknown encoding" exception
|
|
@@ -836,7 +825,7 @@ chown;
|
|
|
836
825
|
export async function lchown(path, uid, gid) {
|
|
837
826
|
const env_8 = { stack: [], error: void 0, hasError: false };
|
|
838
827
|
try {
|
|
839
|
-
const handle = __addDisposableResource(env_8, await _open
|
|
828
|
+
const handle = __addDisposableResource(env_8, await _open(this, path, {
|
|
840
829
|
flag: 'r+',
|
|
841
830
|
mode: 0o644,
|
|
842
831
|
preserveSymlinks: true,
|
|
@@ -875,7 +864,7 @@ chmod;
|
|
|
875
864
|
export async function lchmod(path, mode) {
|
|
876
865
|
const env_10 = { stack: [], error: void 0, hasError: false };
|
|
877
866
|
try {
|
|
878
|
-
const handle = __addDisposableResource(env_10, await _open
|
|
867
|
+
const handle = __addDisposableResource(env_10, await _open(this, path, {
|
|
879
868
|
flag: 'r+',
|
|
880
869
|
mode: 0o644,
|
|
881
870
|
preserveSymlinks: true,
|
|
@@ -920,7 +909,7 @@ utimes;
|
|
|
920
909
|
export async function lutimes(path, atime, mtime) {
|
|
921
910
|
const env_12 = { stack: [], error: void 0, hasError: false };
|
|
922
911
|
try {
|
|
923
|
-
const handle = __addDisposableResource(env_12, await _open
|
|
912
|
+
const handle = __addDisposableResource(env_12, await _open(this, path, {
|
|
924
913
|
flag: 'r+',
|
|
925
914
|
mode: 0o644,
|
|
926
915
|
preserveSymlinks: true,
|
|
@@ -939,52 +928,63 @@ export async function lutimes(path, atime, mtime) {
|
|
|
939
928
|
}
|
|
940
929
|
}
|
|
941
930
|
lutimes;
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
931
|
+
/**
|
|
932
|
+
* Resolves the mount and real path for a path.
|
|
933
|
+
* Additionally, any stats fetched will be returned for de-duplication
|
|
934
|
+
* @internal @hidden
|
|
935
|
+
*/
|
|
936
|
+
async function _resolve($, path, preserveSymlinks) {
|
|
937
|
+
if (preserveSymlinks) {
|
|
938
|
+
const resolved = resolveMount(path, $);
|
|
939
|
+
const stats = await resolved.fs.stat(resolved.path).catch(() => undefined);
|
|
940
|
+
return { ...resolved, fullPath: path, stats };
|
|
941
|
+
}
|
|
947
942
|
/* Try to resolve it directly. If this works,
|
|
948
943
|
that means we don't need to perform any resolution for parent directories. */
|
|
949
944
|
try {
|
|
950
|
-
const
|
|
945
|
+
const resolved = resolveMount(path, $);
|
|
951
946
|
// Stat it to make sure it exists
|
|
952
|
-
const stats = await fs.stat(
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const target = resolve(dirname(resolvedPath), (await readlink.call(this, resolvedPath, options)).toString());
|
|
956
|
-
real = cache.paths.get(((this === null || this === void 0 ? void 0 : this.root) || '') + target) || (await realpath.call(this, target));
|
|
957
|
-
cache.paths.set(ctx_path, real);
|
|
947
|
+
const stats = await resolved.fs.stat(resolved.path);
|
|
948
|
+
if (!stats.isSymbolicLink()) {
|
|
949
|
+
return { ...resolved, fullPath: path, stats };
|
|
958
950
|
}
|
|
959
|
-
|
|
960
|
-
return
|
|
951
|
+
const target = resolve(dirname(path), (await readlink.call($, path)).toString());
|
|
952
|
+
return await _resolve($, target);
|
|
961
953
|
}
|
|
962
954
|
catch {
|
|
963
955
|
// Go the long way
|
|
964
956
|
}
|
|
965
957
|
const { base, dir } = parse(path);
|
|
966
|
-
const realDir = dir == '/' ? '/' : await
|
|
967
|
-
const
|
|
968
|
-
const
|
|
958
|
+
const realDir = dir == '/' ? '/' : await realpath.call($, dir);
|
|
959
|
+
const maybePath = join(realDir, base);
|
|
960
|
+
const resolved = resolveMount(maybePath, $);
|
|
969
961
|
try {
|
|
970
|
-
const
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
cache.paths.set(path, lpath);
|
|
974
|
-
return lpath;
|
|
962
|
+
const stats = await resolved.fs.stat(resolved.path);
|
|
963
|
+
if (!stats.isSymbolicLink()) {
|
|
964
|
+
return { ...resolved, fullPath: maybePath, stats };
|
|
975
965
|
}
|
|
976
|
-
const target = resolve(realDir, (await readlink.call(
|
|
977
|
-
|
|
978
|
-
cache.paths.setAsync(ctx_path, real);
|
|
979
|
-
return await real;
|
|
966
|
+
const target = resolve(realDir, (await readlink.call($, maybePath)).toString());
|
|
967
|
+
return await _resolve($, target);
|
|
980
968
|
}
|
|
981
969
|
catch (e) {
|
|
982
970
|
if (e.code == 'ENOENT') {
|
|
983
|
-
return path;
|
|
971
|
+
return { ...resolved, fullPath: path };
|
|
984
972
|
}
|
|
985
|
-
throw fixError(e, { [
|
|
973
|
+
throw fixError(e, { [resolved.path]: maybePath });
|
|
986
974
|
}
|
|
987
975
|
}
|
|
976
|
+
export async function realpath(path, options) {
|
|
977
|
+
var _a;
|
|
978
|
+
const encoding = typeof options == 'string' ? options : ((_a = options === null || options === void 0 ? void 0 : options.encoding) !== null && _a !== void 0 ? _a : 'utf8');
|
|
979
|
+
path = normalizePath(path);
|
|
980
|
+
const { fullPath } = await _resolve(this, path);
|
|
981
|
+
if (encoding == 'utf8' || encoding == 'utf-8')
|
|
982
|
+
return fullPath;
|
|
983
|
+
const buf = Buffer.from(fullPath, 'utf-8');
|
|
984
|
+
if (encoding == 'buffer')
|
|
985
|
+
return buf;
|
|
986
|
+
return buf.toString(encoding);
|
|
987
|
+
}
|
|
988
988
|
realpath;
|
|
989
989
|
export function watch(filename, options = {}) {
|
|
990
990
|
const watcher = new FSWatcher(this, filename.toString(), typeof options !== 'string' ? options : { encoding: options });
|
|
@@ -1038,23 +1038,18 @@ access;
|
|
|
1038
1038
|
*/
|
|
1039
1039
|
export async function rm(path, options) {
|
|
1040
1040
|
path = normalizePath(path);
|
|
1041
|
-
const stats = await
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
if (!stats) {
|
|
1041
|
+
const stats = await lstat.call(this, path).catch((error) => {
|
|
1042
|
+
if (error.code == 'ENOENT' && (options === null || options === void 0 ? void 0 : options.force))
|
|
1043
|
+
return undefined;
|
|
1044
|
+
throw error;
|
|
1045
|
+
});
|
|
1046
|
+
if (!stats)
|
|
1048
1047
|
return;
|
|
1049
|
-
}
|
|
1050
|
-
cache.stats.set(path, stats);
|
|
1051
1048
|
switch (stats.mode & constants.S_IFMT) {
|
|
1052
1049
|
case constants.S_IFDIR:
|
|
1053
1050
|
if (options === null || options === void 0 ? void 0 : options.recursive) {
|
|
1054
|
-
for (const entry of await readdir.call(this, path
|
|
1055
|
-
|
|
1056
|
-
})) {
|
|
1057
|
-
await rm.call(this, join(path, entry), { ...options, _isIndirect: true });
|
|
1051
|
+
for (const entry of (await readdir.call(this, path))) {
|
|
1052
|
+
await rm.call(this, join(path, entry), options);
|
|
1058
1053
|
}
|
|
1059
1054
|
}
|
|
1060
1055
|
await rmdir.call(this, path);
|
|
@@ -1068,12 +1063,8 @@ export async function rm(path, options) {
|
|
|
1068
1063
|
case constants.S_IFIFO:
|
|
1069
1064
|
case constants.S_IFSOCK:
|
|
1070
1065
|
default:
|
|
1071
|
-
cache.stats.clear();
|
|
1072
1066
|
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
1073
1067
|
}
|
|
1074
|
-
if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
|
|
1075
|
-
cache.stats.clear();
|
|
1076
|
-
}
|
|
1077
1068
|
}
|
|
1078
1069
|
rm;
|
|
1079
1070
|
export async function mkdtemp(prefix, options) {
|
package/dist/vfs/shared.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as fs from 'node:fs';
|
|
2
2
|
import type { File } from '../internal/file.js';
|
|
3
3
|
import type { FileSystem } from '../internal/filesystem.js';
|
|
4
|
+
import type { Stats } from '../stats.js';
|
|
4
5
|
import { type BoundContext, type V_Context } from '../context.js';
|
|
5
6
|
import { ErrnoError } from '../internal/error.js';
|
|
6
7
|
import { type AbsolutePath } from './path.js';
|
|
@@ -46,6 +47,15 @@ export interface ResolvedMount {
|
|
|
46
47
|
mountPoint: string;
|
|
47
48
|
root: string;
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* @internal @hidden
|
|
52
|
+
*/
|
|
53
|
+
export interface ResolvedPath extends ResolvedMount {
|
|
54
|
+
/** The real, absolute path */
|
|
55
|
+
fullPath: string;
|
|
56
|
+
/** Stats */
|
|
57
|
+
stats?: Stats;
|
|
58
|
+
}
|
|
49
59
|
/**
|
|
50
60
|
* Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
|
|
51
61
|
* @internal @hidden
|
package/dist/vfs/shared.js
CHANGED
|
@@ -4,7 +4,6 @@ import { bindContext } from '../context.js';
|
|
|
4
4
|
import { Errno, ErrnoError } from '../internal/error.js';
|
|
5
5
|
import { alert, debug, err, info, log_deprecated, notice, warn } from '../internal/log.js';
|
|
6
6
|
import { normalizePath } from '../utils.js';
|
|
7
|
-
import { paths as pathCache } from './cache.js';
|
|
8
7
|
import { size_max } from './constants.js';
|
|
9
8
|
import { join, resolve } from './path.js';
|
|
10
9
|
// descriptors
|
|
@@ -54,7 +53,6 @@ export function mount(mountPoint, fs) {
|
|
|
54
53
|
mounts.set(mountPoint, fs);
|
|
55
54
|
info(`Mounted ${fs.name} on ${mountPoint}`);
|
|
56
55
|
debug(`${fs.name} attributes: ${[...fs.attributes].map(([k, v]) => (v !== undefined && v !== null ? k + '=' + v : v)).join(', ')}`);
|
|
57
|
-
pathCache.clear();
|
|
58
56
|
}
|
|
59
57
|
/**
|
|
60
58
|
* Unmounts the file system at `mountPoint`.
|
|
@@ -69,7 +67,6 @@ export function umount(mountPoint) {
|
|
|
69
67
|
return;
|
|
70
68
|
}
|
|
71
69
|
mounts.delete(mountPoint);
|
|
72
|
-
pathCache.clear();
|
|
73
70
|
notice('Unmounted ' + mountPoint);
|
|
74
71
|
}
|
|
75
72
|
/**
|
|
@@ -84,9 +81,8 @@ export function resolveMount(path, ctx) {
|
|
|
84
81
|
// We know path is normalized, so it would be a substring of the mount point.
|
|
85
82
|
if (_isParentOf(mountPoint, path)) {
|
|
86
83
|
path = path.slice(mountPoint.length > 1 ? mountPoint.length : 0); // Resolve the path relative to the mount point
|
|
87
|
-
if (path === '')
|
|
84
|
+
if (path === '')
|
|
88
85
|
path = root;
|
|
89
|
-
}
|
|
90
86
|
return { fs, path, mountPoint, root };
|
|
91
87
|
}
|
|
92
88
|
}
|
package/dist/vfs/sync.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as fs from 'node:fs';
|
|
2
2
|
import type { V_Context } from '../context.js';
|
|
3
3
|
import type { Stats } from '../stats.js';
|
|
4
|
-
import type { FileContents,
|
|
4
|
+
import type { FileContents, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
|
|
5
5
|
import { Buffer } from 'buffer';
|
|
6
6
|
import { BigIntStats } from '../stats.js';
|
|
7
7
|
import { Dir, Dirent } from './dir.js';
|
|
@@ -155,7 +155,7 @@ export declare function accessSync(this: V_Context, path: fs.PathLike, mode?: nu
|
|
|
155
155
|
* Synchronous `rm`. Removes files or directories (recursively).
|
|
156
156
|
* @param path The path to the file or directory to remove.
|
|
157
157
|
*/
|
|
158
|
-
export declare function rmSync(this: V_Context, path: fs.PathLike, options?: fs.RmOptions
|
|
158
|
+
export declare function rmSync(this: V_Context, path: fs.PathLike, options?: fs.RmOptions): void;
|
|
159
159
|
/**
|
|
160
160
|
* Synchronous `mkdtemp`. Creates a unique temporary directory.
|
|
161
161
|
* @param prefix The directory prefix.
|
package/dist/vfs/sync.js
CHANGED
|
@@ -56,7 +56,6 @@ import { Errno, ErrnoError } from '../internal/error.js';
|
|
|
56
56
|
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../internal/file.js';
|
|
57
57
|
import { BigIntStats } from '../stats.js';
|
|
58
58
|
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
59
|
-
import * as cache from './cache.js';
|
|
60
59
|
import { config } from './config.js';
|
|
61
60
|
import * as constants from './constants.js';
|
|
62
61
|
import { Dir, Dirent } from './dir.js';
|
|
@@ -154,7 +153,7 @@ export function unlinkSync(path) {
|
|
|
154
153
|
path = normalizePath(path);
|
|
155
154
|
const { fs, path: resolved } = resolveMount(path, this);
|
|
156
155
|
try {
|
|
157
|
-
if (config.checkAccess && !
|
|
156
|
+
if (config.checkAccess && !fs.statSync(resolved).hasAccess(constants.W_OK, this)) {
|
|
158
157
|
throw ErrnoError.With('EACCES', resolved, 'unlink');
|
|
159
158
|
}
|
|
160
159
|
fs.unlinkSync(resolved);
|
|
@@ -236,11 +235,12 @@ openSync;
|
|
|
236
235
|
export function lopenSync(path, flag, mode) {
|
|
237
236
|
return file2fd(_openSync.call(this, path, { flag, mode, preserveSymlinks: true }));
|
|
238
237
|
}
|
|
239
|
-
function _readFileSync(
|
|
238
|
+
function _readFileSync(path, flag, preserveSymlinks) {
|
|
240
239
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
241
240
|
try {
|
|
241
|
+
path = normalizePath(path);
|
|
242
242
|
// Get file.
|
|
243
|
-
const file = __addDisposableResource(env_2, _openSync.call(this,
|
|
243
|
+
const file = __addDisposableResource(env_2, _openSync.call(this, path, { flag, mode: 0o644, preserveSymlinks }), false);
|
|
244
244
|
const stat = file.statSync();
|
|
245
245
|
// Allocate buffer.
|
|
246
246
|
const data = new Uint8Array(stat.size);
|
|
@@ -261,7 +261,7 @@ export function readFileSync(path, _options = {}) {
|
|
|
261
261
|
if (!isReadable(flag)) {
|
|
262
262
|
throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading.');
|
|
263
263
|
}
|
|
264
|
-
const data = Buffer.from(_readFileSync.call(this, typeof path == 'number' ? fd2file(path).path : path
|
|
264
|
+
const data = Buffer.from(_readFileSync.call(this, typeof path == 'number' ? fd2file(path).path : path, options.flag, false));
|
|
265
265
|
return options.encoding ? data.toString(options.encoding) : data;
|
|
266
266
|
}
|
|
267
267
|
readFileSync;
|
|
@@ -426,7 +426,7 @@ export function rmdirSync(path) {
|
|
|
426
426
|
path = normalizePath(path);
|
|
427
427
|
const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
|
|
428
428
|
try {
|
|
429
|
-
const stats =
|
|
429
|
+
const stats = fs.statSync(resolved);
|
|
430
430
|
if (!stats.isDirectory()) {
|
|
431
431
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
432
432
|
}
|
|
@@ -484,8 +484,7 @@ export function readdirSync(path, options) {
|
|
|
484
484
|
const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
|
|
485
485
|
let entries;
|
|
486
486
|
try {
|
|
487
|
-
const stats =
|
|
488
|
-
cache.stats.set(path, stats);
|
|
487
|
+
const stats = fs.statSync(resolved);
|
|
489
488
|
if (config.checkAccess && !stats.hasAccess(constants.R_OK, this)) {
|
|
490
489
|
throw ErrnoError.With('EACCES', resolved, 'readdir');
|
|
491
490
|
}
|
|
@@ -500,8 +499,7 @@ export function readdirSync(path, options) {
|
|
|
500
499
|
// Iterate over entries and handle recursive case if needed
|
|
501
500
|
const values = [];
|
|
502
501
|
for (const entry of entries) {
|
|
503
|
-
const entryStat =
|
|
504
|
-
cache.stats.set(join(path, entry), entryStat);
|
|
502
|
+
const entryStat = fs.statSync(join(resolved, entry));
|
|
505
503
|
if (options === null || options === void 0 ? void 0 : options.withFileTypes) {
|
|
506
504
|
values.push(new Dirent(entry, entryStat));
|
|
507
505
|
}
|
|
@@ -513,7 +511,7 @@ export function readdirSync(path, options) {
|
|
|
513
511
|
}
|
|
514
512
|
if (!entryStat.isDirectory() || !(options === null || options === void 0 ? void 0 : options.recursive))
|
|
515
513
|
continue;
|
|
516
|
-
for (const subEntry of readdirSync.call(this, join(path, entry),
|
|
514
|
+
for (const subEntry of readdirSync.call(this, join(path, entry), options)) {
|
|
517
515
|
if (subEntry instanceof Dirent) {
|
|
518
516
|
subEntry.path = join(entry, subEntry.path);
|
|
519
517
|
values.push(subEntry);
|
|
@@ -526,9 +524,6 @@ export function readdirSync(path, options) {
|
|
|
526
524
|
}
|
|
527
525
|
}
|
|
528
526
|
}
|
|
529
|
-
if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
|
|
530
|
-
cache.stats.clear();
|
|
531
|
-
}
|
|
532
527
|
return values;
|
|
533
528
|
}
|
|
534
529
|
readdirSync;
|
|
@@ -570,13 +565,13 @@ export function symlinkSync(target, path, type = 'file') {
|
|
|
570
565
|
if (existsSync.call(this, path)) {
|
|
571
566
|
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
572
567
|
}
|
|
573
|
-
writeFileSync.call(this, path, target
|
|
568
|
+
writeFileSync.call(this, path, normalizePath(target, true));
|
|
574
569
|
const file = _openSync.call(this, path, { flag: 'r+', mode: 0o644, preserveSymlinks: true });
|
|
575
570
|
file.chmodSync(constants.S_IFLNK);
|
|
576
571
|
}
|
|
577
572
|
symlinkSync;
|
|
578
573
|
export function readlinkSync(path, options) {
|
|
579
|
-
const value = Buffer.from(_readFileSync.call(this, path
|
|
574
|
+
const value = Buffer.from(_readFileSync.call(this, path, 'r', true));
|
|
580
575
|
const encoding = typeof options == 'object' ? options === null || options === void 0 ? void 0 : options.encoding : options;
|
|
581
576
|
if (encoding == 'buffer') {
|
|
582
577
|
return value;
|
|
@@ -627,52 +622,63 @@ export function lutimesSync(path, atime, mtime) {
|
|
|
627
622
|
closeSync(fd);
|
|
628
623
|
}
|
|
629
624
|
lutimesSync;
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
625
|
+
/**
|
|
626
|
+
* Resolves the mount and real path for a path.
|
|
627
|
+
* Additionally, any stats fetched will be returned for de-duplication
|
|
628
|
+
* @internal @hidden
|
|
629
|
+
*/
|
|
630
|
+
function _resolveSync($, path, preserveSymlinks) {
|
|
631
|
+
if (preserveSymlinks) {
|
|
632
|
+
const resolved = resolveMount(path, $);
|
|
633
|
+
const stats = resolved.fs.statSync(resolved.path);
|
|
634
|
+
return { ...resolved, fullPath: path, stats };
|
|
635
|
+
}
|
|
635
636
|
/* Try to resolve it directly. If this works,
|
|
636
637
|
that means we don't need to perform any resolution for parent directories. */
|
|
637
638
|
try {
|
|
638
|
-
const
|
|
639
|
+
const resolved = resolveMount(path, $);
|
|
639
640
|
// Stat it to make sure it exists
|
|
640
|
-
const stats = fs.statSync(
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
const target = resolve(dirname(resolvedPath), readlinkSync.call(this, resolvedPath, options).toString());
|
|
644
|
-
real = cache.paths.get(((this === null || this === void 0 ? void 0 : this.root) || '') + target) || realpathSync.call(this, target);
|
|
645
|
-
cache.paths.set(ctx_path, real);
|
|
641
|
+
const stats = resolved.fs.statSync(resolved.path);
|
|
642
|
+
if (!stats.isSymbolicLink()) {
|
|
643
|
+
return { ...resolved, fullPath: path, stats };
|
|
646
644
|
}
|
|
647
|
-
|
|
648
|
-
return
|
|
645
|
+
const target = resolve(dirname(path), readlinkSync.call($, path).toString());
|
|
646
|
+
return _resolveSync($, target);
|
|
649
647
|
}
|
|
650
648
|
catch {
|
|
651
649
|
// Go the long way
|
|
652
650
|
}
|
|
653
651
|
const { base, dir } = parse(path);
|
|
654
|
-
const realDir = dir == '/' ? '/' :
|
|
655
|
-
const
|
|
656
|
-
const
|
|
652
|
+
const realDir = dir == '/' ? '/' : realpathSync.call($, dir);
|
|
653
|
+
const maybePath = join(realDir, base);
|
|
654
|
+
const resolved = resolveMount(maybePath, $);
|
|
657
655
|
try {
|
|
658
|
-
const stats =
|
|
659
|
-
cache.stats.set(lpath, stats);
|
|
656
|
+
const stats = resolved.fs.statSync(resolved.path);
|
|
660
657
|
if (!stats.isSymbolicLink()) {
|
|
661
|
-
|
|
662
|
-
return lpath;
|
|
658
|
+
return { ...resolved, fullPath: maybePath, stats };
|
|
663
659
|
}
|
|
664
|
-
const target = resolve(realDir, readlinkSync.call(
|
|
665
|
-
|
|
666
|
-
cache.paths.set(ctx_path, real);
|
|
667
|
-
return real;
|
|
660
|
+
const target = resolve(realDir, readlinkSync.call($, maybePath).toString());
|
|
661
|
+
return _resolveSync($, target);
|
|
668
662
|
}
|
|
669
663
|
catch (e) {
|
|
670
664
|
if (e.code == 'ENOENT') {
|
|
671
|
-
return path;
|
|
665
|
+
return { ...resolved, fullPath: path };
|
|
672
666
|
}
|
|
673
|
-
throw fixError(e, { [
|
|
667
|
+
throw fixError(e, { [resolved.path]: maybePath });
|
|
674
668
|
}
|
|
675
669
|
}
|
|
670
|
+
export function realpathSync(path, options) {
|
|
671
|
+
var _a;
|
|
672
|
+
const encoding = typeof options == 'string' ? options : ((_a = options === null || options === void 0 ? void 0 : options.encoding) !== null && _a !== void 0 ? _a : 'utf8');
|
|
673
|
+
path = normalizePath(path);
|
|
674
|
+
const { fullPath } = _resolveSync(this, path);
|
|
675
|
+
if (encoding == 'utf8' || encoding == 'utf-8')
|
|
676
|
+
return fullPath;
|
|
677
|
+
const buf = Buffer.from(fullPath, 'utf-8');
|
|
678
|
+
if (encoding == 'buffer')
|
|
679
|
+
return buf;
|
|
680
|
+
return buf.toString(encoding);
|
|
681
|
+
}
|
|
676
682
|
realpathSync;
|
|
677
683
|
export function accessSync(path, mode = 0o600) {
|
|
678
684
|
if (!config.checkAccess)
|
|
@@ -690,21 +696,19 @@ export function rmSync(path, options) {
|
|
|
690
696
|
path = normalizePath(path);
|
|
691
697
|
let stats;
|
|
692
698
|
try {
|
|
693
|
-
stats =
|
|
699
|
+
stats = lstatSync.bind(this)(path);
|
|
694
700
|
}
|
|
695
701
|
catch (error) {
|
|
696
702
|
if (error.code != 'ENOENT' || !(options === null || options === void 0 ? void 0 : options.force))
|
|
697
703
|
throw error;
|
|
698
704
|
}
|
|
699
|
-
if (!stats)
|
|
705
|
+
if (!stats)
|
|
700
706
|
return;
|
|
701
|
-
}
|
|
702
|
-
cache.stats.set(path, stats);
|
|
703
707
|
switch (stats.mode & constants.S_IFMT) {
|
|
704
708
|
case constants.S_IFDIR:
|
|
705
709
|
if (options === null || options === void 0 ? void 0 : options.recursive) {
|
|
706
|
-
for (const entry of readdirSync.call(this, path
|
|
707
|
-
rmSync.call(this, join(path, entry),
|
|
710
|
+
for (const entry of readdirSync.call(this, path)) {
|
|
711
|
+
rmSync.call(this, join(path, entry), options);
|
|
708
712
|
}
|
|
709
713
|
}
|
|
710
714
|
rmdirSync.call(this, path);
|
|
@@ -718,12 +722,8 @@ export function rmSync(path, options) {
|
|
|
718
722
|
case constants.S_IFIFO:
|
|
719
723
|
case constants.S_IFSOCK:
|
|
720
724
|
default:
|
|
721
|
-
cache.stats.clear();
|
|
722
725
|
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
723
726
|
}
|
|
724
|
-
if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
|
|
725
|
-
cache.stats.clear();
|
|
726
|
-
}
|
|
727
727
|
}
|
|
728
728
|
rmSync;
|
|
729
729
|
export function mkdtempSync(prefix, options) {
|
package/dist/vfs/types.d.ts
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
import type * as fs from 'node:fs';
|
|
2
2
|
export type FileContents = ArrayBufferView | string;
|
|
3
|
-
/**
|
|
4
|
-
* Options used for caching, among other things.
|
|
5
|
-
* @internal @hidden *UNSTABLE*
|
|
6
|
-
*/
|
|
7
|
-
export interface InternalOptions {
|
|
8
|
-
/**
|
|
9
|
-
* If true, then this readdir was called from another function.
|
|
10
|
-
* In this case, don't clear the cache when done.
|
|
11
|
-
* @internal *UNSTABLE*
|
|
12
|
-
*/
|
|
13
|
-
_isIndirect?: boolean;
|
|
14
|
-
}
|
|
15
3
|
/**
|
|
16
4
|
* @internal @hidden Used for the internal `_open` functions
|
|
17
5
|
*/
|
|
@@ -27,7 +15,7 @@ export interface OpenOptions {
|
|
|
27
15
|
*/
|
|
28
16
|
allowDirectory?: boolean;
|
|
29
17
|
}
|
|
30
|
-
export interface ReaddirOptions
|
|
18
|
+
export interface ReaddirOptions {
|
|
31
19
|
withFileTypes?: boolean;
|
|
32
20
|
recursive?: boolean;
|
|
33
21
|
}
|