@zenfs/core 1.2.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/backends/fetch.js +1 -1
  2. package/dist/backends/memory.d.ts +1 -2
  3. package/dist/backends/overlay.js +2 -3
  4. package/dist/backends/port/fs.d.ts +2 -15
  5. package/dist/backends/store/fs.d.ts +17 -28
  6. package/dist/backends/store/fs.js +169 -191
  7. package/dist/backends/store/simple.d.ts +20 -21
  8. package/dist/backends/store/simple.js +24 -24
  9. package/dist/backends/store/store.d.ts +22 -23
  10. package/dist/backends/store/store.js +6 -6
  11. package/dist/config.d.ts +8 -0
  12. package/dist/config.js +2 -1
  13. package/dist/devices.d.ts +2 -3
  14. package/dist/emulation/cache.d.ts +40 -31
  15. package/dist/emulation/cache.js +62 -53
  16. package/dist/emulation/index.d.ts +1 -1
  17. package/dist/emulation/index.js +1 -1
  18. package/dist/emulation/promises.js +18 -17
  19. package/dist/emulation/shared.d.ts +6 -0
  20. package/dist/emulation/shared.js +13 -1
  21. package/dist/emulation/sync.d.ts +1 -1
  22. package/dist/emulation/sync.js +16 -15
  23. package/dist/file.d.ts +3 -15
  24. package/dist/file.js +7 -19
  25. package/dist/inode.d.ts +4 -13
  26. package/dist/inode.js +22 -29
  27. package/dist/mixins/async.d.ts +15 -10
  28. package/dist/mixins/async.js +3 -1
  29. package/dist/stats.js +30 -5
  30. package/dist/utils.d.ts +5 -7
  31. package/dist/utils.js +11 -20
  32. package/package.json +1 -1
  33. package/src/backends/fetch.ts +1 -1
  34. package/src/backends/memory.ts +1 -2
  35. package/src/backends/overlay.ts +2 -2
  36. package/src/backends/store/fs.ts +187 -220
  37. package/src/backends/store/simple.ts +36 -37
  38. package/src/backends/store/store.ts +25 -26
  39. package/src/config.ts +11 -1
  40. package/src/devices.ts +2 -3
  41. package/src/emulation/cache.ts +68 -60
  42. package/src/emulation/index.ts +1 -1
  43. package/src/emulation/promises.ts +20 -19
  44. package/src/emulation/shared.ts +13 -1
  45. package/src/emulation/sync.ts +16 -15
  46. package/src/file.ts +9 -21
  47. package/src/inode.ts +10 -31
  48. package/src/mixins/async.ts +27 -24
  49. package/src/stats.ts +47 -5
  50. package/src/utils.ts +11 -23
  51. package/tests/fs/dir.test.ts +21 -31
  52. package/tests/fs/directory.test.ts +6 -4
  53. package/tests/fs/links.test.ts +9 -2
  54. package/tests/fs/permissions.test.ts +2 -2
  55. package/tests/fs/stat.test.ts +42 -0
  56. package/tests/fs/times.test.ts +28 -28
  57. package/tests/setup/cow+fetch.ts +4 -2
@@ -1,13 +1,12 @@
1
- import type { Ino } from '../../inode.js';
2
1
  import { SyncTransaction, type Store } from './store.js';
3
2
  /**
4
3
  * An interface for simple synchronous stores that don't have special support for transactions and such.
5
4
  */
6
5
  export interface SimpleSyncStore extends Store {
7
- keys(): Iterable<Ino>;
8
- get(ino: Ino): Uint8Array | undefined;
9
- set(ino: Ino, data: Uint8Array): void;
10
- delete(ino: Ino): void;
6
+ keys(): Iterable<bigint>;
7
+ get(id: bigint): Uint8Array | undefined;
8
+ set(id: bigint, data: Uint8Array): void;
9
+ delete(id: bigint): void;
11
10
  }
12
11
  /**
13
12
  * An interface for simple asynchronous stores that don't have special support for transactions and such.
@@ -15,15 +14,15 @@ export interface SimpleSyncStore extends Store {
15
14
  */
