@zenfs/core 1.1.2 → 1.1.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.
@@ -141,7 +141,7 @@ export class UnmutexedOverlayFS extends FileSystem {
141
141
  try {
142
142
  await this.writable.rename(oldPath, newPath);
143
143
  }
144
- catch (e) {
144
+ catch {
145
145
  if (this._deletedFiles.has(oldPath)) {
146
146
  throw ErrnoError.With('ENOENT', oldPath, 'rename');
147
147
  }
@@ -155,7 +155,7 @@ export class UnmutexedOverlayFS extends FileSystem {
155
155
  try {
156
156
  this.writable.renameSync(oldPath, newPath);
157
157
  }
158
- catch (e) {
158
+ catch {
159
159
  if (this._deletedFiles.has(oldPath)) {
160
160
  throw ErrnoError.With('ENOENT', oldPath, 'rename');
161
161
  }
@@ -166,7 +166,7 @@ export class UnmutexedOverlayFS extends FileSystem {
166
166
  try {
167
167
  return await this.writable.stat(path);
168
168
  }
169
- catch (e) {
169
+ catch {
170
170
  if (this._deletedFiles.has(path)) {
171
171
  throw ErrnoError.With('ENOENT', path, 'stat');
172
172
  }
@@ -181,7 +181,7 @@ export class UnmutexedOverlayFS extends FileSystem {
181
181
  try {
182
182
  return this.writable.statSync(path);
183
183
  }
184
- catch (e) {
184
+ catch {
185
185
  if (this._deletedFiles.has(path)) {
186
186
  throw ErrnoError.With('ENOENT', path, 'stat');
187
187
  }
@@ -323,13 +323,13 @@ export class UnmutexedOverlayFS extends FileSystem {
323
323
  try {
324
324
  contents.push(...(await this.writable.readdir(path)));
325
325
  }
326
- catch (e) {
326
+ catch {
327
327
  // NOP.
328
328
  }
329
329
  try {
330
330
  contents.push(...(await this.readable.readdir(path)).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
331
331
  }
332
- catch (e) {
332
+ catch {
333
333
  // NOP.
334
334
  }
335
335
  const seenMap = {};
@@ -350,13 +350,13 @@ export class UnmutexedOverlayFS extends FileSystem {
350
350
  try {
351
351
  contents = contents.concat(this.writable.readdirSync(path));
352
352
  }
353
- catch (e) {
353
+ catch {
354
354
  // NOP.
355
355
  }
356
356
  try {
357
357
  contents = contents.concat(this.readable.readdirSync(path).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
358
358
  }
359
- catch (e) {
359
+ catch {
360
360
  // NOP.
361
361
  }
362
362
  const seenMap = {};
@@ -180,7 +180,7 @@ export async function handleRequest(port, fs, request) {
180
180
  };
181
181
  }
182
182
  break;
183
- case 'file':
183
+ case 'file': {
184
184
  const { fd } = request;
185
185
  if (!descriptors.has(fd)) {
186
186
  throw new ErrnoError(Errno.EBADF);
@@ -191,6 +191,7 @@ export async function handleRequest(port, fs, request) {
191
191
  descriptors.delete(fd);
192
192
  }
193
193
  break;
194
+ }
194
195
  default:
195
196
  return;
196
197
  }
@@ -79,7 +79,7 @@ export function catchMessages(port) {
79
79
  return function (fs) {
80
80
  detach(port, handler);
81
81
  for (const event of events) {
82
- const request = 'data' in event ? event.data : event;
82
+ const request = ('data' in event ? event.data : event);
83
83
  void handleRequest(port, fs, request);
84
84
  }
85
85
  };
package/dist/devices.js CHANGED
@@ -105,14 +105,16 @@ export class DeviceFile extends File {
105
105
  closeSync() {
106
106
  this.driver.close?.(this);
107
107
  }
108
- async close() {
108
+ close() {
109
109
  this.closeSync();
110
+ return Promise.resolve();
110
111
  }
111
112
  syncSync() {
112
113
  this.driver.sync?.(this);
113
114
  }
114
- async sync() {
115
+ sync() {
115
116
  this.syncSync();
117
+ return Promise.resolve();
116
118
  }
117
119
  chown() {
118
120
  throw ErrnoError.With('ENOTSUP', this.path, 'chown');
@@ -181,7 +181,7 @@ export declare function link(existing: fs.PathLike, newpath: fs.PathLike, cb?: C
181
181
  */
182
182
  export declare function symlink(target: fs.PathLike, path: fs.PathLike, cb?: Callback): void;
183
183
  export declare function symlink(target: fs.PathLike, path: fs.PathLike, type?: fs.symlink.Type, cb?: Callback): void;
184
- export declare function readlink(path: fs.PathLike, callback: Callback<[string]> & any): void;
184
+ export declare function readlink(path: fs.PathLike, callback: Callback<[string]>): void;
185
185
  export declare function readlink(path: fs.PathLike, options: fs.BufferEncodingOption, callback: Callback<[Uint8Array]>): void;
186
186
  export declare function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string | Uint8Array]>): void;
187
187
  export declare function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string]>): void;
@@ -92,6 +92,9 @@ export declare class FileHandle implements promises.FileHandle {
92
92
  * @experimental
93
93
  */
94
94
  readableWebStream(options?: promises.ReadableWebStreamOptions): TReadableStream<Uint8Array>;
95
+ /**
96
+ * @todo Implement
97
+ */
95
98
  readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface;
96
99
  [Symbol.asyncDispose](): Promise<void>;
97
100
  /**
@@ -296,6 +299,7 @@ export declare function lutimes(path: fs.PathLike, atime: fs.TimeLike, mtime: fs
296
299
  * Asynchronous realpath(3) - return the canonicalized absolute pathname.
297
300
  * @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
298
301
  * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. Defaults to `'utf8'`.
302
+ * @todo handle options
299
303
  */
300
304
  export declare function realpath(path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
301
305
  export declare function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
@@ -206,6 +206,9 @@ export class FileHandle {
206
206
  }
207
207
  return new _gt.ReadableStream({ start, type: options.type });
208
208
  }
209
+ /**
210
+ * @todo Implement
211
+ */
209
212
  readLines(options) {
210
213
  throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
211
214
  }
@@ -383,11 +386,11 @@ export async function exists(path) {
383
386
  }
384
387
  export async function stat(path, options) {
385
388
  path = normalizePath(path);
386
- const { fs, path: resolved } = resolveMount((await exists(path)) ? await realpath(path) : path);
389
+ const { fs, path: resolved } = resolveMount(await realpath(path));
387
390
  try {
388
391
  const stats = await fs.stat(resolved);
389
392
  if (!stats.hasAccess(constants.R_OK)) {
390
- throw ErrnoError.With('EACCES', path, 'stat');
393
+ throw ErrnoError.With('EACCES', resolved, 'stat');
391
394
  }
392
395
  return options?.bigint ? new BigIntStats(stats) : stats;
393
396
  }
@@ -448,7 +451,7 @@ unlink;
448
451
  async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
449
452
  path = normalizePath(path);
450
453
  const mode = normalizeMode(_mode, 0o644), flag = parseFlag(_flag);
451
- path = resolveSymlinks && (await exists(path)) ? await realpath(path) : path;
454
+ path = resolveSymlinks ? await realpath(path) : path;
452
455
  const { fs, path: resolved } = resolveMount(path);
453
456
  const stats = await fs.stat(resolved).catch(() => null);
454
457
  if (!stats) {
@@ -577,7 +580,7 @@ appendFile;
577
580
  // DIRECTORY-ONLY METHODS
578
581
  export async function rmdir(path) {
579
582
  path = normalizePath(path);
580
- path = (await exists(path)) ? await realpath(path) : path;
583
+ path = await realpath(path);
581
584
  const { fs, path: resolved } = resolveMount(path);
582
585
  try {
583
586
  if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
@@ -594,8 +597,7 @@ rmdir;
594
597
  export async function mkdir(path, options) {
595
598
  options = typeof options === 'object' ? options : { mode: options };
596
599
  const mode = normalizeMode(options?.mode, 0o777);
597
- path = normalizePath(path);
598
- path = (await exists(path)) ? await realpath(path) : path;
600
+ path = await realpath(normalizePath(path));
599
601
  const { fs, path: resolved } = resolveMount(path);
600
602
  const errorPaths = { [resolved]: path };
601
603
  try {
@@ -628,12 +630,11 @@ export async function mkdir(path, options) {
628
630
  mkdir;
629
631
  export async function readdir(path, options) {
630
632
  options = typeof options === 'object' ? options : { encoding: options };
631
- path = normalizePath(path);
632
- if (!(await stat(path)).hasAccess(constants.R_OK)) {
633
+ path = await realpath(normalizePath(path));
634
+ const { fs, path: resolved } = resolveMount(path);
635
+ if (!(await fs.stat(resolved)).hasAccess(constants.R_OK)) {
633
636
  throw ErrnoError.With('EACCES', path, 'readdir');
634
637
  }
635
- path = (await exists(path)) ? await realpath(path) : path;
636
- const { fs, path: resolved } = resolveMount(path);
637
638
  const entries = await fs.readdir(resolved).catch((e) => {
638
639
  throw fixError(e, { [resolved]: path });
639
640
  });
@@ -649,8 +650,12 @@ export async function readdir(path, options) {
649
650
  }
650
651
  const values = [];
651
652
  for (const entry of entries) {
652
- const fullPath = join(path, entry);
653
- const stats = options?.recursive || options?.withFileTypes ? await stat(fullPath) : null;
653
+ let stats;
654
+ if (options?.recursive || options?.withFileTypes) {
655
+ stats = await fs.stat(join(resolved, entry)).catch((error) => {
656
+ throw fixError(error, { [resolved]: path });
657
+ });
658
+ }
654
659
  if (options?.withFileTypes) {
655
660
  values.push(new Dirent(entry, stats));
656
661
  }
@@ -663,7 +668,7 @@ export async function readdir(path, options) {
663
668
  if (!options?.recursive || !stats?.isDirectory()) {
664
669
  continue;
665
670
  }
666
- for (const subEntry of await readdir(fullPath, options)) {
671
+ for (const subEntry of await readdir(join(path, entry), options)) {
667
672
  if (subEntry instanceof Dirent) {
668
673
  subEntry.path = join(entry, subEntry.path);
669
674
  values.push(subEntry);
@@ -861,9 +866,12 @@ export async function realpath(path, options) {
861
866
  if (!stats.isSymbolicLink()) {
862
867
  return lpath;
863
868
  }
864
- return realpath(mountPoint + (await readlink(lpath)));
869
+ return await realpath(mountPoint + (await readlink(lpath)));
865
870
  }
866
871
  catch (e) {
872
+ if (e.code == 'ENOENT') {
873
+ return path;
874
+ }
867
875
  throw fixError(e, { [resolvedPath]: lpath });
868
876
  }
869
877
  }
@@ -911,7 +919,13 @@ access;
911
919
  */
912
920
  export async function rm(path, options) {
913
921
  path = normalizePath(path);
914
- const stats = await stat(path);
922
+ const stats = await stat(path).catch((error) => {
923
+ if (error.code != 'ENOENT' || !options?.force)
924
+ throw error;
925
+ });
926
+ if (!stats) {
927
+ return;
928
+ }
915
929
  switch (stats.mode & constants.S_IFMT) {
916
930
  case constants.S_IFDIR:
917
931
  if (options?.recursive) {
@@ -966,9 +980,9 @@ copyFile;
966
980
  * @returns A `Dir` object representing the opened directory.
967
981
  * @todo Use options
968
982
  */
969
- export async function opendir(path, options) {
983
+ export function opendir(path, options) {
970
984
  path = normalizePath(path);
971
- return new Dir(path);
985
+ return Promise.resolve(new Dir(path));
972
986
  }
973
987
  opendir;
974
988
  /**
@@ -1020,8 +1034,8 @@ export async function cp(source, destination, opts) {
1020
1034
  }
1021
1035
  }
1022
1036
  cp;
1023
- export async function statfs(path, opts) {
1037
+ export function statfs(path, opts) {
1024
1038
  path = normalizePath(path);
1025
1039
  const { fs } = resolveMount(path);
1026
- return _statfs(fs, opts?.bigint);
1040
+ return Promise.resolve(_statfs(fs, opts?.bigint));
1027
1041
  }
@@ -1,4 +1,5 @@
1
1
  import type { BigIntStatsFs, StatsFs } from 'node:fs';
2
+ import { ErrnoError } from '../error.js';
2
3
  import type { File } from '../file.js';
3
4
  import type { FileSystem } from '../filesystem.js';
4
5
  import { type AbsolutePath } from './path.js';
@@ -36,7 +37,7 @@ export declare function fixPaths(text: string, paths: Record<string, string>): s
36
37
  * Fix paths in error stacks
37
38
  * @hidden
38
39
  */
39
- export declare function fixError<E extends Error>(e: E, paths: Record<string, string>): E;
40
+ export declare function fixError<E extends ErrnoError>(e: E, paths: Record<string, string>): E;
40
41
  export declare function mountObject(mounts: MountObject): void;
41
42
  /**
42
43
  * @hidden
@@ -199,6 +199,7 @@ export declare function writevSync(fd: number, buffers: readonly ArrayBufferView
199
199
  * @param path The path to the directory.
200
200
  * @param options Options for opening the directory.
201
201
  * @returns A `Dir` object representing the opened directory.
202
+ * @todo Handle options
202
203
  */
203
204
  export declare function opendirSync(path: fs.PathLike, options?: fs.OpenDirOptions): Dir;
204
205
  /**
@@ -97,11 +97,11 @@ export function existsSync(path) {
97
97
  existsSync;
98
98
  export function statSync(path, options) {
99
99
  path = normalizePath(path);
100
- const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
100
+ const { fs, path: resolved } = resolveMount(realpathSync(path));
101
101
  try {
102
102
  const stats = fs.statSync(resolved);
103
103
  if (!stats.hasAccess(constants.R_OK)) {
104
- throw ErrnoError.With('EACCES', path, 'stat');
104
+ throw ErrnoError.With('EACCES', resolved, 'stat');
105
105
  }
106
106
  return options?.bigint ? new BigIntStats(stats) : stats;
107
107
  }
@@ -159,9 +159,16 @@ unlinkSync;
159
159
  function _openSync(path, _flag, _mode, resolveSymlinks = true) {
160
160
  path = normalizePath(path);
161
161
  const mode = normalizeMode(_mode, 0o644), flag = parseFlag(_flag);
162
- path = resolveSymlinks && existsSync(path) ? realpathSync(path) : path;
162
+ path = resolveSymlinks ? realpathSync(path) : path;
163
163
  const { fs, path: resolved } = resolveMount(path);
164
- if (!fs.existsSync(resolved)) {
164
+ let stats;
165
+ try {
166
+ stats = fs.statSync(resolved);
167
+ }
168
+ catch {
169
+ // nothing
170
+ }
171
+ if (!stats) {
165
172
  if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
166
173
  throw ErrnoError.With('ENOENT', path, '_open');
167
174
  }
@@ -175,7 +182,6 @@ function _openSync(path, _flag, _mode, resolveSymlinks = true) {
175
182
  }
176
183
  return fs.createFileSync(resolved, flag, mode);
177
184
  }
178
- const stats = fs.statSync(resolved);
179
185
  if (!stats.hasAccess(mode) || !stats.hasAccess(flagToMode(flag))) {
180
186
  throw ErrnoError.With('EACCES', path, '_open');
181
187
  }
@@ -384,7 +390,7 @@ export function futimesSync(fd, atime, mtime) {
384
390
  futimesSync;
385
391
  export function rmdirSync(path) {
386
392
  path = normalizePath(path);
387
- const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
393
+ const { fs, path: resolved } = resolveMount(realpathSync(path));
388
394
  try {
389
395
  if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
390
396
  throw ErrnoError.With('EACCES', resolved, 'rmdir');
@@ -400,8 +406,7 @@ rmdirSync;
400
406
  export function mkdirSync(path, options) {
401
407
  options = typeof options === 'object' ? options : { mode: options };
402
408
  const mode = normalizeMode(options?.mode, 0o777);
403
- path = normalizePath(path);
404
- path = existsSync(path) ? realpathSync(path) : path;
409
+ path = realpathSync(normalizePath(path));
405
410
  const { fs, path: resolved } = resolveMount(path);
406
411
  const errorPaths = { [resolved]: path };
407
412
  try {
@@ -433,12 +438,12 @@ mkdirSync;
433
438
  export function readdirSync(path, options) {
434
439
  options = typeof options === 'object' ? options : { encoding: options };
435
440
  path = normalizePath(path);
436
- const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
441
+ const { fs, path: resolved } = resolveMount(realpathSync(path));
437
442
  let entries;
438
- if (!statSync(path).hasAccess(constants.R_OK)) {
439
- throw ErrnoError.With('EACCES', path, 'readdir');
440
- }
441
443
  try {
444
+ if (!fs.statSync(resolved).hasAccess(constants.R_OK)) {
445
+ throw ErrnoError.With('EACCES', path, 'readdir');
446
+ }
442
447
  entries = fs.readdirSync(resolved);
443
448
  }
444
449
  catch (e) {
@@ -458,8 +463,7 @@ export function readdirSync(path, options) {
458
463
  // Iterate over entries and handle recursive case if needed
459
464
  const values = [];
460
465
  for (const entry of entries) {
461
- const fullPath = join(path, entry);
462
- const entryStat = statSync(fullPath);
466
+ const entryStat = fs.statSync(join(resolved, entry));
463
467
  if (options?.withFileTypes) {
464
468
  values.push(new Dirent(entry, entryStat));
465
469
  }
@@ -471,7 +475,7 @@ export function readdirSync(path, options) {
471
475
  }
472
476
  if (!entryStat.isDirectory() || !options?.recursive)
473
477
  continue;
474
- for (const subEntry of readdirSync(fullPath, options)) {
478
+ for (const subEntry of readdirSync(join(path, entry), options)) {
475
479
  if (subEntry instanceof Dirent) {
476
480
  subEntry.path = join(entry, subEntry.path);
477
481
  values.push(subEntry);
@@ -596,13 +600,15 @@ export function realpathSync(path, options) {
596
600
  return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
597
601
  }
598
602
  catch (e) {
603
+ if (e.code == 'ENOENT') {
604
+ return path;
605
+ }
599
606
  throw fixError(e, { [resolvedPath]: lpath });
600
607
  }
601
608
  }
602
609
  realpathSync;
603
610
  export function accessSync(path, mode = 0o600) {
604
- const stats = statSync(path);
605
- if (!stats.hasAccess(mode)) {
611
+ if (!statSync(path).hasAccess(mode)) {
606
612
  throw new ErrnoError(Errno.EACCES);
607
613
  }
608
614
  }
@@ -613,7 +619,17 @@ accessSync;
613
619
  */
614
620
  export function rmSync(path, options) {
615
621
  path = normalizePath(path);
616
- const stats = statSync(path);
622
+ let stats;
623
+ try {
624
+ stats = statSync(path);
625
+ }
626
+ catch (error) {
627
+ if (error.code != 'ENOENT' || !options?.force)
628
+ throw error;
629
+ }
630
+ if (!stats) {
631
+ return;
632
+ }
617
633
  switch (stats.mode & constants.S_IFMT) {
618
634
  case constants.S_IFDIR:
619
635
  if (options?.recursive) {
@@ -696,6 +712,7 @@ writevSync;
696
712
  * @param path The path to the directory.
697
713
  * @param options Options for opening the directory.
698
714
  * @returns A `Dir` object representing the opened directory.
715
+ * @todo Handle options
699
716
  */
700
717
  export function opendirSync(path, options) {
701
718
  path = normalizePath(path);
package/dist/error.js CHANGED
@@ -261,7 +261,7 @@ export class ErrnoError extends Error {
261
261
  this.path = path;
262
262
  this.syscall = syscall;
263
263
  this.code = Errno[errno];
264
- this.message = `${this.code}: ${message}${this.path ? `, '${this.path}'` : ''}`;
264
+ this.message = this.code + ': ' + message + (this.path ? `, '${this.path}'` : '');
265
265
  }
266
266
  /**
267
267
  * @returns A friendly error message.
package/dist/utils.d.ts CHANGED
@@ -17,8 +17,6 @@ export declare function mkdirpSync(path: string, mode: number, fs: FileSystem):
17
17
  * @hidden
18
18
  */
19
19
  export declare function levenshtein(a: string, b: string): number;
20
- /** @hidden */
21
- export declare const setImmediate: (callback: () => unknown) => void;
22
20
  /**
23
21
  * Encodes a string into a buffer
24
22
  * @internal
package/dist/utils.js CHANGED
@@ -83,8 +83,6 @@ export function levenshtein(a, b) {
83
83
  }
84
84
  return dd;
85
85
  }
86
- /** @hidden */
87
- export const setImmediate = typeof globalThis.setImmediate == 'function' ? globalThis.setImmediate : (cb) => setTimeout(cb, 0);
88
86
  /**
89
87
  * Encodes a string into a buffer
90
88
  * @internal
package/eslint.shared.js CHANGED
@@ -44,6 +44,7 @@ export default [
44
44
  '@typescript-eslint/no-redundant-type-constituents': 'warn',
45
45
  '@typescript-eslint/no-unsafe-call': 'warn',
46
46
  '@typescript-eslint/restrict-plus-operands': 'off',
47
+ '@typescript-eslint/no-base-to-string': 'off',
47
48
  },
48
49
  },
49
50
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -1,4 +1,4 @@
1
- import type { RequiredKeys } from 'utilium';
1
+ import type { Entries, RequiredKeys } from 'utilium';
2
2
  import { ErrnoError, Errno } from '../error.js';
3
3
  import type { FileSystem } from '../filesystem.js';
4
4
  import { levenshtein } from '../utils.js';
@@ -102,7 +102,7 @@ export async function checkOptions<T extends Backend>(backend: T, options: Recor
102
102
  }
103
103
 
104
104
  // Check for required options.
105
- for (const [optName, opt] of Object.entries(backend.options)) {
105
+ for (const [optName, opt] of Object.entries(backend.options) as Entries<OptionsConfig<Record<string, any>>>) {
106
106
  const providedValue = options?.[optName];
107
107
 
108
108
  if (providedValue === undefined || providedValue === null) {
@@ -133,7 +133,7 @@ export async function checkOptions<T extends Backend>(backend: T, options: Recor
133
133
  throw new ErrnoError(
134
134
  Errno.EINVAL,
135
135
  `${backend.name}: Value provided for option ${optName} is not the proper type. Expected ${
136
- Array.isArray(opt.type) ? `one of {${opt.type.join(', ')}}` : opt.type
136
+ Array.isArray(opt.type) ? `one of {${opt.type.join(', ')}}` : (opt.type as string)
137
137
  }, but received ${typeof providedValue}`
138
138
  );
139
139
  }
@@ -202,7 +202,7 @@ export abstract class IndexFS extends Readonly(FileSystem) {
202
202
  if (!content.every(item => typeof item == 'string')) {
203
203
  throw ErrnoError.With('ENODATA', path, 'readdir');
204
204
  }
205
- return content as string[];
205
+ return content;
206
206
  }
207
207
 
208
208
  protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
@@ -128,7 +128,7 @@ export class UnmutexedOverlayFS extends FileSystem {
128
128
 
129
129
  try {
130
130
  await this.writable.rename(oldPath, newPath);
131
- } catch (e) {
131
+ } catch {
132
132
  if (this._deletedFiles.has(oldPath)) {
133
133
  throw ErrnoError.With('ENOENT', oldPath, 'rename');
134
134
  }
@@ -144,7 +144,7 @@ export class UnmutexedOverlayFS extends FileSystem {
144
144
 
145
145
  try {
146
146
  this.writable.renameSync(oldPath, newPath);
147
- } catch (e) {
147
+ } catch {
148
148
  if (this._deletedFiles.has(oldPath)) {
149
149
  throw ErrnoError.With('ENOENT', oldPath, 'rename');
150
150
  }
@@ -155,7 +155,7 @@ export class UnmutexedOverlayFS extends FileSystem {
155
155
  this.checkInitialized();
156
156
  try {
157
157
  return await this.writable.stat(path);
158
- } catch (e) {
158
+ } catch {
159
159
  if (this._deletedFiles.has(path)) {
160
160
  throw ErrnoError.With('ENOENT', path, 'stat');
161
161
  }
@@ -170,7 +170,7 @@ export class UnmutexedOverlayFS extends FileSystem {
170
170
  this.checkInitialized();
171
171
  try {
172
172
  return this.writable.statSync(path);
173
- } catch (e) {
173
+ } catch {
174
174
  if (this._deletedFiles.has(path)) {
175
175
  throw ErrnoError.With('ENOENT', path, 'stat');
176
176
  }
@@ -329,12 +329,12 @@ export class UnmutexedOverlayFS extends FileSystem {
329
329
  const contents: string[] = [];
330
330
  try {
331
331
  contents.push(...(await this.writable.readdir(path)));
332
- } catch (e) {
332
+ } catch {
333
333
  // NOP.
334
334
  }
335
335
  try {
336
336
  contents.push(...(await this.readable.readdir(path)).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
337
- } catch (e) {
337
+ } catch {
338
338
  // NOP.
339
339
  }
340
340
  const seenMap: { [name: string]: boolean } = {};
@@ -356,12 +356,12 @@ export class UnmutexedOverlayFS extends FileSystem {
356
356
  let contents: string[] = [];
357
357
  try {
358
358
  contents = contents.concat(this.writable.readdirSync(path));
359
- } catch (e) {
359
+ } catch {
360
360
  // NOP.
361
361
  }
362
362
  try {
363
363
  contents = contents.concat(this.readable.readdirSync(path).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
364
- } catch (e) {
364
+ } catch {
365
365
  // NOP.
366
366
  }
367
367
  const seenMap: { [name: string]: boolean } = {};
@@ -260,7 +260,7 @@ export async function handleRequest(port: RPC.Port, fs: FileSystem, request: Fil
260
260
  };
261
261
  }
262
262
  break;
263
- case 'file':
263
+ case 'file': {
264
264
  const { fd } = request;
265
265
  if (!descriptors.has(fd)) {
266
266
  throw new ErrnoError(Errno.EBADF);
@@ -271,11 +271,12 @@ export async function handleRequest(port: RPC.Port, fs: FileSystem, request: Fil
271
271
  descriptors.delete(fd);
272
272
  }
273
273
  break;
274
+ }
274
275
  default:
275
276
  return;
276
277
  }
277
- } catch (e: any) {
278
- value = e instanceof ErrnoError ? e.toJSON() : e.toString();
278
+ } catch (e) {
279
+ value = e instanceof ErrnoError ? e.toJSON() : (e as object).toString();
279
280
  error = true;
280
281
  }
281
282
 
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { Errno, ErrnoError, type ErrnoErrorJSON } from '../../error.js';
3
+ import type { FileSystem } from '../../filesystem.js';
3
4
  import type { Backend, FilesystemOf } from '../backend.js';
4
5
  import { handleRequest, PortFile, type PortFS } from './fs.js';
5
6
  import type { FileOrFSRequest } from './fs.js';
@@ -156,10 +157,10 @@ export function catchMessages<T extends Backend>(port: Port): (fs: FilesystemOf<
156
157
  const events: _MessageEvent[] = [];
157
158
  const handler = events.push.bind(events);
158
159
  attach(port, handler);
159
- return function (fs: any) {
160
+ return function (fs: FileSystem) {
160
161
  detach(port, handler);
161
162
  for (const event of events) {
162
- const request: FileOrFSRequest = 'data' in event ? event.data : event;
163
+ const request = ('data' in event ? event.data : event) as FileOrFSRequest;
163
164
  void handleRequest(port, fs, request);
164
165
  }
165
166
  };
package/src/devices.ts CHANGED
@@ -134,16 +134,18 @@ export class DeviceFile extends File {
134
134
  this.driver.close?.(this);
135
135
  }
136
136
 
137
- public async close(): Promise<void> {
137
+ public close(): Promise<void> {
138
138
  this.closeSync();
139
+ return Promise.resolve();
139
140
  }
140
141
 
141
142
  public syncSync(): void {
142
143
  this.driver.sync?.(this);
143
144
  }
144
145
 
145
- public async sync(): Promise<void> {
146
+ public sync(): Promise<void> {
146
147
  this.syncSync();
148
+ return Promise.resolve();
147
149
  }
148
150
 
149
151
  public chown(): Promise<void> {
@@ -425,7 +425,7 @@ export function symlink(target: fs.PathLike, path: fs.PathLike, typeOrCB?: fs.sy
425
425
  }
426
426
  symlink satisfies Omit<typeof fs.symlink, '__promisify__'>;
427
427
 
428
- export function readlink(path: fs.PathLike, callback: Callback<[string]> & any): void;
428
+ export function readlink(path: fs.PathLike, callback: Callback<[string]>): void;
429
429
  export function readlink(path: fs.PathLike, options: fs.BufferEncodingOption, callback: Callback<[Uint8Array]>): void;
430
430
  export function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string | Uint8Array]>): void;
431
431
  export function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string]>): void;
@@ -202,6 +202,9 @@ export class FileHandle implements promises.FileHandle {
202
202
  return new (_gt as { ReadableStream: new (...args: unknown[]) => TReadableStream<Uint8Array> }).ReadableStream({ start, type: options.type });
203
203
  }
204
204
 
205
+ /**
206
+ * @todo Implement
207
+ */
205
208
  public readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface {
206
209
  throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
207
210
  }
@@ -396,7 +399,7 @@ export async function rename(oldPath: fs.PathLike, newPath: fs.PathLike): Promis
396
399
  await unlink(oldPath);
397
400
  emitChange('rename', oldPath.toString());
398
401
  } catch (e) {
399
- throw fixError(e as Error, { [src.path]: oldPath, [dst.path]: newPath });
402
+ throw fixError(e as ErrnoError, { [src.path]: oldPath, [dst.path]: newPath });
400
403
  }
401
404
  }
402
405
  rename satisfies typeof promises.rename;
@@ -422,15 +425,15 @@ export async function stat(path: fs.PathLike, options?: { bigint?: false }): Pro
422
425
  export async function stat(path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats>;
423
426
  export async function stat(path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats> {
424
427
  path = normalizePath(path);
425
- const { fs, path: resolved } = resolveMount((await exists(path)) ? await realpath(path) : path);
428
+ const { fs, path: resolved } = resolveMount(await realpath(path));
426
429
  try {
427
430
  const stats = await fs.stat(resolved);
428
431
  if (!stats.hasAccess(constants.R_OK)) {
429
- throw ErrnoError.With('EACCES', path, 'stat');
432
+ throw ErrnoError.With('EACCES', resolved, 'stat');
430
433
  }
431
434
  return options?.bigint ? new BigIntStats(stats) : stats;
432
435
  } catch (e) {
433
- throw fixError(e as Error, { [resolved]: path });
436
+ throw fixError(e as ErrnoError, { [resolved]: path });
434
437
  }
435
438
  }
436
439
  stat satisfies typeof promises.stat;
@@ -449,7 +452,7 @@ export async function lstat(path: fs.PathLike, options?: fs.StatOptions): Promis
449
452
  const stats = await fs.stat(resolved);
450
453
  return options?.bigint ? new BigIntStats(stats) : stats;
451
454
  } catch (e) {
452
- throw fixError(e as Error, { [resolved]: path });
455
+ throw fixError(e as ErrnoError, { [resolved]: path });
453
456
  }
454
457
  }
455
458
  lstat satisfies typeof promises.lstat;
@@ -472,7 +475,7 @@ export async function unlink(path: fs.PathLike): Promise<void> {
472
475
  await fs.unlink(resolved);
473
476
  emitChange('rename', path.toString());
474
477
  } catch (e) {
475
- throw fixError(e as Error, { [resolved]: path });
478
+ throw fixError(e as ErrnoError, { [resolved]: path });
476
479
  }
477
480
  }
478
481
  unlink satisfies typeof promises.unlink;
@@ -486,7 +489,7 @@ async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o6
486
489
  const mode = normalizeMode(_mode, 0o644),
487
490
  flag = parseFlag(_flag);
488
491
 
489
- path = resolveSymlinks && (await exists(path)) ? await realpath(path) : path;
492
+ path = resolveSymlinks ? await realpath(path) : path;
490
493
  const { fs, path: resolved } = resolveMount(path);
491
494
 
492
495
  const stats = await fs.stat(resolved).catch(() => null);
@@ -617,7 +620,7 @@ appendFile satisfies typeof promises.appendFile;
617
620
 
618
621
  export async function rmdir(path: fs.PathLike): Promise<void> {
619
622
  path = normalizePath(path);
620
- path = (await exists(path)) ? await realpath(path) : path;
623
+ path = await realpath(path);
621
624
  const { fs, path: resolved } = resolveMount(path);
622
625
  try {
623
626
  if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
@@ -626,7 +629,7 @@ export async function rmdir(path: fs.PathLike): Promise<void> {
626
629
  await fs.rmdir(resolved);
627
630
  emitChange('rename', path.toString());
628
631
  } catch (e) {
629
- throw fixError(e as Error, { [resolved]: path });
632
+ throw fixError(e as ErrnoError, { [resolved]: path });
630
633
  }
631
634
  }
632
635
  rmdir satisfies typeof promises.rmdir;
@@ -644,8 +647,7 @@ export async function mkdir(path: fs.PathLike, options?: fs.Mode | fs.MakeDirect
644
647
  options = typeof options === 'object' ? options : { mode: options };
645
648
  const mode = normalizeMode(options?.mode, 0o777);
646
649
 
647
- path = normalizePath(path);
648
- path = (await exists(path)) ? await realpath(path) : path;
650
+ path = await realpath(normalizePath(path));
649
651
  const { fs, path: resolved } = resolveMount(path);
650
652
  const errorPaths: Record<string, string> = { [resolved]: path };
651
653
 
@@ -673,7 +675,7 @@ export async function mkdir(path: fs.PathLike, options?: fs.Mode | fs.MakeDirect
673
675
  }
674
676
  return dirs[0];
675
677
  } catch (e) {
676
- throw fixError(e as Error, errorPaths);
678
+ throw fixError(e as ErrnoError, errorPaths);
677
679
  }
678
680
  }
679
681
  mkdir satisfies typeof promises.mkdir;
@@ -699,17 +701,16 @@ export async function readdir(
699
701
  options?: { withFileTypes?: boolean; recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null } | BufferEncoding | 'buffer' | null
700
702
  ): Promise<string[] | Dirent[] | Buffer[]> {
701
703
  options = typeof options === 'object' ? options : { encoding: options };
702
- path = normalizePath(path);
704
+ path = await realpath(normalizePath(path));
703
705
 
704
- if (!(await stat(path)).hasAccess(constants.R_OK)) {
706
+ const { fs, path: resolved } = resolveMount(path);
707
+
708
+ if (!(await fs.stat(resolved)).hasAccess(constants.R_OK)) {
705
709
  throw ErrnoError.With('EACCES', path, 'readdir');
706
710
  }
707
711
 
708
- path = (await exists(path)) ? await realpath(path) : path;
709
- const { fs, path: resolved } = resolveMount(path);
710
-
711
- const entries = await fs.readdir(resolved).catch((e: Error) => {
712
- throw fixError(e as Error, { [resolved]: path });
712
+ const entries = await fs.readdir(resolved).catch((e: ErrnoError) => {
713
+ throw fixError(e, { [resolved]: path });
713
714
  });
714
715
 
715
716
  for (const point of mounts.keys()) {
@@ -725,9 +726,12 @@ export async function readdir(
725
726
 
726
727
  const values: (string | Dirent | Buffer)[] = [];
727
728
  for (const entry of entries) {
728
- const fullPath = join(path, entry);
729
-
730
- const stats = options?.recursive || options?.withFileTypes ? await stat(fullPath) : null;
729
+ let stats: Stats | undefined | void;
730
+ if (options?.recursive || options?.withFileTypes) {
731
+ stats = await fs.stat(join(resolved, entry)).catch((error: ErrnoError) => {
732
+ throw fixError(error, { [resolved]: path });
733
+ });
734
+ }
731
735
  if (options?.withFileTypes) {
732
736
  values.push(new Dirent(entry, stats!));
733
737
  } else if (options?.encoding === 'buffer') {
@@ -740,7 +744,7 @@ export async function readdir(
740
744
  continue;
741
745
  }
742
746
 
743
- for (const subEntry of await readdir(fullPath, options)) {
747
+ for (const subEntry of await readdir(join(path, entry), options)) {
744
748
  if (subEntry instanceof Dirent) {
745
749
  subEntry.path = join(entry, subEntry.path);
746
750
  values.push(subEntry);
@@ -778,7 +782,7 @@ export async function link(targetPath: fs.PathLike, linkPath: fs.PathLike): Prom
778
782
  }
779
783
  return await fs.link(path, link.path);
780
784
  } catch (e) {
781
- throw fixError(e as Error, { [link.path]: linkPath, [path]: targetPath });
785
+ throw fixError(e as ErrnoError, { [link.path]: linkPath, [path]: targetPath });
782
786
  }
783
787
  }
784
788
  link satisfies typeof promises.link;
@@ -863,6 +867,7 @@ lutimes satisfies typeof promises.lutimes;
863
867
  * Asynchronous realpath(3) - return the canonicalized absolute pathname.
864
868
  * @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
865
869
  * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. Defaults to `'utf8'`.
870
+ * @todo handle options
866
871
  */
867
872
  export async function realpath(path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
868
873
  export async function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
@@ -878,9 +883,12 @@ export async function realpath(path: fs.PathLike, options?: fs.EncodingOption |
878
883
  return lpath;
879
884
  }
880
885
 
881
- return realpath(mountPoint + (await readlink(lpath)));
886
+ return await realpath(mountPoint + (await readlink(lpath)));
882
887
  } catch (e) {
883
- throw fixError(e as Error, { [resolvedPath]: lpath });
888
+ if ((e as ErrnoError).code == 'ENOENT') {
889
+ return path;
890
+ }
891
+ throw fixError(e as ErrnoError, { [resolvedPath]: lpath });
884
892
  }
885
893
  }
886
894
  realpath satisfies typeof promises.realpath;
@@ -938,7 +946,13 @@ access satisfies typeof promises.access;
938
946
  export async function rm(path: fs.PathLike, options?: fs.RmOptions) {
939
947
  path = normalizePath(path);
940
948
 
941
- const stats = await stat(path);
949
+ const stats = await stat(path).catch((error: ErrnoError) => {
950
+ if (error.code != 'ENOENT' || !options?.force) throw error;
951
+ });
952
+
953
+ if (!stats) {
954
+ return;
955
+ }
942
956
 
943
957
  switch (stats.mode & constants.S_IFMT) {
944
958
  case constants.S_IFDIR:
@@ -1010,9 +1024,9 @@ copyFile satisfies typeof promises.copyFile;
1010
1024
  * @returns A `Dir` object representing the opened directory.
1011
1025
  * @todo Use options
1012
1026
  */
1013
- export async function opendir(path: fs.PathLike, options?: fs.OpenDirOptions): Promise<Dir> {
1027
+ export function opendir(path: fs.PathLike, options?: fs.OpenDirOptions): Promise<Dir> {
1014
1028
  path = normalizePath(path);
1015
- return new Dir(path);
1029
+ return Promise.resolve(new Dir(path));
1016
1030
  }
1017
1031
  opendir satisfies typeof promises.opendir;
1018
1032
 
@@ -1077,8 +1091,8 @@ cp satisfies typeof promises.cp;
1077
1091
  export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions & { bigint?: false }): Promise<fs.StatsFs>;
1078
1092
  export async function statfs(path: fs.PathLike, opts: fs.StatFsOptions & { bigint: true }): Promise<fs.BigIntStatsFs>;
1079
1093
  export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs>;
1080
- export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs> {
1094
+ export function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs> {
1081
1095
  path = normalizePath(path);
1082
1096
  const { fs } = resolveMount(path);
1083
- return _statfs(fs, opts?.bigint);
1097
+ return Promise.resolve(_statfs(fs, opts?.bigint));
1084
1098
  }
@@ -99,7 +99,7 @@ export function fixPaths(text: string, paths: Record<string, string>): string {
99
99
  * Fix paths in error stacks
100
100
  * @hidden
101
101
  */
102
- export function fixError<E extends Error>(e: E, paths: Record<string, string>): E {
102
+ export function fixError<E extends ErrnoError>(e: E, paths: Record<string, string>): E {
103
103
  if (typeof e.stack == 'string') {
104
104
  e.stack = fixPaths(e.stack, paths);
105
105
  }
@@ -31,7 +31,7 @@ export function renameSync(oldPath: fs.PathLike, newPath: fs.PathLike): void {
31
31
  unlinkSync(oldPath);
32
32
  emitChange('rename', oldPath.toString());
33
33
  } catch (e) {
34
- throw fixError(e as Error, { [oldMount.path]: oldPath, [newMount.path]: newPath });
34
+ throw fixError(e as ErrnoError, { [oldMount.path]: oldPath, [newMount.path]: newPath });
35
35
  }
36
36
  }
37
37
  renameSync satisfies typeof fs.renameSync;
@@ -58,15 +58,15 @@ export function statSync(path: fs.PathLike, options?: { bigint?: boolean }): Sta
58
58
  export function statSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
59
59
  export function statSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
60
60
  path = normalizePath(path);
61
- const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
61
+ const { fs, path: resolved } = resolveMount(realpathSync(path));
62
62
  try {
63
63
  const stats = fs.statSync(resolved);
64
64
  if (!stats.hasAccess(constants.R_OK)) {
65
- throw ErrnoError.With('EACCES', path, 'stat');
65
+ throw ErrnoError.With('EACCES', resolved, 'stat');
66
66
  }
67
67
  return options?.bigint ? new BigIntStats(stats) : stats;
68
68
  } catch (e) {
69
- throw fixError(e as Error, { [resolved]: path });
69
+ throw fixError(e as ErrnoError, { [resolved]: path });
70
70
  }
71
71
  }
72
72
  statSync satisfies typeof fs.statSync;
@@ -85,7 +85,7 @@ export function lstatSync(path: fs.PathLike, options?: fs.StatOptions): Stats |
85
85
  const stats = fs.statSync(resolved);
86
86
  return options?.bigint ? new BigIntStats(stats) : stats;
87
87
  } catch (e) {
88
- throw fixError(e as Error, { [resolved]: path });
88
+ throw fixError(e as ErrnoError, { [resolved]: path });
89
89
  }
90
90
  }
91
91
  lstatSync satisfies typeof fs.lstatSync;
@@ -110,7 +110,7 @@ export function unlinkSync(path: fs.PathLike): void {
110
110
  fs.unlinkSync(resolved);
111
111
  emitChange('rename', path.toString());
112
112
  } catch (e) {
113
- throw fixError(e as Error, { [resolved]: path });
113
+ throw fixError(e as ErrnoError, { [resolved]: path });
114
114
  }
115
115
  }
116
116
  unlinkSync satisfies typeof fs.unlinkSync;
@@ -120,10 +120,17 @@ function _openSync(path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null
120
120
  const mode = normalizeMode(_mode, 0o644),
121
121
  flag = parseFlag(_flag);
122
122
 
123
- path = resolveSymlinks && existsSync(path) ? realpathSync(path) : path;
123
+ path = resolveSymlinks ? realpathSync(path) : path;
124
124
  const { fs, path: resolved } = resolveMount(path);
125
125
 
126
- if (!fs.existsSync(resolved)) {
126
+ let stats: Stats | undefined;
127
+ try {
128
+ stats = fs.statSync(resolved);
129
+ } catch {
130
+ // nothing
131
+ }
132
+
133
+ if (!stats) {
127
134
  if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
128
135
  throw ErrnoError.With('ENOENT', path, '_open');
129
136
  }
@@ -138,8 +145,6 @@ function _openSync(path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null
138
145
  return fs.createFileSync(resolved, flag, mode);
139
146
  }
140
147
 
141
- const stats: Stats = fs.statSync(resolved);
142
-
143
148
  if (!stats.hasAccess(mode) || !stats.hasAccess(flagToMode(flag))) {
144
149
  throw ErrnoError.With('EACCES', path, '_open');
145
150
  }
@@ -378,7 +383,7 @@ futimesSync satisfies typeof fs.futimesSync;
378
383
 
379
384
  export function rmdirSync(path: fs.PathLike): void {
380
385
  path = normalizePath(path);
381
- const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
386
+ const { fs, path: resolved } = resolveMount(realpathSync(path));
382
387
  try {
383
388
  if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
384
389
  throw ErrnoError.With('EACCES', resolved, 'rmdir');
@@ -386,7 +391,7 @@ export function rmdirSync(path: fs.PathLike): void {
386
391
  fs.rmdirSync(resolved);
387
392
  emitChange('rename', path.toString());
388
393
  } catch (e) {
389
- throw fixError(e as Error, { [resolved]: path });
394
+ throw fixError(e as ErrnoError, { [resolved]: path });
390
395
  }
391
396
  }
392
397
  rmdirSync satisfies typeof fs.rmdirSync;
@@ -401,8 +406,7 @@ export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirector
401
406
  options = typeof options === 'object' ? options : { mode: options };
402
407
  const mode = normalizeMode(options?.mode, 0o777);
403
408
 
404
- path = normalizePath(path);
405
- path = existsSync(path) ? realpathSync(path) : path;
409
+ path = realpathSync(normalizePath(path));
406
410
  const { fs, path: resolved } = resolveMount(path);
407
411
  const errorPaths: Record<string, string> = { [resolved]: path };
408
412
 
@@ -428,7 +432,7 @@ export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirector
428
432
  }
429
433
  return dirs[0];
430
434
  } catch (e) {
431
- throw fixError(e as Error, errorPaths);
435
+ throw fixError(e as ErrnoError, errorPaths);
432
436
  }
433
437
  }
434
438
  mkdirSync satisfies typeof fs.mkdirSync;
@@ -447,15 +451,15 @@ export function readdirSync(
447
451
  ): string[] | Dirent[] | Buffer[] {
448
452
  options = typeof options === 'object' ? options : { encoding: options };
449
453
  path = normalizePath(path);
450
- const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
454
+ const { fs, path: resolved } = resolveMount(realpathSync(path));
451
455
  let entries: string[];
452
- if (!statSync(path).hasAccess(constants.R_OK)) {
453
- throw ErrnoError.With('EACCES', path, 'readdir');
454
- }
455
456
  try {
457
+ if (!fs.statSync(resolved).hasAccess(constants.R_OK)) {
458
+ throw ErrnoError.With('EACCES', path, 'readdir');
459
+ }
456
460
  entries = fs.readdirSync(resolved);
457
461
  } catch (e) {
458
- throw fixError(e as Error, { [resolved]: path });
462
+ throw fixError(e as ErrnoError, { [resolved]: path });
459
463
  }
460
464
 
461
465
  for (const mount of mounts.keys()) {
@@ -473,8 +477,7 @@ export function readdirSync(
473
477
  // Iterate over entries and handle recursive case if needed
474
478
  const values: (string | Dirent | Buffer)[] = [];
475
479
  for (const entry of entries) {
476
- const fullPath = join(path, entry);
477
- const entryStat = statSync(fullPath);
480
+ const entryStat = fs.statSync(join(resolved, entry));
478
481
 
479
482
  if (options?.withFileTypes) {
480
483
  values.push(new Dirent(entry, entryStat));
@@ -485,7 +488,7 @@ export function readdirSync(
485
488
  }
486
489
  if (!entryStat.isDirectory() || !options?.recursive) continue;
487
490
 
488
- for (const subEntry of readdirSync(fullPath, options)) {
491
+ for (const subEntry of readdirSync(join(path, entry), options)) {
489
492
  if (subEntry instanceof Dirent) {
490
493
  subEntry.path = join(entry, subEntry.path);
491
494
  values.push(subEntry);
@@ -524,7 +527,7 @@ export function linkSync(targetPath: fs.PathLike, linkPath: fs.PathLike): void {
524
527
  }
525
528
  return fs.linkSync(path, linkPath);
526
529
  } catch (e) {
527
- throw fixError(e as Error, { [path]: targetPath, [link.path]: linkPath });
530
+ throw fixError(e as ErrnoError, { [path]: targetPath, [link.path]: linkPath });
528
531
  }
529
532
  }
530
533
  linkSync satisfies typeof fs.linkSync;
@@ -628,14 +631,16 @@ export function realpathSync(path: fs.PathLike, options?: fs.EncodingOption | fs
628
631
 
629
632
  return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
630
633
  } catch (e) {
631
- throw fixError(e as Error, { [resolvedPath]: lpath });
634
+ if ((e as ErrnoError).code == 'ENOENT') {
635
+ return path;
636
+ }
637
+ throw fixError(e as ErrnoError, { [resolvedPath]: lpath });
632
638
  }
633
639
  }
634
640
  realpathSync satisfies Omit<typeof fs.realpathSync, 'native'>;
635
641
 
636
642
  export function accessSync(path: fs.PathLike, mode: number = 0o600): void {
637
- const stats = statSync(path);
638
- if (!stats.hasAccess(mode)) {
643
+ if (!statSync(path).hasAccess(mode)) {
639
644
  throw new ErrnoError(Errno.EACCES);
640
645
  }
641
646
  }
@@ -648,7 +653,16 @@ accessSync satisfies typeof fs.accessSync;
648
653
  export function rmSync(path: fs.PathLike, options?: fs.RmOptions): void {
649
654
  path = normalizePath(path);
650
655
 
651
- const stats = statSync(path);
656
+ let stats: Stats | undefined;
657
+ try {
658
+ stats = statSync(path);
659
+ } catch (error) {
660
+ if ((error as ErrnoError).code != 'ENOENT' || !options?.force) throw error;
661
+ }
662
+
663
+ if (!stats) {
664
+ return;
665
+ }
652
666
 
653
667
  switch (stats.mode & constants.S_IFMT) {
654
668
  case constants.S_IFDIR:
@@ -754,6 +768,7 @@ writevSync satisfies typeof fs.writevSync;
754
768
  * @param path The path to the directory.
755
769
  * @param options Options for opening the directory.
756
770
  * @returns A `Dir` object representing the opened directory.
771
+ * @todo Handle options
757
772
  */
758
773
  export function opendirSync(path: fs.PathLike, options?: fs.OpenDirOptions): Dir {
759
774
  path = normalizePath(path);
package/src/error.ts CHANGED
@@ -276,7 +276,7 @@ export class ErrnoError extends Error implements NodeJS.ErrnoException {
276
276
  ) {
277
277
  super(message);
278
278
  this.code = Errno[errno] as keyof typeof Errno;
279
- this.message = `${this.code}: ${message}${this.path ? `, '${this.path}'` : ''}`;
279
+ this.message = this.code + ': ' + message + (this.path ? `, '${this.path}'` : '');
280
280
  }
281
281
 
282
282
  /**
package/src/utils.ts CHANGED
@@ -10,10 +10,6 @@ declare global {
10
10
  function btoa(data: string): string;
11
11
  }
12
12
 
13
- declare const globalThis: {
14
- setImmediate?: (callback: () => unknown) => void;
15
- };
16
-
17
13
  /**
18
14
  * Synchronous recursive makedir.
19
15
  * @hidden
@@ -112,9 +108,6 @@ export function levenshtein(a: string, b: string): number {
112
108
  return dd;
113
109
  }
114
110
 
115
- /** @hidden */
116
- export const setImmediate = typeof globalThis.setImmediate == 'function' ? globalThis.setImmediate : (cb: () => unknown) => setTimeout(cb, 0);
117
-
118
111
  /**
119
112
  * Encodes a string into a buffer
120
113
  * @internal