@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,14 +1,13 @@
1
- import type { Ino } from '../../inode.js';
2
1
  import { SyncTransaction, type Store } from './store.js';
3
2
 
4
3
  /**
5
4
  * An interface for simple synchronous stores that don't have special support for transactions and such.
6
5
  */
7
6
  export interface SimpleSyncStore extends Store {
8
- keys(): Iterable<Ino>;
9
- get(ino: Ino): Uint8Array | undefined;
10
- set(ino: Ino, data: Uint8Array): void;
11
- delete(ino: Ino): void;
7
+ keys(): Iterable<bigint>;
8
+ get(id: bigint): Uint8Array | undefined;
9
+ set(id: bigint, data: Uint8Array): void;
10
+ delete(id: bigint): void;
12
11
  }
13
12
 
14
13
  /**
@@ -18,33 +17,33 @@ export interface SimpleSyncStore extends Store {
18
17
  export abstract class SimpleAsyncStore implements SimpleSyncStore {
19
18
  public abstract name: string;
20
19
 
21
- protected cache: Map<Ino, Uint8Array> = new Map();
20
+ protected cache: Map<bigint, Uint8Array> = new Map();
22
21
 
23
22
  protected queue: Set<Promise<unknown>> = new Set();
24
23
 
25
- protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>;
24
+ protected abstract entries(): Promise<Iterable<[bigint, Uint8Array]>>;
26
25
 
27
- public keys(): Iterable<Ino> {
26
+ public keys(): Iterable<bigint> {
28
27
  return this.cache.keys();
29
28
  }
30
29
 
31
- public get(ino: Ino): Uint8Array | undefined {
32
- return this.cache.get(ino);
30
+ public get(id: bigint): Uint8Array | undefined {
31
+ return this.cache.get(id);
33
32
  }
34
33
 
35
- public set(ino: Ino, data: Uint8Array): void {
36
- this.cache.set(ino, data);
37
- this.queue.add(this._set(ino, data));
34
+ public set(id: bigint, data: Uint8Array): void {
35
+ this.cache.set(id, data);
36
+ this.queue.add(this._set(id, data));
38
37
  }
39
38
 
40
- protected abstract _set(ino: Ino, data: Uint8Array): Promise<void>;
39
+ protected abstract _set(ino: bigint, data: Uint8Array): Promise<void>;
41
40
 
42
- public delete(ino: Ino): void {
43
- this.cache.delete(ino);
44
- this.queue.add(this._delete(ino));
41
+ public delete(id: bigint): void {
42
+ this.cache.delete(id);
43
+ this.queue.add(this._delete(id));
45
44
  }
46
45
 
47
- protected abstract _delete(ino: Ino): Promise<void>;
46
+ protected abstract _delete(ino: bigint): Promise<void>;
48
47
 
49
48
  public clearSync(): void {
50
49
  this.cache.clear();
@@ -79,32 +78,32 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
79
78
  * Stores data in the keys we modify prior to modifying them.
80
79
  * Allows us to roll back commits.
81
80
  */
82
- protected originalData: Map<Ino, Uint8Array | void> = new Map();
81
+ protected originalData: Map<bigint, Uint8Array | void> = new Map();
83
82
  /**
84
83
  * List of keys modified in this transaction, if any.
85
84
  */
86
- protected modifiedKeys: Set<Ino> = new Set();
85
+ protected modifiedKeys: Set<bigint> = new Set();
87
86
 
88
87
  protected declare store: SimpleSyncStore;
89
88
 