16
15
  export declare abstract class SimpleAsyncStore implements SimpleSyncStore {
17
16
  abstract name: string;
18
- protected cache: Map<Ino, Uint8Array>;
17
+ protected cache: Map<bigint, Uint8Array>;
19
18
  protected queue: Set<Promise<unknown>>;
20
- protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>;
21
- keys(): Iterable<Ino>;
22
- get(ino: Ino): Uint8Array | undefined;
23
- set(ino: Ino, data: Uint8Array): void;
24
- protected abstract _set(ino: Ino, data: Uint8Array): Promise<void>;
25
- delete(ino: Ino): void;
26
- protected abstract _delete(ino: Ino): Promise<void>;
19
+ protected abstract entries(): Promise<Iterable<[bigint, Uint8Array]>>;
20
+ keys(): Iterable<bigint>;
21
+ get(id: bigint): Uint8Array | undefined;
22
+ set(id: bigint, data: Uint8Array): void;
23
+ protected abstract _set(ino: bigint, data: Uint8Array): Promise<void>;
24
+ delete(id: bigint): void;
25
+ protected abstract _delete(ino: bigint): Promise<void>;
27
26
  clearSync(): void;
28
27
  abstract clear(): Promise<void>;
29
28
  sync(): Promise<void>;
@@ -39,16 +38,16 @@ export declare class SimpleTransaction extends SyncTransaction<SimpleSyncStore>
39
38
  * Stores data in the keys we modify prior to modifying them.
40
39
  * Allows us to roll back commits.
41
40
  */
42
- protected originalData: Map<Ino, Uint8Array | void>;
41
+ protected originalData: Map<bigint, Uint8Array | void>;
43
42
  /**
44
43
  * List of keys modified in this transaction, if any.
45
44
  */
46
- protected modifiedKeys: Set<Ino>;
45
+ protected modifiedKeys: Set<bigint>;
47
46
  protected store: SimpleSyncStore;
48
- keysSync(): Iterable<Ino>;
49
- getSync(ino: Ino): Uint8Array;
50
- setSync(ino: Ino, data: Uint8Array): void;
51
- removeSync(ino: Ino): void;
47
+ keysSync(): Iterable<bigint>;
48
+ getSync(id: bigint): Uint8Array;
49
+ setSync(id: bigint, data: Uint8Array): void;
50
+ removeSync(id: bigint): void;
52
51
  commitSync(): void;
53
52
  abortSync(): void;
54
53
  /**
@@ -57,10 +56,10 @@ export declare class SimpleTransaction extends SyncTransaction<SimpleSyncStore>
57
56
  * prevent needless `get` requests if the program modifies the data later
58
57
  * on during the transaction.
59
58
  */
60
- protected stashOldValue(ino: Ino, value?: Uint8Array): void;
59
+ protected stashOldValue(id: bigint, value?: Uint8Array): void;
61
60
  /**
62
61
  * Marks `ino` as modified, and stashes its value if it has not been
63
62
  * stashed already.
64
63
  */
65
- protected markModified(ino: Ino): void;
64
+ protected markModified(id: bigint): void;
66
65
  }
