@zenfs/core 1.2.1 → 1.2.3

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.
@@ -4,6 +4,7 @@ import { SyncTransaction, type Store } from './store.js';
4
4
  * An interface for simple synchronous stores that don't have special support for transactions and such.
5
5
  */
6
6
  export interface SimpleSyncStore extends Store {
7
+ keys(): Iterable<Ino>;
7
8
  get(ino: Ino): Uint8Array | undefined;
8
9
  set(ino: Ino, data: Uint8Array): void;
9
10
  delete(ino: Ino): void;
@@ -17,6 +18,7 @@ export declare abstract class SimpleAsyncStore implements SimpleSyncStore {
17
18
  protected cache: Map<Ino, Uint8Array>;
18
19
  protected queue: Set<Promise<unknown>>;
19
20
  protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>;
21
+ keys(): Iterable<Ino>;
20
22
  get(ino: Ino): Uint8Array | undefined;
21
23
  set(ino: Ino, data: Uint8Array): void;
22
24
  protected abstract _set(ino: Ino, data: Uint8Array): Promise<void>;
@@ -43,6 +45,7 @@ export declare class SimpleTransaction extends SyncTransaction<SimpleSyncStore>
43
45
  */
44
46
  protected modifiedKeys: Set<Ino>;
45
47
  protected store: SimpleSyncStore;
48
+ keysSync(): Iterable<Ino>;
46
49
  getSync(ino: Ino): Uint8Array;
47
50
  setSync(ino: Ino, data: Uint8Array): void;
48
51
  removeSync(ino: Ino): void;
@@ -8,6 +8,9 @@ export class SimpleAsyncStore {
8
8
  this.cache = new Map();
9
9
  this.queue = new Set();
10
10
  }
11
+ keys() {
12
+ return this.cache.keys();
13
+ }
11
14
  get(ino) {
12
15
  return this.cache.get(ino);
13
16
  }
@@ -55,6 +58,9 @@ export class SimpleTransaction extends SyncTransaction {
55
58
  */
56
59
  this.modifiedKeys = new Set();
57
60
  }
61
+ keysSync() {
62
+ return this.store.keys();
63
+ }
58
64
  getSync(ino) {
59
65
  const val = this.store.get(ino);
60
66
  this.stashOldValue(ino, val);
@@ -32,9 +32,17 @@ export declare abstract class Transaction<T extends Store = Store> {
32
32
  protected store: T;
33
33
  constructor(store: T);
34
34
  /**
35
- * Whether the transaction was commited or aborted
35
+ * Whether the transaction was committed or aborted
36
36
  */
37
37
  protected done: boolean;
38
+ /**
39
+ * Gets all of the keys
40
+ */
41
+ abstract keys(): Promise<Iterable<Ino>>;
42
+ /**
43
+ * Gets all of the keys
44
+ */
45
+ abstract keysSync(): Iterable<Ino>;
38
46
  /**
39
47
  * Retrieves the data at `ino`.
40
48
  * @param ino The key to look under for data.
@@ -92,6 +100,7 @@ export declare abstract class Transaction<T extends Store = Store> {
92
100
  * Transaction that implements asynchronous operations with synchronous ones
93
101
  */
94
102
  export declare abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
103
+ keys(): Promise<Iterable<Ino>>;
95
104
  get(ino: Ino): Promise<Uint8Array>;
96
105
  set(ino: bigint, data: Uint8Array): Promise<void>;
97
106
  remove(ino: Ino): Promise<void>;
@@ -102,9 +111,10 @@ export declare abstract class SyncTransaction<T extends Store = Store> extends T
102
111
  * Transaction that only supports asynchronous operations
103
112
  */
104
113
  export declare abstract class AsyncTransaction<T extends Store = Store> extends Transaction<T> {
105
- getSync(): Uint8Array;
106
- setSync(): void;
107
- removeSync(): void;
108
- commitSync(): void;
109
- abortSync(): void;
114
+ keysSync(): never;
115
+ getSync(): never;
116
+ setSync(): never;
117
+ removeSync(): never;
118
+ commitSync(): never;
119
+ abortSync(): never;
110
120
  }
@@ -7,7 +7,7 @@ export class Transaction {
7
7
  constructor(store) {
8
8
  this.store = store;
9
9
  /**
10
- * Whether the transaction was commited or aborted
10
+ * Whether the transaction was committed or aborted
11
11
  */
12
12
  this.done = false;
13
13
  }
@@ -29,6 +29,9 @@ export class Transaction {
29
29
  */
30
30
  export class SyncTransaction extends Transaction {
31
31
  /* eslint-disable @typescript-eslint/require-await */
32
+ async keys() {
33
+ return this.keysSync();
34
+ }
32
35
  async get(ino) {
33
36
  return this.getSync(ino);
34
37
  }
@@ -49,6 +52,9 @@ export class SyncTransaction extends Transaction {
49
52
  * Transaction that only supports asynchronous operations
50
53
  */
51
54
  export class AsyncTransaction extends Transaction {
55
+ keysSync() {
56
+ throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.keysSync');
57
+ }
52
58
  getSync() {
53
59
  throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.getSync');
54
60
  }
@@ -10,11 +10,23 @@ export declare function setEnabled(value: boolean): void;
10
10
  /**
11
11
  * Gets stats from the cache, if they exist and the cache is enabled.
12
12
  */
13
- export declare function getStats(path: string): Stats | undefined;
13
+ export declare function getStatsSync(path: string): Stats | undefined;
14
14
  /**
15
15
  * Adds stats if the cache is enabled
16
16
  */
17
- export declare function setStats(path: string, value: Stats): void;
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;
18
30
  /**
19
31
  * Clears the cache if it is enabled
20
32
  */
@@ -9,6 +9,31 @@ export let isEnabled = false;
9
9
  export function setEnabled(value) {
10
10
  isEnabled = value;
11
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);
28
+ }
29
+ /**
30
+ * Clears the cache if it is enabled
31
+ */
32
+ export function clearStatsSync() {
33
+ if (!isEnabled)
34
+ return;
35
+ statsSync.clear();
36
+ }
12
37
  const stats = new Map();
13
38
  /**
14
39
  * Gets stats from the cache, if they exist and the cache is enabled.
@@ -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 && !(cache.getStats(path) || (await fs.stat(resolved))).hasAccess(constants.W_OK)) {
438
+ if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
439
439
  throw ErrnoError.With('EACCES', resolved, 'unlink');
440
440
  }
441
441
  await fs.unlink(resolved);
@@ -584,7 +584,10 @@ export async function rmdir(path) {
584
584
  path = await realpath(path);
585
585
  const { fs, path: resolved } = resolveMount(path);
586
586
  try {
587
- const stats = cache.getStats(path) || (await fs.stat(resolved));
587
+ const stats = await (cache.getStats(path) || fs.stat(resolved));
588
+ if (!stats) {
589
+ throw ErrnoError.With('ENOENT', path, 'readdir');
590
+ }
588
591
  if (!stats.isDirectory()) {
589
592
  throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
590
593
  }
@@ -640,8 +643,12 @@ export async function readdir(path, options) {
640
643
  throw fixError(e, { [resolved]: path });
641
644
  };
642
645
  const { fs, path: resolved } = resolveMount(path);
643
- const stats = cache.getStats(path) || (await fs.stat(resolved).catch(handleError));
644
- cache.setStats(path, stats);
646
+ const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
647
+ cache.setStats(path, _stats);
648
+ const stats = await _stats;
649
+ if (!stats) {
650
+ throw ErrnoError.With('ENOENT', path, 'readdir');
651
+ }
645
652
  if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
646
653
  throw ErrnoError.With('EACCES', path, 'readdir');
647
654
  }
@@ -663,8 +670,9 @@ export async function readdir(path, options) {
663
670
  const addEntry = async (entry) => {
664
671
  let entryStats;
665
672
  if (options?.recursive || options?.withFileTypes) {
666
- entryStats = cache.getStats(join(path, entry)) || (await fs.stat(join(resolved, entry)).catch(handleError));
667
- cache.setStats(join(path, entry), entryStats);
673
+ const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
674
+ cache.setStats(join(path, entry), _entryStats);
675
+ entryStats = await _entryStats;
668
676
  }
669
677
  if (options?.withFileTypes) {
670
678
  values.push(new Dirent(entry, entryStats));
@@ -934,15 +942,17 @@ access;
934
942
  */
935
943
  export async function rm(path, options) {
936
944
  path = normalizePath(path);
937
- const stats = cache.getStats(path) ||
938
- (await stat(path).catch((error) => {
939
- if (error.code != 'ENOENT' || !options?.force)
940
- throw error;
941
- }));
945
+ const _stats = cache.getStats(path) ||
946
+ stat(path).catch((error) => {
947
+ if (error.code == 'ENOENT' && options?.force)
948
+ return undefined;
949
+ throw error;
950
+ });
951
+ cache.setStats(path, _stats);
952
+ const stats = await _stats;
942
953
  if (!stats) {
943
954
  return;
944
955
  }
945
- cache.setStats(path, stats);
946
956
  switch (stats.mode & constants.S_IFMT) {
947
957
  case constants.S_IFDIR:
948
958
  if (options?.recursive) {
@@ -147,7 +147,7 @@ export function unlinkSync(path) {
147
147
  path = normalizePath(path);
148
148
  const { fs, path: resolved } = resolveMount(path);
149
149
  try {
150
- if (config.checkAccess && !(cache.getStats(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
150
+ if (config.checkAccess && !(cache.getStatsSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
151
151
  throw ErrnoError.With('EACCES', resolved, 'unlink');
152
152
  }
153
153
  fs.unlinkSync(resolved);
@@ -394,7 +394,7 @@ export function rmdirSync(path) {
394
394
  path = normalizePath(path);
395
395
  const { fs, path: resolved } = resolveMount(realpathSync(path));
396
396
  try {
397
- const stats = cache.getStats(path) || fs.statSync(resolved);
397
+ const stats = cache.getStatsSync(path) || fs.statSync(resolved);
398
398
  if (!stats.isDirectory()) {
399
399
  throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
400
400
  }
@@ -447,8 +447,8 @@ export function readdirSync(path, options) {
447
447
  const { fs, path: resolved } = resolveMount(realpathSync(path));
448
448
  let entries;
449
449
  try {
450
- const stats = cache.getStats(path) || fs.statSync(resolved);
451
- cache.setStats(path, stats);
450
+ const stats = cache.getStatsSync(path) || fs.statSync(resolved);
451
+ cache.setStatsSync(path, stats);
452
452
  if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
453
453
  throw ErrnoError.With('EACCES', resolved, 'readdir');
454
454
  }
@@ -474,8 +474,8 @@ export function readdirSync(path, options) {
474
474
  // Iterate over entries and handle recursive case if needed
475
475
  const values = [];
476
476
  for (const entry of entries) {
477
- const entryStat = cache.getStats(join(path, entry)) || fs.statSync(join(resolved, entry));
478
- cache.setStats(join(path, entry), entryStat);
477
+ const entryStat = cache.getStatsSync(join(path, entry)) || fs.statSync(join(resolved, entry));
478
+ cache.setStatsSync(join(path, entry), entryStat);
479
479
  if (options?.withFileTypes) {
480
480
  values.push(new Dirent(entry, entryStat));
481
481
  }
@@ -501,7 +501,7 @@ export function readdirSync(path, options) {
501
501
  }
502
502
  }
503
503
  if (!options?._isIndirect) {
504
- cache.clearStats();
504
+ cache.clearStatsSync();
505
505
  }
506
506
  return values;
507
507
  }
@@ -638,7 +638,7 @@ export function rmSync(path, options) {
638
638
  path = normalizePath(path);
639
639
  let stats;
640
640
  try {
641
- stats = cache.getStats(path) || statSync(path);
641
+ stats = cache.getStatsSync(path) || statSync(path);
642
642
  }
643
643
  catch (error) {
644
644
  if (error.code != 'ENOENT' || !options?.force)
@@ -647,7 +647,7 @@ export function rmSync(path, options) {
647
647
  if (!stats) {
648
648
  return;
649
649
  }
650
- cache.setStats(path, stats);
650
+ cache.setStatsSync(path, stats);
651
651
  switch (stats.mode & constants.S_IFMT) {
652
652
  case constants.S_IFDIR:
653
653
  if (options?.recursive) {
@@ -666,11 +666,11 @@ export function rmSync(path, options) {
666
666
  case constants.S_IFIFO:
667
667
  case constants.S_IFSOCK:
668
668
  default:
669
- cache.clearStats();
669
+ cache.clearStatsSync();
670
670
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
671
671
  }
672
672
  if (!options?._isIndirect) {
673
- cache.clearStats();
673
+ cache.clearStatsSync();
674
674
  }
675
675
  }
676
676
  rmSync;
@@ -45,6 +45,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
45
45
  var e = new Error(message);
46
46
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
47
47
  });
48
+ import { StoreFS } from '../backends/store/fs.js';
48
49
  import { join } from '../emulation/path.js';
49
50
  import { Errno, ErrnoError } from '../error.js';
50
51
  import { parseFlag, PreloadFile } from '../file.js';
@@ -84,6 +85,18 @@ export function Async(FS) {
84
85
  }
85
86
  this.checkSync();
86
87
  await this._sync.ready();
88
+ // optimization: for 2 storeFS', we copy at a lower abstraction level.
89
+ if (this._sync instanceof StoreFS && this instanceof StoreFS) {
90
+ const sync = this._sync['store'].transaction();
91
+ const async = this['store'].transaction();
92
+ const promises = [];
93
+ for (const key of sync.keysSync()) {
94
+ promises.push(async.set(key, sync.getSync(key)));
95
+ }
96
+ await Promise.all(promises);
97
+ this._isInitialized = true;
98
+ return;
99
+ }
87
100
  try {
88
101
  await this.crossCopy('/');
89
102
  this._isInitialized = true;
@@ -187,10 +200,11 @@ export function Async(FS) {
187
200
  const stats = await this.stat(path);
188
201
  this._sync.mkdirSync(path, stats.mode);
189
202
  }
190
- const files = await this.readdir(path);
191
- for (const file of files) {
192
- await this.crossCopy(join(path, file));
203
+ const promises = [];
204
+ for (const file of await this.readdir(path)) {
205
+ promises.push(this.crossCopy(join(path, file)));
193
206
  }
207
+ await Promise.all(promises);
194
208
  }
195
209
  /**
196
210
  * @internal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -5,6 +5,7 @@ import { SyncTransaction, type Store } from './store.js';
5
5
  * An interface for simple synchronous stores that don't have special support for transactions and such.
6
6
  */
7
7
  export interface SimpleSyncStore extends Store {
8
+ keys(): Iterable<Ino>;
8
9
  get(ino: Ino): Uint8Array | undefined;
9
10
  set(ino: Ino, data: Uint8Array): void;
10
11
  delete(ino: Ino): void;
@@ -23,6 +24,10 @@ export abstract class SimpleAsyncStore implements SimpleSyncStore {
23
24
 
24
25
  protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>;
25
26
 
27
+ public keys(): Iterable<Ino> {
28
+ return this.cache.keys();
29
+ }
30
+
26
31
  public get(ino: Ino): Uint8Array | undefined {
27
32
  return this.cache.get(ino);
28
33
  }
@@ -82,6 +87,10 @@ export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
82
87
 
83
88
  protected declare store: SimpleSyncStore;
84
89
 
90
+ public keysSync(): Iterable<Ino> {
91
+ return this.store.keys();
92
+ }
93
+
85
94
  public getSync(ino: Ino): Uint8Array {
86
95
  const val = this.store.get(ino);
87
96
  this.stashOldValue(ino, val);
@@ -39,10 +39,20 @@ export abstract class Transaction<T extends Store = Store> {
39
39
  public constructor(protected store: T) {}
40
40
 
41
41
  /**
42
- * Whether the transaction was commited or aborted
42
+ * Whether the transaction was committed or aborted
43
43
  */
44
44
  protected done: boolean = false;
45
45
 
46
+ /**
47
+ * Gets all of the keys
48
+ */
49
+ public abstract keys(): Promise<Iterable<Ino>>;
50
+
51
+ /**
52
+ * Gets all of the keys
53
+ */
54
+ public abstract keysSync(): Iterable<Ino>;
55
+
46
56
  /**
47
57
  * Retrieves the data at `ino`.
48
58
  * @param ino The key to look under for data.
@@ -125,6 +135,9 @@ export abstract class Transaction<T extends Store = Store> {
125
135
  */
126
136
  export abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
127
137
  /* eslint-disable @typescript-eslint/require-await */
138
+ public async keys(): Promise<Iterable<Ino>> {
139
+ return this.keysSync();
140
+ }
128
141
  public async get(ino: Ino): Promise<Uint8Array> {
129
142
  return this.getSync(ino);
130
143
  }
@@ -151,23 +164,27 @@ export abstract class SyncTransaction<T extends Store = Store> extends Transacti
151
164
  * Transaction that only supports asynchronous operations
152
165
  */
153
166
  export abstract class AsyncTransaction<T extends Store = Store> extends Transaction<T> {
154
- public getSync(): Uint8Array {
167
+ public keysSync(): never {
168
+ throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.keysSync');
169
+ }
170
+
171
+ public getSync(): never {
155
172
  throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.getSync');
156
173
  }
157
174
 
158
- public setSync(): void {
175
+ public setSync(): never {
159
176
  throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.setSync');
160
177
  }
161
178
 
162
- public removeSync(): void {
179
+ public removeSync(): never {
163
180
  throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.removeSync');
164
181
  }
165
182
 
166
- public commitSync(): void {
183
+ public commitSync(): never {
167
184
  throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.commitSync');
168
185
  }
169
186
 
170
- public abortSync(): void {
187
+ public abortSync(): never {
171
188
  throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.abortSync');
172
189
  }
173
190
  }
@@ -14,12 +14,41 @@ export function setEnabled(value: boolean): void {
14
14
  isEnabled = value;
15
15
  }
16
16
 
17
- const stats = new Map<string, Stats>();
17
+ const statsSync = new Map<string, Stats>();
18
18
 
19
19
  /**
20
20
  * Gets stats from the cache, if they exist and the cache is enabled.
21
21
  */
22
- export function getStats(path: string): Stats | undefined {
22
+ export function getStatsSync(path: string): Stats | undefined {
23
+ if (!isEnabled) return;
24
+
25
+ return statsSync.get(path);
26
+ }
27
+
28
+ /**
29
+ * Adds stats if the cache is enabled
30
+ */
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 {
23
52
  if (!isEnabled) return;
24
53
 
25
54
  return stats.get(path);
@@ -28,7 +57,7 @@ export function getStats(path: string): Stats | undefined {
28
57
  /**
29
58
  * Adds stats if the cache is enabled
30
59
  */
31
- export function setStats(path: string, value: Stats): void {
60
+ export function setStats(path: string, value: Promise<Stats | undefined>): void {
32
61
  if (!isEnabled) return;
33
62
 
34
63
  stats.set(path, value);
@@ -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 && !(cache.getStats(path) || (await fs.stat(resolved))).hasAccess(constants.W_OK)) {
474
+ if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) {
475
475
  throw ErrnoError.With('EACCES', resolved, 'unlink');
476
476
  }
477
477
  await fs.unlink(resolved);
@@ -624,7 +624,10 @@ export async function rmdir(path: fs.PathLike): Promise<void> {
624
624
  path = await realpath(path);
625
625
  const { fs, path: resolved } = resolveMount(path);
626
626
  try {
627
- const stats = cache.getStats(path) || (await fs.stat(resolved));
627
+ const stats = await (cache.getStats(path) || fs.stat(resolved));
628
+ if (!stats) {
629
+ throw ErrnoError.With('ENOENT', path, 'readdir');
630
+ }
628
631
  if (!stats.isDirectory()) {
629
632
  throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
630
633
  }
@@ -716,8 +719,13 @@ export async function readdir(
716
719
 
717
720
  const { fs, path: resolved } = resolveMount(path);
718
721
 
719
- const stats = cache.getStats(path) || (await fs.stat(resolved).catch(handleError));
720
- cache.setStats(path, stats);
722
+ const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError);
723
+ cache.setStats(path, _stats);
724
+ const stats = await _stats;
725
+
726
+ if (!stats) {
727
+ throw ErrnoError.With('ENOENT', path, 'readdir');
728
+ }
721
729
 
722
730
  if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
723
731
  throw ErrnoError.With('EACCES', path, 'readdir');
@@ -744,8 +752,9 @@ export async function readdir(
744
752
  const addEntry = async (entry: string) => {
745
753
  let entryStats: Stats | undefined;
746
754
  if (options?.recursive || options?.withFileTypes) {
747
- entryStats = cache.getStats(join(path, entry)) || (await fs.stat(join(resolved, entry)).catch(handleError));
748
- cache.setStats(join(path, entry), entryStats);
755
+ const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
756
+ cache.setStats(join(path, entry), _entryStats);
757
+ entryStats = await _entryStats;
749
758
  }
750
759
  if (options?.withFileTypes) {
751
760
  values.push(new Dirent(entry, entryStats!));
@@ -968,18 +977,20 @@ access satisfies typeof promises.access;
968
977
  export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOptions) {
969
978
  path = normalizePath(path);
970
979
 
971
- const stats =
980
+ const _stats =
972
981
  cache.getStats(path) ||
973
- (await stat(path).catch((error: ErrnoError) => {
974
- if (error.code != 'ENOENT' || !options?.force) throw error;
975
- }));
982
+ stat(path).catch((error: ErrnoError) => {
983
+ if (error.code == 'ENOENT' && options?.force) return undefined;
984
+ throw error;
985
+ });
986
+
987
+ cache.setStats(path, _stats);
988
+ const stats = await _stats;
976
989
 
977
990
  if (!stats) {
978
991
  return;
979
992
  }
980
993
 
981
- cache.setStats(path, stats);
982
-
983
994
  switch (stats.mode & constants.S_IFMT) {
984
995
  case constants.S_IFDIR:
985
996
  if (options?.recursive) {