@zenfs/core 1.9.3 → 1.9.4

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.
@@ -73,5 +73,8 @@ declare const _Fetch: {
73
73
  type _Fetch = typeof _Fetch;
74
74
  export interface Fetch extends _Fetch {
75
75
  }
76
+ /**
77
+ * @category Backends and Configuration
78
+ */
76
79
  export declare const Fetch: Fetch;
77
80
  export {};
@@ -123,4 +123,7 @@ const _Fetch = {
123
123
  return fs;
124
124
  },
125
125
  };
126
+ /**
127
+ * @category Backends and Configuration
128
+ */
126
129
  export const Fetch = _Fetch;
@@ -31,6 +31,7 @@ export interface InMemory extends _InMemory {
31
31
  /**
32
32
  * A simple in-memory file system backed by an InMemoryStore.
33
33
  * Files are not persisted across page loads.
34
+ * @category Backends and Configuration
34
35
  */
35
36
  export declare const InMemory: InMemory;
36
37
  export {};
@@ -33,5 +33,6 @@ const _InMemory = {
33
33
  /**
34
34
  * A simple in-memory file system backed by an InMemoryStore.
35
35
  * Files are not persisted across page loads.
36
+ * @category Backends and Configuration
36
37
  */
37
38
  export const InMemory = _InMemory;
@@ -117,6 +117,7 @@ export interface Overlay extends _Overlay {
117
117
  /**
118
118
  * Overlay makes a read-only filesystem writable by storing writes on a second, writable file system.
119
119
  * Deletes are persisted via metadata stored on the writable file system.
120
+ * @category Backends and Configuration
120
121
  * @internal
121
122
  */
122
123
  export declare const Overlay: Overlay;
@@ -550,6 +550,7 @@ const _Overlay = {
550
550
  /**
551
551
  * Overlay makes a read-only filesystem writable by storing writes on a second, writable file system.
552
552
  * Deletes are persisted via metadata stored on the writable file system.
553
+ * @category Backends and Configuration
553
554
  * @internal
554
555
  */
555
556
  export const Overlay = _Overlay;
@@ -2,12 +2,12 @@ import type { ExtractProperties } from 'utilium';
2
2
  import type { Inode, InodeLike } from '../..//internal/inode.js';
3
3
  import type { MountConfiguration } from '../../config.js';
4
4
  import type { File } from '../../internal/file.js';
5
- import type { CreationOptions, FileSystemMetadata } from '../../internal/filesystem.js';
5
+ import type { CreationOptions, UsageInfo } from '../../internal/filesystem.js';
6
6
  import type { Backend, FilesystemOf } from '../backend.js';
7
7
  import { FileSystem } from '../../internal/filesystem.js';
8
8
  import { Stats } from '../../stats.js';
9
9
  import * as RPC from './rpc.js';
10
- type FSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<any> | FileSystemMetadata>;
10
+ type FSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<any> | UsageInfo>;
11
11
  type FSMethod = keyof FSMethods;
12
12
  export type FSRequest<TMethod extends FSMethod = FSMethod> = RPC.Message & {
13
13
  [M in TMethod]: {
@@ -73,6 +73,12 @@ declare const _Port: {
73
73
  type _Port = typeof _Port;
74
74
  export interface Port extends _Port {
75
75
  }
76
+ /**
77
+ * @category Backends and Configuration
78
+ */
76
79
  export declare const Port: Port;
80
+ /**
81
+ * @category Backends and Configuration
82
+ */
77
83
  export declare function resolveRemoteMount<T extends Backend>(port: RPC.Port, config: MountConfiguration<T>, _depth?: number): Promise<FilesystemOf<T>>;
78
84
  export {};
@@ -134,7 +134,13 @@ const _Port = {
134
134
  return new PortFS(options);
135
135
  },
136
136
  };
137
+ /**
138
+ * @category Backends and Configuration
139
+ */
137
140
  export const Port = _Port;
141
+ /**
142
+ * @category Backends and Configuration
143
+ */
138
144
  export async function resolveRemoteMount(port, config, _depth = 0) {
139
145
  const stopAndReplay = RPC.catchMessages(port);
140
146
  const fs = await resolveMountConfig(config, _depth);
@@ -6,11 +6,8 @@ import { Inode, type InodeLike } from '../../internal/inode.js';
6
6
  import type { Stats } from '../../stats.js';
7
7
  import { WrappedTransaction, type Store } from './store.js';
8
8
  /**
9
- * A file system which uses a key-value store.
9
+ * A file system which uses a `Store`
10
10
  *
11
- * We use a unique ID for each node in the file system. The root node has a fixed ID.
12
- *
13
- * @todo Introduce Node ID caching?
14
11
  * @todo Check modes?
15
12
  * @category Stores and Transactions
16
13
  * @internal
@@ -63,11 +63,8 @@ import { S_IFDIR, S_IFREG, S_ISGID, S_ISUID, size_max } from '../../vfs/constant
63
63
  import { basename, dirname, join, parse, relative } from '../../vfs/path.js';
64
64
  import { WrappedTransaction } from './store.js';
65
65
  /**
66
- * A file system which uses a key-value store.
66
+ * A file system which uses a `Store`
67
67
  *
68
- * We use a unique ID for each node in the file system. The root node has a fixed ID.
69
- *
70
- * @todo Introduce Node ID caching?
71
68
  * @todo Check modes?
72
69
  * @category Stores and Transactions
73
70
  * @internal
package/dist/config.d.ts CHANGED
@@ -45,21 +45,6 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
45
45
  * @default false
46
46
  */
47
47
  addDevices: boolean;
48
- /**
49
- * If true, enables caching stats for certain operations.
50
- * This should reduce the number of stat calls performed.
51
- * @experimental
52
- * @default false
53
- */
54
- cacheStats: boolean;
55
- /**
56
- * If true, enables caching realpath output
57
- *
58
- * This can increase performance.
59
- * @experimental
60
- * @default false
61
- */
62
- cachePaths: boolean;
63
48
  /**
64
49
  * If true, disables *all* permissions checking.
65
50
  *
package/dist/config.js CHANGED
@@ -4,7 +4,6 @@ import { DeviceFS } from './internal/devices.js';
4
4
  import { Errno, ErrnoError } from './internal/error.js';
5
5
  import { FileSystem } from './internal/filesystem.js';
6
6
  import { configure as configureLog, crit, err, info } from './internal/log.js';
7
- import * as cache from './vfs/cache.js';
8
7
  import { config } from './vfs/config.js';
9
8
  import * as fs from './vfs/index.js';
10
9
  import { mounts } from './vfs/shared.js';
@@ -99,12 +98,10 @@ export function addDevice(driver, options) {
99
98
  * @see Configuration
100
99
  */
101
100
  export async function configure(configuration) {
102
- var _a, _b, _c;
101
+ var _a;
103
102
  const uid = 'uid' in configuration ? configuration.uid || 0 : 0;
104
103
  const gid = 'gid' in configuration ? configuration.gid || 0 : 0;
105
104
  useCredentials({ uid, gid });
106
- cache.stats.isEnabled = (_a = configuration.cacheStats) !== null && _a !== void 0 ? _a : false;
107
- cache.paths.isEnabled = (_b = configuration.cachePaths) !== null && _b !== void 0 ? _b : false;
108
105
  config.checkAccess = !configuration.disableAccessChecks;
109
106
  config.updateOnRead = !configuration.disableUpdateOnRead;
110
107
  config.syncImmediately = !configuration.onlySyncOnClose;
@@ -115,7 +112,7 @@ export async function configure(configuration) {
115
112
  for (const [_point, mountConfig] of Object.entries(configuration.mounts).sort(([a], [b]) => (a.length > b.length ? 1 : -1))) {
116
113
  const point = _point.startsWith('/') ? _point : '/' + _point;
117
114
  if (isBackendConfig(mountConfig)) {
118
- (_c = mountConfig.disableAsyncCache) !== null && _c !== void 0 ? _c : (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);
115
+ (_a = mountConfig.disableAsyncCache) !== null && _a !== void 0 ? _a : (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);
119
116
  }
120
117
  if (point == '/')
121
118
  fs.umount('/');
@@ -10,7 +10,7 @@ export interface IndexData {
10
10
  }
11
11
  export declare const version = 1;
12
12
  /**
13
- * An index of files
13
+ * An index of file metadata
14
14
  * @category Internals
15
15
  * @internal
16
16
  */
@@ -6,7 +6,7 @@ import { Errno, ErrnoError } from './error.js';
6
6
  import { Inode } from './inode.js';
7
7
  export const version = 1;
8
8
  /**
9
- * An index of files
9
+ * An index of file metadata
10
10
  * @category Internals
11
11
  * @internal
12
12
  */
@@ -4,7 +4,7 @@ import { Index } from './file_index.js';
4
4
  import { FileSystem, type CreationOptions, type PureCreationOptions } from './filesystem.js';
5
5
  import { Inode, type InodeLike } from './inode.js';
6
6
  /**
7
- * Uses an `Index` for metadata.
7
+ * A file system that uses an `Index` for metadata.
8
8
  * @category Internals
9
9
  * @internal
10
10
  */
@@ -9,7 +9,7 @@ import { Index } from './file_index.js';
9
9
  import { FileSystem } from './filesystem.js';
10
10
  import { Inode } from './inode.js';
11
11
  /**
12
- * Uses an `Index` for metadata.
12
+ * A file system that uses an `Index` for metadata.
13
13
  * @category Internals
14
14
  * @internal
15
15
  */
@@ -6,7 +6,7 @@ import type { Interface as ReadlineInterface } from 'readline';
6
6
  import type { V_Context } from '../context.js';
7
7
  import type { File } from '../internal/file.js';
8
8
  import type { Stats } from '../stats.js';
9
- import type { FileContents, InternalOptions, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
9
+ import type { FileContents, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
10
10
  import { Buffer } from 'buffer';
11
11
  import '../polyfills.js';
12
12
  import { BigIntStats } from '../stats.js';
@@ -304,7 +304,7 @@ export declare function access(this: V_Context, path: fs.PathLike, mode?: number
304
304
  * Asynchronous `rm`. Removes files or directories (recursively).
305
305
  * @param path The path to the file or directory to remove.
306
306
  */
307
- export declare function rm(this: V_Context, path: fs.PathLike, options?: fs.RmOptions & InternalOptions): Promise<void>;
307
+ export declare function rm(this: V_Context, path: fs.PathLike, options?: fs.RmOptions): Promise<void>;
308
308
  /**
309
309
  * Asynchronous `mkdtemp`. Creates a unique temporary directory.
310
310
  * @param prefix The directory prefix.
@@ -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';
@@ -459,7 +458,7 @@ export async function unlink(path) {
459
458
  path = normalizePath(path);
460
459
  const { fs, path: resolved } = resolveMount(path, this);
461
460
  try {
462
- if (config.checkAccess && !(await (cache.stats.getAsync(path) || fs.stat(resolved))).hasAccess(constants.W_OK, this)) {
461
+ if (config.checkAccess && !(await fs.stat(resolved)).hasAccess(constants.W_OK, this)) {
463
462
  throw ErrnoError.With('EACCES', resolved, 'unlink');
464
463
  }
465
464
  await fs.unlink(resolved);
@@ -484,37 +483,35 @@ async function applySetId(file, uid, gid) {
484
483
  * Opens a file. This helper handles the complexity of file flags.
485
484
  * @internal
486
485
  */
487
- async function _open(path, opt) {
486
+ async function _open($, path, opt) {
488
487
  var _a;
489
488
  path = normalizePath(path);
490
489
  const mode = normalizeMode(opt.mode, 0o644), flag = parseFlag(opt.flag);
491
- path = opt.preserveSymlinks ? path : await realpath.call(this, path);
492
- const { fs, path: resolved } = resolveMount(path, this);
493
- const stats = await fs.stat(resolved).catch(() => null);
490
+ const { fullPath: realpath, fs, path: resolved, stats } = await _resolve($, path.toString(), opt.preserveSymlinks);
494
491
  if (!stats) {
495
492
  if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
496
- throw ErrnoError.With('ENOENT', path, '_open');
493
+ throw ErrnoError.With('ENOENT', realpath, '_open');
497
494
  }
498
495
  // Create the file
499
496
  const parentStats = await fs.stat(dirname(resolved));
500
- if (config.checkAccess && !parentStats.hasAccess(constants.W_OK, this)) {
501
- throw ErrnoError.With('EACCES', dirname(path), '_open');
497
+ if (config.checkAccess && !parentStats.hasAccess(constants.W_OK, $)) {
498
+ throw ErrnoError.With('EACCES', dirname(realpath), '_open');
502
499
  }
503
500
  if (!parentStats.isDirectory()) {
504
- throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
501
+ throw ErrnoError.With('ENOTDIR', dirname(realpath), '_open');
505
502
  }
506
- const { euid: uid, egid: gid } = (_a = this === null || this === void 0 ? void 0 : this.credentials) !== null && _a !== void 0 ? _a : credentials;
503
+ const { euid: uid, egid: gid } = (_a = $ === null || $ === void 0 ? void 0 : $.credentials) !== null && _a !== void 0 ? _a : credentials;
507
504
  const file = await fs.createFile(resolved, flag, mode, { uid, gid });
508
505
  await applySetId(file, uid, gid);
509
- return new FileHandle(file, this);
506
+ return new FileHandle(file, $);
510
507
  }
511
- if (config.checkAccess && !stats.hasAccess(flagToMode(flag), this)) {
512
- throw ErrnoError.With('EACCES', path, '_open');
508
+ if (config.checkAccess && !stats.hasAccess(flagToMode(flag), $)) {
509
+ throw ErrnoError.With('EACCES', realpath, '_open');
513
510
  }
514
511
  if (isExclusive(flag)) {
515
- throw ErrnoError.With('EEXIST', path, '_open');
512
+ throw ErrnoError.With('EEXIST', realpath, '_open');
516
513
  }
517
- const handle = new FileHandle(await fs.openFile(resolved, flag), this);
514
+ const handle = new FileHandle(await fs.openFile(resolved, flag), $);
518
515
  /*
519
516
  In a previous implementation, we deleted the file and
520
517
  re-created it. However, this created a race condition if another
@@ -533,7 +530,7 @@ async function _open(path, opt) {
533
530
  * @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
534
531
  */
535
532
  export async function open(path, flag = 'r', mode = 0o644) {
536
- return await _open.call(this, path, { flag, mode });
533
+ return await _open(this, path, { flag, mode });
537
534
  }
538
535
  open;
539
536
  export async function readFile(path, _options) {
@@ -625,7 +622,7 @@ export async function rmdir(path) {
625
622
  path = await realpath.call(this, path);
626
623
  const { fs, path: resolved } = resolveMount(path, this);
627
624
  try {
628
- const stats = await (cache.stats.getAsync(path) || fs.stat(resolved));
625
+ const stats = await fs.stat(resolved);
629
626
  if (!stats) {
630
627
  throw ErrnoError.With('ENOENT', path, 'rmdir');
631
628
  }
@@ -688,9 +685,7 @@ export async function readdir(path, options) {
688
685
  throw fixError(e, { [resolved]: path });
689
686
  };
690
687
  const { fs, path: resolved } = resolveMount(path, this);
691
- const _stats = cache.stats.getAsync(path) || fs.stat(resolved).catch(handleError);
692
- cache.stats.setAsync(path, _stats);
693
- const stats = await _stats;
688
+ const stats = await fs.stat(resolved).catch(handleError);
694
689
  if (!stats) {
695
690
  throw ErrnoError.With('ENOENT', path, 'readdir');
696
691
  }
@@ -705,9 +700,7 @@ export async function readdir(path, options) {
705
700
  const addEntry = async (entry) => {
706
701
  let entryStats;
707
702
  if ((options === null || options === void 0 ? void 0 : options.recursive) || (options === null || options === void 0 ? void 0 : options.withFileTypes)) {
708
- const _entryStats = cache.stats.getAsync(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
709
- cache.stats.setAsync(join(path, entry), _entryStats);
710
- entryStats = await _entryStats;
703
+ entryStats = await fs.stat(join(resolved, entry)).catch(handleError);
711
704
  }
712
705
  if (options === null || options === void 0 ? void 0 : options.withFileTypes) {
713
706
  values.push(new Dirent(entry, entryStats));
@@ -720,7 +713,7 @@ export async function readdir(path, options) {
720
713
  }
721
714
  if (!(options === null || options === void 0 ? void 0 : options.recursive) || !(entryStats === null || entryStats === void 0 ? void 0 : entryStats.isDirectory()))
722
715
  return;
723
- for (const subEntry of await readdir.call(this, join(path, entry), { ...options, _isIndirect: true })) {
716
+ for (const subEntry of await readdir.call(this, join(path, entry), options)) {
724
717
  if (subEntry instanceof Dirent) {
725
718
  subEntry.path = join(entry, subEntry.path);
726
719
  values.push(subEntry);
@@ -735,9 +728,6 @@ export async function readdir(path, options) {
735
728
  }
736
729
  };
737
730
  await Promise.all(entries.map(addEntry));
738
- if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
739
- cache.stats.clear();
740
- }
741
731
  return values;
742
732
  }
743
733
  readdir;
@@ -781,7 +771,7 @@ export async function symlink(target, path, type = 'file') {
781
771
  if (await exists.call(this, path)) {
782
772
  throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
783
773
  }
784
- const handle = __addDisposableResource(env_5, await _open.call(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
774
+ const handle = __addDisposableResource(env_5, await _open(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
785
775
  await handle.writeFile(target.toString());
786
776
  await handle.file.chmod(constants.S_IFLNK);
787
777
  }
@@ -799,7 +789,7 @@ symlink;
799
789
  export async function readlink(path, options) {
800
790
  const env_6 = { stack: [], error: void 0, hasError: false };
801
791
  try {
802
- const handle = __addDisposableResource(env_6, await _open.call(this, normalizePath(path), { flag: 'r', mode: 0o644, preserveSymlinks: true }), true);
792
+ const handle = __addDisposableResource(env_6, await _open(this, normalizePath(path), { flag: 'r', mode: 0o644, preserveSymlinks: true }), true);
803
793
  const value = await handle.readFile();
804
794
  const encoding = typeof options == 'object' ? options === null || options === void 0 ? void 0 : options.encoding : options;
805
795
  // always defaults to utf-8 to avoid wrangler (cloudflare) worker "unknown encoding" exception
@@ -836,7 +826,7 @@ chown;
836
826
  export async function lchown(path, uid, gid) {
837
827
  const env_8 = { stack: [], error: void 0, hasError: false };
838
828
  try {
839
- const handle = __addDisposableResource(env_8, await _open.call(this, path, {
829
+ const handle = __addDisposableResource(env_8, await _open(this, path, {
840
830
  flag: 'r+',
841
831
  mode: 0o644,
842
832
  preserveSymlinks: true,
@@ -875,7 +865,7 @@ chmod;
875
865
  export async function lchmod(path, mode) {
876
866
  const env_10 = { stack: [], error: void 0, hasError: false };
877
867
  try {
878
- const handle = __addDisposableResource(env_10, await _open.call(this, path, {
868
+ const handle = __addDisposableResource(env_10, await _open(this, path, {
879
869
  flag: 'r+',
880
870
  mode: 0o644,
881
871
  preserveSymlinks: true,
@@ -920,7 +910,7 @@ utimes;
920
910
  export async function lutimes(path, atime, mtime) {
921
911
  const env_12 = { stack: [], error: void 0, hasError: false };
922
912
  try {
923
- const handle = __addDisposableResource(env_12, await _open.call(this, path, {
913
+ const handle = __addDisposableResource(env_12, await _open(this, path, {
924
914
  flag: 'r+',
925
915
  mode: 0o644,
926
916
  preserveSymlinks: true,
@@ -939,52 +929,64 @@ export async function lutimes(path, atime, mtime) {
939
929
  }
940
930
  }
941
931
  lutimes;
942
- export async function realpath(path, options) {
943
- path = normalizePath(path);
944
- const ctx_path = ((this === null || this === void 0 ? void 0 : this.root) || '') + path;
945
- if (cache.paths.hasAsync(ctx_path))
946
- return cache.paths.getAsync(ctx_path);
932
+ /**
933
+ * Resolves the mount and real path for a path.
934
+ * Additionally, any stats fetched will be returned for de-duplication
935
+ * @internal @hidden
936
+ */
937
+ async function _resolve($, path, preserveSymlinks) {
938
+ if (preserveSymlinks) {
939
+ const resolved = resolveMount(path, $);
940
+ const stats = await resolved.fs.stat(resolved.path).catch(() => undefined);
941
+ return { ...resolved, fullPath: path, stats };
942
+ }
947
943
  /* Try to resolve it directly. If this works,
948
944
  that means we don't need to perform any resolution for parent directories. */
949
945
  try {
950
- const { fs, path: resolvedPath } = resolveMount(path, this);
946
+ const resolved = resolveMount(path, $);
951
947
  // Stat it to make sure it exists
952
- const stats = await fs.stat(resolvedPath);
953
- let real = path.toString();
954
- if (stats.isSymbolicLink()) {
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);
948
+ const stats = await resolved.fs.stat(resolved.path);
949
+ if (!stats.isSymbolicLink()) {
950
+ return { ...resolved, fullPath: path, stats };
958
951
  }
959
- cache.paths.set(path.toString(), real);
960
- return real;
952
+ const target = resolve(dirname(path), (await readlink.call($, path)).toString());
953
+ return await _resolve($, target);
961
954
  }
962
955
  catch {
963
956
  // Go the long way
964
957
  }
965
958
  const { base, dir } = parse(path);
966
- const realDir = dir == '/' ? '/' : await (cache.paths.getAsync(((this === null || this === void 0 ? void 0 : this.root) || '') + dir) || realpath.call(this, dir));
967
- const lpath = join(realDir, base);
968
- const { fs, path: resolvedPath } = resolveMount(lpath, this);
959
+ const realDir = dir == '/' ? '/' : await realpath.call($, dir);
960
+ const maybePath = join(realDir, base);
961
+ const resolved = resolveMount(maybePath, $);
969
962
  try {
970
- const _stats = cache.stats.getAsync(lpath) || fs.stat(resolvedPath);
971
- cache.stats.setAsync(lpath, _stats);
972
- if (!(await _stats).isSymbolicLink()) {
973
- cache.paths.set(path, lpath);
974
- return lpath;
963
+ const stats = await resolved.fs.stat(resolved.path);
964
+ if (!stats.isSymbolicLink()) {
965
+ return { ...resolved, fullPath: maybePath, stats };
975
966
  }
976
- const target = resolve(realDir, (await readlink.call(this, lpath)).toString());
977
- const real = cache.paths.getAsync(((this === null || this === void 0 ? void 0 : this.root) || '') + target) || realpath.call(this, target);
978
- cache.paths.setAsync(ctx_path, real);
979
- return await real;
967
+ const target = resolve(realDir, (await readlink.call($, maybePath)).toString());
968
+ const real = await realpath.call($, target);
969
+ return { ...resolved, fullPath: real, stats };
980
970
  }
981
971
  catch (e) {
982
972
  if (e.code == 'ENOENT') {
983
- return path;
973
+ return { ...resolved, fullPath: path };
984
974
  }
985
- throw fixError(e, { [resolvedPath]: lpath });
975
+ throw fixError(e, { [resolved.path]: maybePath });
986
976
  }
987
977
  }
978
+ export async function realpath(path, options) {
979
+ var _a;
980
+ const encoding = typeof options == 'string' ? options : ((_a = options === null || options === void 0 ? void 0 : options.encoding) !== null && _a !== void 0 ? _a : 'utf8');
981
+ path = normalizePath(path);
982
+ const { fullPath } = await _resolve(this, path);
983
+ if (encoding == 'utf8' || encoding == 'utf-8')
984
+ return fullPath;
985
+ const buf = Buffer.from(fullPath, 'utf-8');
986
+ if (encoding == 'buffer')
987
+ return buf;
988
+ return buf.toString(encoding);
989
+ }
988
990
  realpath;
989
991
  export function watch(filename, options = {}) {
990
992
  const watcher = new FSWatcher(this, filename.toString(), typeof options !== 'string' ? options : { encoding: options });
@@ -1038,23 +1040,18 @@ access;
1038
1040
  */
1039
1041
  export async function rm(path, options) {
1040
1042
  path = normalizePath(path);
1041
- const stats = await (cache.stats.getAsync(path) ||
1042
- lstat.call(this, path).catch((error) => {
1043
- if (error.code == 'ENOENT' && (options === null || options === void 0 ? void 0 : options.force))
1044
- return undefined;
1045
- throw error;
1046
- }));
1047
- if (!stats) {
1043
+ const stats = await lstat.call(this, path).catch((error) => {
1044
+ if (error.code == 'ENOENT' && (options === null || options === void 0 ? void 0 : options.force))
1045
+ return undefined;
1046
+ throw error;
1047
+ });
1048
+ if (!stats)
1048
1049
  return;
1049
- }
1050
- cache.stats.set(path, stats);
1051
1050
  switch (stats.mode & constants.S_IFMT) {
1052
1051
  case constants.S_IFDIR:
1053
1052
  if (options === null || options === void 0 ? void 0 : options.recursive) {
1054
- for (const entry of await readdir.call(this, path, {
1055
- _isIndirect: true,
1056
- })) {
1057
- await rm.call(this, join(path, entry), { ...options, _isIndirect: true });
1053
+ for (const entry of (await readdir.call(this, path))) {
1054
+ await rm.call(this, join(path, entry), options);
1058
1055
  }
1059
1056
  }
1060
1057
  await rmdir.call(this, path);
@@ -1068,12 +1065,8 @@ export async function rm(path, options) {
1068
1065
  case constants.S_IFIFO:
1069
1066
  case constants.S_IFSOCK:
1070
1067
  default:
1071
- cache.stats.clear();
1072
1068
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
1073
1069
  }
1074
- if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
1075
- cache.stats.clear();
1076
- }
1077
1070
  }
1078
1071
  rm;
1079
1072
  export async function mkdtemp(prefix, options) {
@@ -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
@@ -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
  }
@@ -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, InternalOptions, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
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 & InternalOptions): void;
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 && !(cache.stats.get(path) || fs.statSync(resolved)).hasAccess(constants.W_OK, this)) {
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);
@@ -426,7 +425,7 @@ export function rmdirSync(path) {
426
425
  path = normalizePath(path);
427
426
  const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
428
427
  try {
429
- const stats = cache.stats.get(path) || fs.statSync(resolved);
428
+ const stats = fs.statSync(resolved);
430
429
  if (!stats.isDirectory()) {
431
430
  throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
432
431
  }
@@ -484,8 +483,7 @@ export function readdirSync(path, options) {
484
483
  const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
485
484
  let entries;
486
485
  try {
487
- const stats = cache.stats.get(path) || fs.statSync(resolved);
488
- cache.stats.set(path, stats);
486
+ const stats = fs.statSync(resolved);
489
487
  if (config.checkAccess && !stats.hasAccess(constants.R_OK, this)) {
490
488
  throw ErrnoError.With('EACCES', resolved, 'readdir');
491
489
  }
@@ -500,8 +498,7 @@ export function readdirSync(path, options) {
500
498
  // Iterate over entries and handle recursive case if needed
501
499
  const values = [];
502
500
  for (const entry of entries) {
503
- const entryStat = cache.stats.get(join(path, entry)) || fs.statSync(join(resolved, entry));
504
- cache.stats.set(join(path, entry), entryStat);
501
+ const entryStat = fs.statSync(join(resolved, entry));
505
502
  if (options === null || options === void 0 ? void 0 : options.withFileTypes) {
506
503
  values.push(new Dirent(entry, entryStat));
507
504
  }
@@ -513,7 +510,7 @@ export function readdirSync(path, options) {
513
510
  }
514
511
  if (!entryStat.isDirectory() || !(options === null || options === void 0 ? void 0 : options.recursive))
515
512
  continue;
516
- for (const subEntry of readdirSync.call(this, join(path, entry), { ...options, _isIndirect: true })) {
513
+ for (const subEntry of readdirSync.call(this, join(path, entry), options)) {
517
514
  if (subEntry instanceof Dirent) {
518
515
  subEntry.path = join(entry, subEntry.path);
519
516
  values.push(subEntry);
@@ -526,9 +523,6 @@ export function readdirSync(path, options) {
526
523
  }
527
524
  }
528
525
  }
529
- if (!(options === null || options === void 0 ? void 0 : options._isIndirect)) {
530
- cache.stats.clear();
531
- }
532
526
  return values;
533
527
  }
534
528
  readdirSync;
@@ -627,52 +621,64 @@ export function lutimesSync(path, atime, mtime) {
627
621
  closeSync(fd);
628
622
  }
629
623
  lutimesSync;
630
- export function realpathSync(path, options) {
631
- path = normalizePath(path);
632
- const ctx_path = ((this === null || this === void 0 ? void 0 : this.root) || '') + path;
633
- if (cache.paths.has(ctx_path))
634
- return cache.paths.get(ctx_path);
624
+ /**
625
+ * Resolves the mount and real path for a path.
626
+ * Additionally, any stats fetched will be returned for de-duplication
627
+ * @internal @hidden
628
+ */
629
+ function _resolveSync($, path, preserveSymlinks) {
630
+ if (preserveSymlinks) {
631
+ const resolved = resolveMount(path, $);
632
+ const stats = resolved.fs.statSync(resolved.path);
633
+ return { ...resolved, fullPath: path, stats };
634
+ }
635
635
  /* Try to resolve it directly. If this works,
636
636
  that means we don't need to perform any resolution for parent directories. */
637
637
  try {
638
- const { fs, path: resolvedPath } = resolveMount(path, this);
638
+ const resolved = resolveMount(path, $);
639
639
  // Stat it to make sure it exists
640
- const stats = fs.statSync(resolvedPath);
641
- let real = path;
642
- if (stats.isSymbolicLink()) {
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);
640
+ const stats = resolved.fs.statSync(resolved.path);
641
+ if (!stats.isSymbolicLink()) {
642
+ return { ...resolved, fullPath: path, stats };
646
643
  }
647
- cache.paths.set(path, real);
648
- return real;
644
+ const target = resolve(dirname(path), readlinkSync.call($, path).toString());
645
+ return _resolveSync($, target);
649
646
  }
650
647
  catch {
651
648
  // Go the long way
652
649
  }
653
650
  const { base, dir } = parse(path);
654
- const realDir = dir == '/' ? '/' : cache.paths.get(((this === null || this === void 0 ? void 0 : this.root) || '') + dir) || realpathSync.call(this, dir);
655
- const lpath = join(realDir, base);
656
- const { fs, path: resolvedPath } = resolveMount(lpath, this);
651
+ const realDir = dir == '/' ? '/' : realpathSync.call($, dir);
652
+ const maybePath = join(realDir, base);
653
+ const resolved = resolveMount(maybePath, $);
657
654
  try {
658
- const stats = cache.stats.get(lpath) || fs.statSync(resolvedPath);
659
- cache.stats.set(lpath, stats);
655
+ const stats = resolved.fs.statSync(resolved.path);
660
656
  if (!stats.isSymbolicLink()) {
661
- cache.paths.set(path, lpath);
662
- return lpath;
657
+ return { ...resolved, fullPath: maybePath, stats };
663
658
  }
664
- const target = resolve(realDir, readlinkSync.call(this, lpath, options).toString());
665
- const real = cache.paths.get(((this === null || this === void 0 ? void 0 : this.root) || '') + target) || realpathSync.call(this, target);
666
- cache.paths.set(ctx_path, real);
667
- return real;
659
+ const target = resolve(realDir, readlinkSync.call($, maybePath).toString());
660
+ const real = realpathSync.call($, target);
661
+ return { ...resolved, fullPath: real, stats };
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, { [resolvedPath]: lpath });
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 = cache.stats.get(path) || lstatSync.bind(this)(path);
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, { _isIndirect: true })) {
707
- rmSync.call(this, join(path, entry), { ...options, _isIndirect: true });
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) {
@@ -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 extends InternalOptions {
18
+ export interface ReaddirOptions {
31
19
  withFileTypes?: boolean;
32
20
  recursive?: boolean;
33
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.9.3",
3
+ "version": "1.9.4",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -1,46 +0,0 @@
1
- import type { Stats } from '../stats.js';
2
- /**
3
- * Used for caching data
4
- * @internal
5
- */
6
- export declare class Cache<T> {
7
- isEnabled: boolean;
8
- protected sync: Map<string, T>;
9
- protected async: Map<string, Promise<T>>;
10
- /**
11
- * Whether the data exists in the cache
12
- */
13
- has(path: string): boolean;
14
- /**
15
- * Gets data from the cache, if is exists and the cache is enabled.
16
- */
17
- get(path: string): T | undefined;
18
- /**
19
- * Adds data if the cache is enabled
20
- */
21
- set(path: string, value: T): void;
22
- /**
23
- * Whether the data exists in the cache
24
- */
25
- hasAsync(path: string): boolean;
26
- /**
27
- * Gets data from the cache, if it exists and the cache is enabled.
28
- */
29
- getAsync(path: string): Promise<T> | undefined;
30
- /**
31
- * Adds data if the cache is enabled
32
- */
33
- setAsync(path: string, value: Promise<T>): void;
34
- /**
35
- * Clears the cache if it is enabled
36
- */
37
- clear(): void;
38
- }
39
- /**
40
- * Used to cache
41
- */
42
- export declare const stats: Cache<Stats>;
43
- /**
44
- * Used to cache realpath lookups
45
- */
46
- export declare const paths: Cache<string>;
package/dist/vfs/cache.js DELETED
@@ -1,75 +0,0 @@
1
- /* Experimental caching */
2
- /**
3
- * Used for caching data
4
- * @internal
5
- */
6
- export class Cache {
7
- constructor() {
8
- this.isEnabled = false;
9
- this.sync = new Map();
10
- this.async = new Map();
11
- }
12
- /**
13
- * Whether the data exists in the cache
14
- */
15
- has(path) {
16
- return this.isEnabled && this.sync.has(path);
17
- }
18
- /**
19
- * Gets data from the cache, if is exists and the cache is enabled.
20
- */
21
- get(path) {
22
- if (!this.isEnabled)
23
- return;
24
- return this.sync.get(path);
25
- }
26
- /**
27
- * Adds data if the cache is enabled
28
- */
29
- set(path, value) {
30
- if (!this.isEnabled)
31
- return;
32
- this.sync.set(path, value);
33
- this.async.set(path, Promise.resolve(value));
34
- }
35
- /**
36
- * Whether the data exists in the cache
37
- */
38
- hasAsync(path) {
39
- return this.isEnabled && this.async.has(path);
40
- }
41
- /**
42
- * Gets data from the cache, if it exists and the cache is enabled.
43
- */
44
- getAsync(path) {
45
- if (!this.isEnabled)
46
- return;
47
- return this.async.get(path);
48
- }
49
- /**
50
- * Adds data if the cache is enabled
51
- */
52
- setAsync(path, value) {
53
- if (!this.isEnabled)
54
- return;
55
- this.async.set(path, value);
56
- void value.then(v => this.sync.set(path, v));
57
- }
58
- /**
59
- * Clears the cache if it is enabled
60
- */
61
- clear() {
62
- if (!this.isEnabled)
63
- return;
64
- this.sync.clear();
65
- this.async.clear();
66
- }
67
- }
68
- /**
69
- * Used to cache
70
- */
71
- export const stats = new Cache();
72
- /**
73
- * Used to cache realpath lookups
74
- */
75
- export const paths = new Cache();