@@ -11,16 +11,16 @@ export class SimpleAsyncStore {
11
11
  keys() {
12
12
  return this.cache.keys();
13
13
  }
14
- get(ino) {
15
- return this.cache.get(ino);
14
+ get(id) {
15
+ return this.cache.get(id);
16
16
  }
17
- set(ino, data) {
18
- this.cache.set(ino, data);
19
- this.queue.add(this._set(ino, data));
17
+ set(id, data) {
18
+ this.cache.set(id, data);
19
+ this.queue.add(this._set(id, data));
20
20
  }
21
- delete(ino) {
22
- this.cache.delete(ino);
23
- this.queue.add(this._delete(ino));
21
+ delete(id) {
22
+ this.cache.delete(id);
23
+ this.queue.add(this._delete(id));
24
24
  }
25
25
  clearSync() {
26
26
  this.cache.clear();
@@ -61,18 +61,18 @@ export class SimpleTransaction extends SyncTransaction {
61
61
  keysSync() {
62
62
  return this.store.keys();
63
63
  }
64
- getSync(ino) {
65
- const val = this.store.get(ino);
66
- this.stashOldValue(ino, val);
64
+ getSync(id) {
65
+ const val = this.store.get(id);
66
+ this.stashOldValue(id, val);
67
67
  return val;
68
68
  }
69
- setSync(ino, data) {
70
- this.markModified(ino);
71
- return this.store.set(ino, data);
69
+ setSync(id, data) {
70
+ this.markModified(id);
71
+ return this.store.set(id, data);
72
72
  }
73
- removeSync(ino) {
74
- this.markModified(ino);
75
- this.store.delete(ino);
73
+ removeSync(id) {
74
+ this.markModified(id);
75
+ this.store.delete(id);
76
76
  }
77
77
  commitSync() {
78
78
  this.done = true;
@@ -101,20 +101,20 @@ export class SimpleTransaction extends SyncTransaction {
101
101
  * prevent needless `get` requests if the program modifies the data later
102
102
  * on during the transaction.
103
103
  */
104
- stashOldValue(ino, value) {
104
+ stashOldValue(id, value) {
105
105
  // Keep only the earliest value in the transaction.
106
- if (!this.originalData.has(ino)) {
107
- this.originalData.set(ino, value);
106
+ if (!this.originalData.has(id)) {
107
+ this.originalData.set(id, value);
108
108
  }
109
109
  }
110
110
  /**
111
111
  * Marks `ino` as modified, and stashes its value if it has not been
112
112
  * stashed already.
113
113
  */
114
- markModified(ino) {
115
- this.modifiedKeys.add(ino);
116
- if (!this.originalData.has(ino)) {
117
- this.originalData.set(ino, this.store.get(ino));
114
+ markModified(id) {
115
+ this.modifiedKeys.add(id);
116
+ if (!this.originalData.has(id)) {
117
+ this.originalData.set(id, this.store.get(id));
118
118
  }
119
119
  }
120
120
  }
@@ -1,4 +1,3 @@
1
- import type { Ino } from '../../inode.js';
2
1
  import '../../polyfills.js';
3
2
  /**
4
3
  * Represents a key-value store.
@@ -38,45 +37,45 @@ export declare abstract class Transaction<T extends Store = Store> {
38
37
  /**
39
38
  * Gets all of the keys
40
39
  */
41
- abstract keys(): Promise<Iterable<Ino>>;
40
+ abstract keys(): Promise<Iterable<bigint>>;
42
41
  /**
43
42
  * Gets all of the keys
44
43
  */
45
- abstract keysSync(): Iterable<Ino>;
44
+ abstract keysSync(): Iterable<bigint>;
46
45
  /**
47
- * Retrieves the data at `ino`.
48
- * @param ino The key to look under for data.
46
+ * Retrieves data.
47
+ * @param id The key to look under for data.
49
48
  */
50
- abstract get(ino: Ino): Promise<Uint8Array>;
49
+ abstract get(id: bigint): Promise<Uint8Array>;
51
50
  /**
52
- * Retrieves the data at `ino`.
51
+ * Retrieves data.
53
52
  * Throws an error if an error occurs or if the key does not exist.
54
- * @param ino The key to look under for data.
53
+ * @param id The key to look under for data.
55
54
  * @return The data stored under the key, or undefined if not present.
56
55
  */
57
- abstract getSync(ino: Ino): Uint8Array;
56
+ abstract getSync(id: bigint): Uint8Array;
58
57
  /**
59
- * Adds the data to the store under `ino`. Overwrites any existing data.
60
- * @param ino The key to add the data under.
58
+ * Adds the data to the store under an id. Overwrites any existing data.
59
+ * @param id The key to add the data under.
61
60
  * @param data The data to add to the store.
62
61
  */
63
- abstract set(ino: Ino, data: Uint8Array): Promise<void>;
62
+ abstract set(id: bigint, data: Uint8Array): Promise<void>;
64
63
  /**
65
- * Adds the data to the store under `ino`.
66
- * @param ino The key to add the data under.
64
+ * Adds the data to the store under and id.
65
+ * @param id The key to add the data under.
67
66
  * @param data The data to add to the store.
68
67
  */
69
- abstract setSync(ino: Ino, data: Uint8Array): void;
68
+ abstract setSync(id: bigint, data: Uint8Array): void;
70
69
  /**
71
70
  * Deletes the data at `ino`.
72
- * @param ino The key to delete from the store.
71
+ * @param id The key to delete from the store.
73
72
  */
74
- abstract remove(ino: Ino): Promise<void>;
73
+ abstract remove(id: bigint): Promise<void>;
75
74
  /**
76
75
  * Deletes the data at `ino`.
77
- * @param ino The key to delete from the store.
76
+ * @param id The key to delete from the store.
78
77
  */
79
- abstract removeSync(ino: Ino): void;
78
+ abstract removeSync(id: bigint): void;
80
79
  /**
81
80
  * Commits the transaction.
82
81
  */
@@ -100,10 +99,10 @@ export declare abstract class Transaction<T extends Store = Store> {
100
99
  * Transaction that implements asynchronous operations with synchronous ones
101
100
  */
102
101
  export declare abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
103
- keys(): Promise<Iterable<Ino>>;
104
- get(ino: Ino): Promise<Uint8Array>;
105
- set(ino: bigint, data: Uint8Array): Promise<void>;
106
- remove(ino: Ino): Promise<void>;
102
+ keys(): Promise<Iterable<bigint>>;
103
+ get(id: bigint): Promise<Uint8Array>;
104
+ set(id: bigint, data: Uint8Array): Promise<void>;
105
+ remove(id: bigint): Promise<void>;
107
106
  commit(): Promise<void>;
108
107
  abort(): Promise<void>;
109
108
  }
@@ -32,14 +32,14 @@ export class SyncTransaction extends Transaction {
32
32
  async keys() {
33
33
  return this.keysSync();
34
34
  }
35
- async get(ino) {
36
- return this.getSync(ino);
35
+ async get(id) {
36
+ return this.getSync(id);
37
37
  }
38
- async set(ino, data) {
39
- return this.setSync(ino, data);
38
+ async set(id, data) {
39
+ return this.setSync(id, data);
40
40
  }
41
- async remove(ino) {
42
- return this.removeSync(ino);
41
+ async remove(id) {
42
+ return this.removeSync(id);
43
43
  }
44
44
  async commit() {
45
45
  return this.commitSync();
package/dist/config.d.ts CHANGED
@@ -45,6 +45,14 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
45
45
  * @default false
46
46
  */
47
47
  cacheStats: boolean;
48
+ /**
49
+ * If true, enables caching realpath output
50
+ *
51
+ * This can increase performance.
52
+ * @experimental
53
+ * @default false
54
+ */
55
+ cachePaths: boolean;
48
56
  /**
49
57
  * If true, disables *all* permissions checking.
50
58
  *
package/dist/config.js CHANGED
@@ -88,7 +88,8 @@ 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
  Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
91
- cache.setEnabled(configuration.cacheStats ?? false);
91
+ cache.stats.isEnabled = configuration.cacheStats ?? false;
92
+ cache.paths.isEnabled = configuration.cachePaths ?? false;
92
93
  config.checkAccess = !configuration.disableAccessChecks;
93
94
  config.updateOnRead = !configuration.disableUpdateOnRead;
94
95
  config.syncImmediately = !configuration.onlySyncOnClose;
package/dist/devices.d.ts CHANGED
@@ -4,7 +4,6 @@ import { StoreFS } from './backends/store/fs.js';
4
4
  import { File } from './file.js';
5
5
  import type { StatsLike } from './stats.js';
6
6
  import { Stats } from './stats.js';
7
- import type { Ino } from './inode.js';
8
7
  /**
9
8
  * A device
10
9
  * @todo Maybe add major/minor number or some other device information, like a UUID?
@@ -19,7 +18,7 @@ export interface Device<TData = any> {
19
18
  /**
20
19
  * Which inode the device is assigned
21
20
  */
22
- ino: Ino;
21
+ ino: bigint;
23
22
  /**
24
23
  * Data associated with a device.
25
24
  * This is meant to be used by device drivers.
@@ -55,7 +54,7 @@ export interface DeviceDriver<TData = any> {
55
54
  * @returns `Device.data`
56
55
  * @experimental
57
56
  */
58
- init?(ino: Ino): {
57
+ init?(ino: bigint): {
59
58
  data?: TData;
60
59
  minor?: number;
61
60
  major?: number;
@@ -1,33 +1,42 @@
1
1
  import type { Stats } from '../stats.js';
2
2
  /**
3
- * Whether the cache is enabled
4
- */
5
- export declare let isEnabled: boolean;
6
- /**
7
- * Sets whether the cache is enabled or not
8
- */
9
- export declare function setEnabled(value: boolean): void;
10
- /**
11
- * Gets stats from the cache, if they exist and the cache is enabled.
12
- */
13
- export declare function getStatsSync(path: string): Stats | undefined;
14
- /**
15
- * Adds stats if the cache is enabled
16
- */
17
- export declare function setStatsSync(path: string, value: Stats): void;
18
- /**
19
- * Clears the cache if it is enabled
20
- */
21
- export declare function clearStatsSync(): void;
22
- /**
23
- * Gets stats from the cache, if they exist and the cache is enabled.
24
- */
25
- export declare function getStats(path: string): Promise<Stats | undefined> | undefined;
26
- /**
27
- * Adds stats if the cache is enabled
28
- */
29
- export declare function setStats(path: string, value: Promise<Stats | undefined>): void;
30
- /**
31
- * Clears the cache if it is enabled
32
- */
33
- export declare function clearStats(): void;
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
+ * Gets data from the cache, if is exists and the cache is enabled.
12
+ */
13
+ getSync(path: string): T | undefined;
14
+ /**
15
+ * Adds data if the cache is enabled
16
+ */
17
+ setSync(path: string, value: T): void;
18
+ /**
19
+ * Clears the cache if it is enabled
20
+ */
21
+ clearSync(): void;
22
+ /**
23
+ * Gets data from the cache, if it exists and the cache is enabled.
24
+ */
25
+ get(path: string): Promise<T> | undefined;
26
+ /**
27
+ * Adds data if the cache is enabled
28
+ */
29
+ set(path: string, value: Promise<T>): void;
30
+ /**
31
+ * Clears the cache if it is enabled
32
+ */
33
+ clear(): void;
34
+ }
35
+ /**
36
+ * Used to cache
37
+ */
38
+ export declare const stats: Cache<Stats>;
39
+ /**
40
+ * Used to cache realpath lookups
41
+ */
42
+ export declare const paths: Cache<string>;
@@ -1,61 +1,70 @@
1
1
  /* Experimental caching */
2
2
  /**
3
- * Whether the cache is enabled
3
+ * Used for caching data
4
+ * @internal
4
5
  */
5
- export let isEnabled = false;
6
- /**
7
- * Sets whether the cache is enabled or not
8
- */
9
- export function setEnabled(value) {
10
- isEnabled = value;
11
- }
12
- const statsSync = new Map();
13
- /**
14
- * Gets stats from the cache, if they exist and the cache is enabled.
15
- */
16
- export function getStatsSync(path) {
17
- if (!isEnabled)
18
- return;
19
- return statsSync.get(path);
20
- }
21
- /**
22
- * Adds stats if the cache is enabled
23
- */
24
- export function setStatsSync(path, value) {
25
- if (!isEnabled)
26
- return;
27
- statsSync.set(path, value);
6
+ export class Cache {
7
+ constructor() {
8
+ this.isEnabled = false;
9
+ this.sync = new Map();
10
+ this.async = new Map();
11
+ }
12
+ /**
13
+ * Gets data from the cache, if is exists and the cache is enabled.
14
+ */
15
+ getSync(path) {
16
+ if (!this.isEnabled)
17
+ return;
18
+ return this.sync.get(path);
19
+ }
20
+ /**
21
+ * Adds data if the cache is enabled
22
+ */
23
+ setSync(path, value) {
24
+ if (!this.isEnabled)
25
+ return;
26
+ this.sync.set(path, value);
27
+ this.async.set(path, Promise.resolve(value));
28
+ }
29
+ /**
30
+ * Clears the cache if it is enabled
31
+ */
32
+ clearSync() {
33
+ if (!this.isEnabled)
34
+ return;
35
+ this.sync.clear();
36
+ }
37
+ /**
38
+ * Gets data from the cache, if it exists and the cache is enabled.
39
+ */
40
+ get(path) {
41
+ if (!this.isEnabled)
42
+ return;
43
+ return this.async.get(path);
44
+ }
45
+ /**
46
+ * Adds data if the cache is enabled
47
+ */
48
+ set(path, value) {
49
+ if (!this.isEnabled)
50
+ return;
51
+ this.async.set(path, value);
52
+ void value.then(v => this.sync.set(path, v));
53
+ }
54
+ /**
55
+ * Clears the cache if it is enabled
56
+ */
57
+ clear() {
58
+ if (!this.isEnabled)
59
+ return;
60
+ this.async.clear();
61
+ }
28
62
  }
29
63
  /**
30
- * Clears the cache if it is enabled
64
+ * Used to cache
31
65
  */
32
- export function clearStatsSync() {
33
- if (!isEnabled)
34
- return;
35
- statsSync.clear();
36
- }
37
- const stats = new Map();
38
- /**
39
- * Gets stats from the cache, if they exist and the cache is enabled.
40
- */
41
- export function getStats(path) {
42
- if (!isEnabled)
43
- return;
44
- return stats.get(path);
45
- }
66
+ export const stats = new Cache();
46
67
  /**
47
- * Adds stats if the cache is enabled
68
+ * Used to cache realpath lookups
48
69
  */
49
- export function setStats(path, value) {
50
- if (!isEnabled)
51
- return;
52
- stats.set(path, value);
53
- }
54
- /**
55
- * Clears the cache if it is enabled
56
- */
57
- export function clearStats() {
58
- if (!isEnabled)
59
- return;
60
- stats.clear();
61
- }
70
+ export const paths = new Cache();
@@ -4,5 +4,5 @@ export * as promises from './promises.js';
4
4
  export * as constants from './constants.js';
5
5
  export * from './streams.js';
6
6
  export * from './dir.js';
7
- export { mountObject, mounts, mount, umount } from './shared.js';
7
+ export { mountObject, mounts, mount, umount, _synced } from './shared.js';
8
8
  export { Stats, StatsFs, BigIntStatsFs } from '../stats.js';
@@ -4,5 +4,5 @@ export * as promises from './promises.js';
4
4
  export * as constants from './constants.js';
5
5
  export * from './streams.js';
6
6
  export * from './dir.js';
7
- export { mountObject, mounts, mount, umount } from './shared.js';
7
+ export { mountObject, mounts, mount, umount, _synced } from './shared.js';
8
8
  export { Stats, StatsFs, BigIntStatsFs } from '../stats.js';
@@ -435,7 +435,7 @@ export async function unlink(path) {
435
435
  path = normalizePath(path);
436
436
  const { fs, path: resolved } = resolveMount(path);
437
437
  try {
438
- if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
438
+ if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
439
439
  throw ErrnoError.With('EACCES', resolved, 'unlink');
440
440
  }
441
441
  await fs.unlink(resolved);
@@ -583,7 +583,7 @@ export async function rmdir(path) {
583
583
  path = await realpath(path);
584
584
  const { fs, path: resolved } = resolveMount(path);
585
585
  try {
586
- const stats = await (cache.getStats(path) || fs.stat(resolved));
586
+ const stats = await (cache.stats.get(path) || fs.stat(resolved));
587
587
  if (!stats) {
588
588
  throw ErrnoError.With('ENOENT', path, 'rmdir');
589
589
  }
@@ -642,8 +642,8 @@ export async function readdir(path, options) {
642
642
  throw fixError(e, { [resolved]: path });
643
643
  };
644
644
  const { fs, path: resolved } = resolveMount(path);
645
- const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
646
- cache.setStats(path, _stats);
645
+ const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
646
+ cache.stats.set(path, _stats);
647
647
  const stats = await _stats;
648
648
  if (!stats) {
649
649
  throw ErrnoError.With('ENOENT', path, 'readdir');
@@ -659,8 +659,8 @@ export async function readdir(path, options) {
659
659
  const addEntry = async (entry) => {
660
660
  let entryStats;
661
661
  if (options?.recursive || options?.withFileTypes) {
662
- const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
663
- cache.setStats(join(path, entry), _entryStats);
662
+ const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
663
+ cache.stats.set(join(path, entry), _entryStats);
664
664
  entryStats = await _entryStats;
665
665
  }
666
666
  if (options?.withFileTypes) {
@@ -690,7 +690,7 @@ export async function readdir(path, options) {
690
690
  };
691
691
  await Promise.all(entries.map(addEntry));
692
692
  if (!options?._isIndirect) {
693
- cache.clearStats();
693
+ cache.stats.clear();
694
694
  }
695
695
  return values;
696
696
  }
@@ -881,14 +881,16 @@ lutimes;
881
881
  export async function realpath(path, options) {
882
882
  path = normalizePath(path);
883
883
  const { base, dir } = parse(path);
884
- const lpath = join(dir == '/' ? '/' : await realpath(dir), base);
884
+ const lpath = join(dir == '/' ? '/' : await (cache.paths.get(dir) || realpath(dir)), base);
885
885
  const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
886
886
  try {
887
- const stats = await fs.stat(resolvedPath);
888
- if (!stats.isSymbolicLink()) {
887
+ const _stats = cache.stats.get(lpath) || fs.stat(resolvedPath);
888
+ cache.stats.set(lpath, _stats);
889
+ if (!(await _stats).isSymbolicLink()) {
889
890
  return lpath;
890
891
  }
891
- return await realpath(mountPoint + (await readlink(lpath)));
892
+ const target = mountPoint + (await readlink(lpath));
893
+ return await (cache.paths.get(target) || realpath(target));
892
894
  }
893
895
  catch (e) {
894
896
  if (e.code == 'ENOENT') {
@@ -943,17 +945,16 @@ access;
943
945
  */
944
946
  export async function rm(path, options) {
945
947
  path = normalizePath(path);
946
- const _stats = cache.getStats(path) ||
948
+ const stats = await (cache.stats.get(path) ||
947
949
  stat(path).catch((error) => {
948
950
  if (error.code == 'ENOENT' && options?.force)
949
951
  return undefined;
950
952
  throw error;
951
- });
952
- cache.setStats(path, _stats);
953
- const stats = await _stats;
953
+ }));
954
954
  if (!stats) {
955
955
  return;
956
956
  }
957
+ cache.stats.setSync(path, stats);
957
958
  switch (stats.mode & constants.S_IFMT) {
958
959
  case constants.S_IFDIR:
959
960
  if (options?.recursive) {
@@ -972,11 +973,11 @@ export async function rm(path, options) {
972
973
  case constants.S_IFIFO:
973
974
  case constants.S_IFSOCK:
974
975
  default:
975
- cache.clearStats();
976
+ cache.stats.clear();
976
977
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
977
978
  }
978
979
  if (!options?._isIndirect) {
979
- cache.clearStats();
980
+ cache.stats.clear();
980
981
  }
981
982
  }
982
983
  rm;
@@ -28,6 +28,12 @@ export declare function resolveMount(path: string): {
28
28
  path: string;
29
29
  mountPoint: string;
30
30
  };
31
+ /**
32
+ * Wait for all file systems to be ready and synced.
33
+ * May be removed at some point.
34
+ * @experimental @internal
35
+ */
36
+ export declare function _synced(): Promise<void>;
31
37
  /**
32
38
  * Reverse maps the paths in text from the mounted FileSystem to the global path
33
39
  * @hidden