90
- public keysSync(): Iterable<Ino> {
89
+ public keysSync(): Iterable<bigint> {
91
90
  return this.store.keys();
92
91
  }
93
92
 
94
- public getSync(ino: Ino): Uint8Array {
95
- const val = this.store.get(ino);
96
- this.stashOldValue(ino, val);
93
+ public getSync(id: bigint): Uint8Array {
94
+ const val = this.store.get(id);
95
+ this.stashOldValue(id, val);
97
96
  return val!;
98
97
  }
99
98
 
100
- public setSync(ino: Ino, data: Uint8Array): void {
101
- this.markModified(ino);
102
- return this.store.set(ino, data);
99
+ public setSync(id: bigint, data: Uint8Array): void {
100
+ this.markModified(id);
101
+ return this.store.set(id, data);
103
102
  }
104
103
 
105
- public removeSync(ino: Ino): void {
106
- this.markModified(ino);
107
- this.store.delete(ino);
104
+ public removeSync(id: bigint): void {
105
+ this.markModified(id);
106
+ this.store.delete(id);
108
107
  }
109
108
 
110
109
  public commitSync(): void {
@@ -135,10 +134,10 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
135
134
  * prevent needless `get` requests if the program modifies the data later
136
135
  * on during the transaction.
137
136
  */
138
- protected stashOldValue(ino: Ino, value?: Uint8Array): void {
137
+ protected stashOldValue(id: bigint, value?: Uint8Array): void {
139
138
  // Keep only the earliest value in the transaction.
140
- if (!this.originalData.has(ino)) {
141
- this.originalData.set(ino, value);
139
+ if (!this.originalData.has(id)) {
140
+ this.originalData.set(id, value);
142
141
  }
143
142
  }
144
143
 
@@ -146,10 +145,10 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
146
145
  * Marks `ino` as modified, and stashes its value if it has not been
147
146
  * stashed already.
148
147
  */
149
- protected markModified(ino: Ino): void {
150
- this.modifiedKeys.add(ino);
151
- if (!this.originalData.has(ino)) {
152
- this.originalData.set(ino, this.store.get(ino));
148
+ protected markModified(id: bigint): void {
149
+ this.modifiedKeys.add(id);
150
+ if (!this.originalData.has(id)) {
151
+ this.originalData.set(id, this.store.get(id));
153
152
  }
154
153
  }
155
154
  }
@@ -1,5 +1,4 @@
1
1
  import { ErrnoError } from '../../error.js';
2
- import type { Ino } from '../../inode.js';
3
2
  import '../../polyfills.js';
4
3
 
5
4
  /**
@@ -46,52 +45,52 @@ export abstract class Transaction<T extends Store = Store> {
46
45
  /**
47
46
  * Gets all of the keys
48
47
  */
49
- public abstract keys(): Promise<Iterable<Ino>>;
48
+ public abstract keys(): Promise<Iterable<bigint>>;
50
49
 
51
50
  /**
52
51
  * Gets all of the keys
53
52
  */
54
- public abstract keysSync(): Iterable<Ino>;
53
+ public abstract keysSync(): Iterable<bigint>;
55
54
 
56
55
  /**
57
- * Retrieves the data at `ino`.
58
- * @param ino The key to look under for data.
56
+ * Retrieves data.
57
+ * @param id The key to look under for data.
59
58
  */
60
- public abstract get(ino: Ino): Promise<Uint8Array>;
59
+ public abstract get(id: bigint): Promise<Uint8Array>;
61
60
 
62
61
  /**
63
- * Retrieves the data at `ino`.
62
+ * Retrieves data.
64
63
  * Throws an error if an error occurs or if the key does not exist.
65
- * @param ino The key to look under for data.
64
+ * @param id The key to look under for data.
66
65
  * @return The data stored under the key, or undefined if not present.
67
66
  */
68
- public abstract getSync(ino: Ino): Uint8Array;
67
+ public abstract getSync(id: bigint): Uint8Array;
69
68
 
70
69
  /**
71
- * Adds the data to the store under `ino`. Overwrites any existing data.
72
- * @param ino The key to add the data under.
70
+ * Adds the data to the store under an id. Overwrites any existing data.
71
+ * @param id The key to add the data under.
73
72
  * @param data The data to add to the store.
74
73
  */
75
- public abstract set(ino: Ino, data: Uint8Array): Promise<void>;
74
+ public abstract set(id: bigint, data: Uint8Array): Promise<void>;
76
75
 
77
76
  /**
78
- * Adds the data to the store under `ino`.
79
- * @param ino The key to add the data under.
77
+ * Adds the data to the store under and id.
78
+ * @param id The key to add the data under.
80
79
  * @param data The data to add to the store.
81
80
  */
82
- public abstract setSync(ino: Ino, data: Uint8Array): void;
81
+ public abstract setSync(id: bigint, data: Uint8Array): void;
83
82
 
84
83
  /**
85
84
  * Deletes the data at `ino`.
86
- * @param ino The key to delete from the store.
85
+ * @param id The key to delete from the store.
87
86
  */
88
- public abstract remove(ino: Ino): Promise<void>;
87
+ public abstract remove(id: bigint): Promise<void>;
89
88
 
90
89
  /**
91
90
  * Deletes the data at `ino`.
92
- * @param ino The key to delete from the store.
91
+ * @param id The key to delete from the store.
93
92
  */
94
- public abstract removeSync(ino: Ino): void;
93
+ public abstract removeSync(id: bigint): void;
95
94
 
96
95
  /**
97
96
  * Commits the transaction.
@@ -135,19 +134,19 @@ export abstract class Transaction<T extends Store = Store> {
135
134
  */
136
135
  export abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
137
136
  /* eslint-disable @typescript-eslint/require-await */
138
- public async keys(): Promise<Iterable<Ino>> {
137
+ public async keys(): Promise<Iterable<bigint>> {
139
138
  return this.keysSync();
140
139
  }
141
- public async get(ino: Ino): Promise<Uint8Array> {
142
- return this.getSync(ino);
140
+ public async get(id: bigint): Promise<Uint8Array> {
141
+ return this.getSync(id);
143
142
  }
144
143
 
145
- public async set(ino: bigint, data: Uint8Array): Promise<void> {
146
- return this.setSync(ino, data);
144
+ public async set(id: bigint, data: Uint8Array): Promise<void> {
145
+ return this.setSync(id, data);
147
146
  }
148
147
 
149
- public async remove(ino: Ino): Promise<void> {
150
- return this.removeSync(ino);
148
+ public async remove(id: bigint): Promise<void> {
149
+ return this.removeSync(id);
151
150
  }
152
151
 
153
152
  public async commit(): Promise<void> {
package/src/config.ts CHANGED
@@ -108,6 +108,15 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
108
108
  */
109
109
  cacheStats: boolean;
110
110
 
111
+ /**
112
+ * If true, enables caching realpath output
113
+ *
114
+ * This can increase performance.
115
+ * @experimental
116
+ * @default false
117
+ */
118
+ cachePaths: boolean;
119
+
111
120
  /**
112
121
  * If true, disables *all* permissions checking.
113
122
  *
@@ -181,7 +190,8 @@ export async function configure<T extends ConfigMounts>(configuration: Partial<C
181
190
 
182
191
  Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
183
192
 
184
- cache.setEnabled(configuration.cacheStats ?? false);
193
+ cache.stats.isEnabled = configuration.cacheStats ?? false;
194
+ cache.paths.isEnabled = configuration.cachePaths ?? false;
185
195
  config.checkAccess = !configuration.disableAccessChecks;
186
196
  config.updateOnRead = !configuration.disableUpdateOnRead;
187
197
  config.syncImmediately = !configuration.onlySyncOnClose;
package/src/devices.ts CHANGED
@@ -11,7 +11,6 @@ import { File } from './file.js';
11
11
  import type { StatsLike } from './stats.js';
12
12
  import { Stats } from './stats.js';
13
13
  import { basename, dirname } from './emulation/path.js';
14
- import type { Ino } from './inode.js';
15
14
 
16
15
  /**
17
16
  * A device
@@ -28,7 +27,7 @@ export interface Device<TData = any> {
28
27
  /**
29
28
  * Which inode the device is assigned
30
29
  */
31
- ino: Ino;
30
+ ino: bigint;
32
31
 
33
32
  /**
34
33
  * Data associated with a device.
@@ -70,7 +69,7 @@ export interface DeviceDriver<TData = any> {
70
69
  * @returns `Device.data`
71
70
  * @experimental
72
71
  */
73
- init?(ino: Ino): {
72
+ init?(ino: bigint): {
74
73
  data?: TData;
75
74
  minor?: number;
76
75
  major?: number;
@@ -3,71 +3,79 @@
3
3
  import type { Stats } from '../stats.js';
4
4
 
5
5
  /**
6
- * Whether the cache is enabled
6
+ * Used for caching data
7
+ * @internal
7
8
  */
8
- export let isEnabled = false;
9
-
10
- /**
11
- * Sets whether the cache is enabled or not
12
- */
13
- export function setEnabled(value: boolean): void {
14
- isEnabled = value;
9
+ export class Cache<T> {
10
+ public isEnabled: boolean = false;
11
+
12
+ protected sync = new Map<string, T>();
13
+
14
+ protected async = new Map<string, Promise<T>>();
15
+
16
+ /**
17
+ * Gets data from the cache, if is exists and the cache is enabled.
18
+ */
19
+ getSync(path: string): T | undefined {
20
+ if (!this.isEnabled) return;
21
+
22
+ return this.sync.get(path);
23
+ }
24
+
25
+ /**
26
+ * Adds data if the cache is enabled
27
+ */
28
+ setSync(path: string, value: T): void {
29
+ if (!this.isEnabled) return;
30
+
31
+ this.sync.set(path, value);
32
+ this.async.set(path, Promise.resolve(value));
33
+ }
34
+
35
+ /**
36
+ * Clears the cache if it is enabled
37
+ */
38
+ clearSync(): void {
39
+ if (!this.isEnabled) return;
40
+
41
+ this.sync.clear();
42
+ }
43
+
44
+ /**
45
+ * Gets data from the cache, if it exists and the cache is enabled.
46
+ */
47
+ get(path: string): Promise<T> | undefined {
48
+ if (!this.isEnabled) return;
49
+
50
+ return this.async.get(path);
51
+ }
52
+
53
+ /**
54
+ * Adds data if the cache is enabled
55
+ */
56
+ set(path: string, value: Promise<T>): void {
57
+ if (!this.isEnabled) return;
58
+
59
+ this.async.set(path, value);
60
+ void value.then(v => this.sync.set(path, v));
61
+ }
62
+
63
+ /**
64
+ * Clears the cache if it is enabled
65
+ */
66
+ clear(): void {
67
+ if (!this.isEnabled) return;
68
+
69
+ this.async.clear();
70
+ }
15
71
  }
16
72
 
17
- const statsSync = new Map<string, Stats>();
18
-
19
73
  /**
20
- * Gets stats from the cache, if they exist and the cache is enabled.
74
+ * Used to cache
21
75
  */
22
- export function getStatsSync(path: string): Stats | undefined {
23
- if (!isEnabled) return;
24
-
25
- return statsSync.get(path);
26
- }
76
+ export const stats = new Cache<Stats>();
27
77
 
28
78
  /**
29
- * Adds stats if the cache is enabled
79
+ * Used to cache realpath lookups
30
80
  */
31
- export function setStatsSync(path: string, value: Stats): void {
32
- if (!isEnabled) return;
33
-
34
- statsSync.set(path, value);
35
- }
36
-
37
- /**
38
- * Clears the cache if it is enabled
39
- */
40
- export function clearStatsSync(): void {
41
- if (!isEnabled) return;
42
-
43
- statsSync.clear();
44
- }
45
-
46
- const stats = new Map<string, Promise<Stats | undefined>>();
47
-
48
- /**
49
- * Gets stats from the cache, if they exist and the cache is enabled.
50
- */
51
- export function getStats(path: string): Promise<Stats | undefined> | undefined {
52
- if (!isEnabled) return;
53
-
54
- return stats.get(path);
55
- }
56
-
57
- /**
58
- * Adds stats if the cache is enabled
59
- */
60
- export function setStats(path: string, value: Promise<Stats | undefined>): void {
61
- if (!isEnabled) return;
62
-
63
- stats.set(path, value);
64
- }
65
-
66
- /**
67
- * Clears the cache if it is enabled
68
- */
69
- export function clearStats(): void {
70
- if (!isEnabled) return;
71
-
72
- stats.clear();
73
- }
81
+ export const paths = new Cache<string>();
@@ -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';
@@ -471,7 +471,7 @@ export async function unlink(path: fs.PathLike): Promise<void> {
471
471
  path = normalizePath(path);
472
472
  const { fs, path: resolved } = resolveMount(path);
473
473
  try {
474
- if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) {
474
+ if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) {
475
475
  throw ErrnoError.With('EACCES', resolved, 'unlink');
476
476
  }
477
477
  await fs.unlink(resolved);
@@ -623,7 +623,7 @@ export async function rmdir(path: fs.PathLike): Promise<void> {
623
623
  path = await realpath(path);
624
624
  const { fs, path: resolved } = resolveMount(path);
625
625
  try {
626
- const stats = await (cache.getStats(path) || fs.stat(resolved));
626
+ const stats = await (cache.stats.get(path) || fs.stat(resolved));
627
627
  if (!stats) {
628
628
  throw ErrnoError.With('ENOENT', path, 'rmdir');
629
629
  }
@@ -718,8 +718,8 @@ export async function readdir(
718
718
 
719
719
  const { fs, path: resolved } = resolveMount(path);
720
720
 
721
- const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
722
- cache.setStats(path, _stats);
721
+ const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
722
+ cache.stats.set(path, _stats);
723
723
  const stats = await _stats;
724
724
 
725
725
  if (!stats) {
@@ -740,8 +740,8 @@ export async function readdir(
740
740
  const addEntry = async (entry: string) => {
741
741
  let entryStats: Stats | undefined;
742
742
  if (options?.recursive || options?.withFileTypes) {
743
- const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
744
- cache.setStats(join(path, entry), _entryStats);
743
+ const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
744
+ cache.stats.set(join(path, entry), _entryStats);
745
745
  entryStats = await _entryStats;
746
746
  }
747
747
  if (options?.withFileTypes) {
@@ -768,7 +768,7 @@ export async function readdir(
768
768
  };
769
769
  await Promise.all(entries.map(addEntry));
770
770
  if (!options?._isIndirect) {
771
- cache.clearStats();
771
+ cache.stats.clear();
772
772
  }
773
773
 
774
774
  return values as string[] | Dirent[];
@@ -892,16 +892,19 @@ export async function realpath(path: fs.PathLike, options?: fs.EncodingOption |
892
892
  export async function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Promise<string | Buffer> {
893
893
  path = normalizePath(path);
894
894
  const { base, dir } = parse(path);
895
- const lpath = join(dir == '/' ? '/' : await realpath(dir), base);
895
+ const lpath = join(dir == '/' ? '/' : await (cache.paths.get(dir) || realpath(dir)), base);
896
896
  const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
897
897
 
898
898
  try {
899
- const stats = await fs.stat(resolvedPath);
900
- if (!stats.isSymbolicLink()) {
899
+ const _stats = cache.stats.get(lpath) || fs.stat(resolvedPath);
900
+ cache.stats.set(lpath, _stats);
901
+ if (!(await _stats).isSymbolicLink()) {
901
902
  return lpath;
902
903
  }
903
904
 
904
- return await realpath(mountPoint + (await readlink(lpath)));
905
+ const target = mountPoint + (await readlink(lpath));
906
+
907
+ return await (cache.paths.get(target) || realpath(target));
905
908
  } catch (e) {
906
909
  if ((e as ErrnoError).code == 'ENOENT') {
907
910
  return path;
@@ -965,20 +968,18 @@ access satisfies typeof promises.access;
965
968
  export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOptions) {
966
969
  path = normalizePath(path);
967
970
 
968
- const _stats =
969
- cache.getStats(path) ||
971
+ const stats = await (cache.stats.get(path) ||
970
972
  stat(path).catch((error: ErrnoError) => {
971
973
  if (error.code == 'ENOENT' && options?.force) return undefined;
972
974
  throw error;
973
- });
974
-
975
- cache.setStats(path, _stats);
976
- const stats = await _stats;
975
+ }));
977
976
 
978
977
  if (!stats) {
979
978
  return;
980
979
  }
981
980
 
981
+ cache.stats.setSync(path, stats);
982
+
982
983
  switch (stats.mode & constants.S_IFMT) {
983
984
  case constants.S_IFDIR:
984
985
  if (options?.recursive) {
@@ -998,12 +999,12 @@ export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOpt
998
999
  case constants.S_IFIFO:
999
1000
  case constants.S_IFSOCK:
1000
1001
  default:
1001
- cache.clearStats();
1002
+ cache.stats.clear();
1002
1003
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
1003
1004
  }
1004
1005
 
1005
1006
  if (!options?._isIndirect) {
1006
- cache.clearStats();
1007
+ cache.stats.clear();
1007
1008
  }
1008
1009
  }
1009
1010
  rm satisfies typeof promises.rm;
@@ -83,6 +83,16 @@ export function resolveMount(path: string): { fs: FileSystem; path: string; moun
83
83
  throw new ErrnoError(Errno.EIO, 'ZenFS not initialized with a file system');
84
84
  }
85
85
 
86
+ /**
87
+ * Wait for all file systems to be ready and synced.
88
+ * May be removed at some point.
89
+ * @experimental @internal
90
+ */
91
+ export async function _synced(): Promise<void> {
92
+ await Promise.all([...mounts.values()].map(m => m.ready()));
93
+ return;
94
+ }
95
+
86
96
  /**
87
97
  * Reverse maps the paths in text from the mounted FileSystem to the global path
88
98
  * @hidden
@@ -102,7 +112,9 @@ export function fixError<E extends ErrnoError>(e: E, paths: Record<string, strin
102
112
  if (typeof e.stack == 'string') {
103
113
  e.stack = fixPaths(e.stack, paths);
104
114
  }
105
- e.message = fixPaths(e.message, paths);
115
+ try {
116
+ e.message = fixPaths(e.message, paths);
117
+ } catch {}
106
118
  return e;
107
119
  }
108
120