@zenfs/core 2.0.0 → 2.1.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 (87) hide show
  1. package/dist/backends/backend.js +6 -5
  2. package/dist/backends/cow.d.ts +2 -2
  3. package/dist/backends/cow.js +39 -58
  4. package/dist/backends/fetch.js +27 -29
  5. package/dist/backends/passthrough.d.ts +2 -3
  6. package/dist/backends/passthrough.js +84 -199
  7. package/dist/backends/port.d.ts +16 -3
  8. package/dist/backends/port.js +61 -30
  9. package/dist/backends/single_buffer.d.ts +52 -46
  10. package/dist/backends/single_buffer.js +462 -219
  11. package/dist/backends/store/fs.d.ts +16 -10
  12. package/dist/backends/store/fs.js +227 -242
  13. package/dist/backends/store/store.d.ts +3 -3
  14. package/dist/backends/store/store.js +11 -10
  15. package/dist/config.d.ts +2 -2
  16. package/dist/config.js +10 -11
  17. package/dist/internal/devices.d.ts +2 -2
  18. package/dist/internal/devices.js +39 -49
  19. package/dist/internal/error.d.ts +9 -204
  20. package/dist/internal/error.js +19 -288
  21. package/dist/internal/file_index.d.ts +1 -1
  22. package/dist/internal/file_index.js +9 -9
  23. package/dist/internal/filesystem.d.ts +23 -8
  24. package/dist/internal/index.d.ts +1 -1
  25. package/dist/internal/index.js +1 -1
  26. package/dist/internal/index_fs.d.ts +2 -2
  27. package/dist/internal/index_fs.js +19 -19
  28. package/dist/internal/inode.d.ts +81 -103
  29. package/dist/internal/inode.js +336 -195
  30. package/dist/mixins/async.js +32 -28
  31. package/dist/mixins/mutexed.d.ts +4 -4
  32. package/dist/mixins/mutexed.js +39 -39
  33. package/dist/mixins/readonly.d.ts +2 -2
  34. package/dist/mixins/readonly.js +20 -20
  35. package/dist/mixins/sync.js +2 -2
  36. package/dist/polyfills.js +1 -1
  37. package/dist/readline.js +1 -1
  38. package/dist/utils.d.ts +8 -5
  39. package/dist/utils.js +14 -17
  40. package/dist/vfs/acl.d.ts +8 -8
  41. package/dist/vfs/acl.js +66 -47
  42. package/dist/vfs/async.d.ts +2 -2
  43. package/dist/vfs/async.js +6 -8
  44. package/dist/vfs/dir.d.ts +1 -1
  45. package/dist/vfs/dir.js +3 -4
  46. package/dist/vfs/file.js +33 -24
  47. package/dist/vfs/flags.js +3 -3
  48. package/dist/vfs/ioctl.d.ts +8 -7
  49. package/dist/vfs/ioctl.js +132 -27
  50. package/dist/vfs/promises.d.ts +3 -3
  51. package/dist/vfs/promises.js +200 -235
  52. package/dist/vfs/shared.d.ts +1 -12
  53. package/dist/vfs/shared.js +7 -35
  54. package/dist/vfs/streams.js +9 -9
  55. package/dist/vfs/sync.d.ts +1 -2
  56. package/dist/vfs/sync.js +158 -170
  57. package/dist/vfs/watchers.js +8 -8
  58. package/dist/vfs/xattr.js +89 -106
  59. package/package.json +4 -2
  60. package/scripts/test.js +2 -2
  61. package/tests/assignment.ts +1 -1
  62. package/tests/backend/port.test.ts +4 -4
  63. package/tests/backend/single-buffer.test.ts +39 -10
  64. package/tests/backend/single-buffer.worker.js +30 -0
  65. package/tests/common/context.test.ts +2 -2
  66. package/tests/common/mutex.test.ts +9 -9
  67. package/tests/fetch/fetch.ts +1 -1
  68. package/tests/fs/append.test.ts +4 -4
  69. package/tests/fs/directory.test.ts +25 -25
  70. package/tests/fs/errors.test.ts +15 -19
  71. package/tests/fs/links.test.ts +3 -2
  72. package/tests/fs/open.test.ts +4 -21
  73. package/tests/fs/permissions.test.ts +8 -13
  74. package/tests/fs/read.test.ts +10 -9
  75. package/tests/fs/readFile.test.ts +8 -24
  76. package/tests/fs/rename.test.ts +4 -9
  77. package/tests/fs/stat.test.ts +2 -2
  78. package/tests/fs/times.test.ts +6 -6
  79. package/tests/fs/truncate.test.ts +8 -36
  80. package/tests/fs/watch.test.ts +10 -10
  81. package/tests/fs/write.test.ts +77 -13
  82. package/tests/fs/xattr.test.ts +7 -7
  83. package/tests/logs.js +2 -2
  84. package/tests/setup/port.ts +6 -0
  85. package/dist/internal/log.d.ts +0 -139
  86. package/dist/internal/log.js +0 -219
  87. package/tests/fs/writeFile.test.ts +0 -70
@@ -51,20 +51,20 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
53
  import { Buffer } from 'buffer';
54
- import { _throw, decodeUTF8, pick } from 'utilium';
54
+ import { Exception, rethrow, setUVMessage, UV } from 'kerium';
55
+ import { decodeUTF8, pick } from 'utilium';
55
56
  import { defaultContext } from '../internal/contexts.js';
56
- import { Errno, ErrnoError } from '../internal/error.js';
57
57
  import { hasAccess, InodeFlags, isBlockDevice, isCharacterDevice, isDirectory, isSymbolicLink } from '../internal/inode.js';
58
58
  import { dirname, join, parse, resolve } from '../path.js';
59
59
  import '../polyfills.js';
60
60
  import { createInterface } from '../readline.js';
61
- import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
61
+ import { __assertType, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
62
62
  import { checkAccess } from './config.js';
63
63
  import * as constants from './constants.js';
64
64
  import { Dir, Dirent } from './dir.js';
65
65
  import { deleteFD, fromFD, SyncHandle, toFD } from './file.js';
66
66
  import * as flags from './flags.js';
67
- import { _statfs, fixError, resolveMount } from './shared.js';
67
+ import { _statfs, resolveMount } from './shared.js';
68
68
  import { _chown, BigIntStats, Stats } from './stats.js';
69
69
  import { ReadStream, WriteStream } from './streams.js';
70
70
  import { emitChange, FSWatcher } from './watchers.js';
@@ -114,7 +114,7 @@ export class FileHandle {
114
114
  */
115
115
  async chown(uid, gid) {
116
116
  if (this.closed)
117
- throw ErrnoError.With('EBADF', this.path, 'chown');
117
+ throw UV('EBADF', 'chown', this.path);
118
118
  this.dirty = true;
119
119
  _chown(this.inode, uid, gid);
120
120
  if (this._isSync)
@@ -128,9 +128,9 @@ export class FileHandle {
128
128
  async chmod(mode) {
129
129
  const numMode = normalizeMode(mode, -1);
130
130
  if (numMode < 0)
131
- throw new ErrnoError(Errno.EINVAL, 'Invalid mode');
131
+ throw UV('EINVAL', 'chmod', this.path);
132
132
  if (this.closed)
133
- throw ErrnoError.With('EBADF', this.path, 'chmod');
133
+ throw UV('EBADF', 'chmod', this.path);
134
134
  this.dirty = true;
135
135
  this.inode.mode = (this.inode.mode & (numMode > constants.S_IFMT ? ~constants.S_IFMT : constants.S_IFMT)) | numMode;
136
136
  if (this._isSync || numMode > constants.S_IFMT)
@@ -148,7 +148,7 @@ export class FileHandle {
148
148
  */
149
149
  async sync() {
150
150
  if (this.closed)
151
- throw ErrnoError.With('EBADF', this.path, 'sync');
151
+ throw UV('EBADF', 'sync', this.path);
152
152
  if (!this.dirty)
153
153
  return;
154
154
  if (!this.fs.attributes.has('no_write'))
@@ -161,13 +161,18 @@ export class FileHandle {
161
161
  */
162
162
  async truncate(length = 0) {
163
163
  if (this.closed)
164
- throw ErrnoError.With('EBADF', this.path, 'truncate');
164
+ throw UV('EBADF', 'truncate', this.path);
165
165
  if (length < 0)
166
- throw new ErrnoError(Errno.EINVAL);
166
+ throw UV('EINVAL', 'truncate', this.path);
167
+ if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR))
168
+ throw UV('EBADF', 'truncate', this.path);
169
+ if (this.fs.attributes.has('readonly'))
170
+ throw UV('EROFS', 'truncate', this.path);
171
+ if (this.inode.flags & InodeFlags.Immutable)
172
+ throw UV('EPERM', 'truncate', this.path);
167
173
  this.dirty = true;
168
- if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR)) {
169
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode', this.path, 'truncate');
170
- }
174
+ if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR))
175
+ throw UV('EBADF', 'truncate', this.path);
171
176
  this.inode.mtimeMs = Date.now();
172
177
  this.inode.size = length;
173
178
  if (this._isSync)
@@ -181,7 +186,7 @@ export class FileHandle {
181
186
  */
182
187
  async utimes(atime, mtime) {
183
188
  if (this.closed)
184
- throw ErrnoError.With('EBADF', this.path, 'utimes');
189
+ throw UV('EBADF', 'utimes', this.path);
185
190
  this.dirty = true;
186
191
  this.inode.atimeMs = normalizeTime(atime);
187
192
  this.inode.mtimeMs = normalizeTime(mtime);
@@ -201,12 +206,8 @@ export class FileHandle {
201
206
  async appendFile(data, _options = {}) {
202
207
  const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
203
208
  const flag = flags.parse(options.flag);
204
- if (!(flag & constants.O_APPEND)) {
205
- throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
206
- }
207
- if (typeof data != 'string' && !options.encoding) {
208
- throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
209
- }
209
+ if (!(flag & constants.O_APPEND))
210
+ throw UV('EBADF', 'write', this.path);
210
211
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
211
212
  await this._write(encodedData, 0, encodedData.length);
212
213
  this._emitChange();
@@ -221,10 +222,10 @@ export class FileHandle {
221
222
  */
222
223
  async _read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
223
224
  if (this.closed)
224
- throw ErrnoError.With('EBADF', this.path, 'read');
225
+ throw UV('EBADF', 'read', this.path);
225
226
  if (this.flag & constants.O_WRONLY)
226
- throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode');
227
- if (!(this.inode.flags & InodeFlags.NoAtime)) {
227
+ throw UV('EBADF', 'read', this.path);
228
+ if (!(this.inode.flags & InodeFlags.NoAtime) && !this.fs.attributes.has('no_atime')) {
228
229
  this.dirty = true;
229
230
  this.inode.atimeMs = Date.now();
230
231
  }
@@ -259,11 +260,11 @@ export class FileHandle {
259
260
  async readFile(_options) {
260
261
  const options = normalizeOptions(_options, null, 'r', 0o444);
261
262
  const flag = flags.parse(options.flag);
262
- if (flag & constants.O_WRONLY) {
263
- throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading', this.path, 'readFile');
264
- }
263
+ if (flag & constants.O_WRONLY)
264
+ throw UV('EBADF', 'read', this.path);
265
265
  const { size } = await this.stat();
266
- const { buffer: data } = await this._read(new Uint8Array(size), 0, size, 0);
266
+ const data = new Uint8Array(size);
267
+ await this._read(data, 0, size, 0);
267
268
  const buffer = Buffer.from(data);
268
269
  return options.encoding ? buffer.toString(options.encoding) : buffer;
269
270
  }
@@ -273,7 +274,7 @@ export class FileHandle {
273
274
  */
274
275
  readableWebStream(options = {}) {
275
276
  if (this.closed)
276
- throw ErrnoError.With('EBADF', this.path, 'readableWebStream');
277
+ throw UV('EBADF', 'readableWebStream', this.path);
277
278
  return this.fs.streamRead(this.internalPath, options);
278
279
  }
279
280
  /**
@@ -285,9 +286,9 @@ export class FileHandle {
285
286
  */
286
287
  writableWebStream(options = {}) {
287
288
  if (this.closed)
288
- throw ErrnoError.With('EBADF', this.path, 'writableWebStream');
289
+ throw UV('EBADF', 'writableWebStream', this.path);
289
290
  if (this.inode.flags & InodeFlags.Immutable)
290
- throw new ErrnoError(Errno.EPERM, 'File is immutable', this.path, 'writableWebStream');
291
+ throw UV('EPERM', 'writableWebStream', this.path);
291
292
  return this.fs.streamWrite(this.internalPath, options);
292
293
  }
293
294
  /**
@@ -297,7 +298,7 @@ export class FileHandle {
297
298
  */
298
299
  readLines(options) {
299
300
  if (this.closed || this.flag & constants.O_WRONLY)
300
- throw ErrnoError.With('EBADF', this.path, 'readLines');
301
+ throw UV('EBADF', 'read', this.path);
301
302
  return createInterface({ input: this.createReadStream(options), crlfDelay: Infinity });
302
303
  }
303
304
  [Symbol.asyncDispose]() {
@@ -305,10 +306,9 @@ export class FileHandle {
305
306
  }
306
307
  async stat(opts) {
307
308
  if (this.closed)
308
- throw ErrnoError.With('EBADF', this.path, 'stat');
309
- if (checkAccess && !hasAccess(this.context, this.inode, constants.R_OK)) {
310
- throw ErrnoError.With('EACCES', this.path, 'stat');
311
- }
309
+ throw UV('EBADF', 'stat', this.path);
310
+ if (checkAccess && !hasAccess(this.context, this.inode, constants.R_OK))
311
+ throw UV('EACCES', 'stat', this.path);
312
312
  return (opts === null || opts === void 0 ? void 0 : opts.bigint) ? new BigIntStats(this.inode) : new Stats(this.inode);
313
313
  }
314
314
  /**
@@ -321,11 +321,13 @@ export class FileHandle {
321
321
  */
322
322
  async _write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
323
323
  if (this.closed)
324
- throw ErrnoError.With('EBADF', this.path, 'write');
324
+ throw UV('EBADF', 'write', this.path);
325
325
  if (this.inode.flags & InodeFlags.Immutable)
326
- throw new ErrnoError(Errno.EPERM, 'File is immutable', this.path, 'write');
326
+ throw UV('EPERM', 'write', this.path);
327
327
  if (!(this.flag & constants.O_WRONLY || this.flag & constants.O_RDWR))
328
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode', this.path, 'write');
328
+ throw UV('EBADF', 'write', this.path);
329
+ if (this.fs.attributes.has('readonly'))
330
+ throw UV('EROFS', 'write', this.path);
329
331
  this.dirty = true;
330
332
  const end = position + length;
331
333
  const slice = buffer.subarray(offset, offset + length);
@@ -382,12 +384,8 @@ export class FileHandle {
382
384
  async writeFile(data, _options = {}) {
383
385
  const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
384
386
  const flag = flags.parse(options.flag);
385
- if (!(flag & constants.O_WRONLY || flag & constants.O_RDWR)) {
386
- throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing', this.path, 'writeFile');
387
- }
388
- if (typeof data != 'string' && !options.encoding) {
389
- throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
390
- }
387
+ if (!(flag & constants.O_WRONLY || flag & constants.O_RDWR))
388
+ throw UV('EBADF', 'writeFile', this.path);
391
389
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
392
390
  await this._write(encodedData, 0, encodedData.length, 0);
393
391
  this._emitChange();
@@ -397,7 +395,7 @@ export class FileHandle {
397
395
  */
398
396
  async close() {
399
397
  if (this.closed)
400
- throw ErrnoError.With('EBADF', this.path, 'close');
398
+ throw UV('EBADF', 'close', this.path);
401
399
  await this.sync();
402
400
  this.dispose();
403
401
  deleteFD(this.context, this.fd);
@@ -407,9 +405,9 @@ export class FileHandle {
407
405
  */
408
406
  dispose(force) {
409
407
  if (this.closed)
410
- throw ErrnoError.With('EBADF', this.path, 'dispose');
408
+ throw UV('EBADF', 'close', this.path);
411
409
  if (this.dirty && !force)
412
- throw ErrnoError.With('EBUSY', this.path, 'dispose');
410
+ throw UV('EBUSY', 'close', this.path);
413
411
  this.closed = true;
414
412
  }
415
413
  /**
@@ -448,7 +446,7 @@ export class FileHandle {
448
446
  */
449
447
  createReadStream(options = {}) {
450
448
  if (this.closed || this.flag & constants.O_WRONLY)
451
- throw ErrnoError.With('EBADF', this.path, 'createReadStream');
449
+ throw UV('EBADF', 'createReadStream', this.path);
452
450
  return new ReadStream(options, this);
453
451
  }
454
452
  /**
@@ -457,34 +455,43 @@ export class FileHandle {
457
455
  */
458
456
  createWriteStream(options = {}) {
459
457
  if (this.closed)
460
- throw ErrnoError.With('EBADF', this.path, 'createWriteStream');
458
+ throw UV('EBADF', 'createWriteStream', this.path);
461
459
  if (this.inode.flags & InodeFlags.Immutable)
462
- throw new ErrnoError(Errno.EPERM, 'File is immutable', this.path, 'createWriteStream');
460
+ throw UV('EPERM', 'createWriteStream', this.path);
461
+ if (this.fs.attributes.has('readonly'))
462
+ throw UV('EROFS', 'createWriteStream', this.path);
463
463
  return new WriteStream(options, this);
464
464
  }
465
465
  }
466
466
  export async function rename(oldPath, newPath) {
467
467
  oldPath = normalizePath(oldPath);
468
+ __assertType(oldPath);
468
469
  newPath = normalizePath(newPath);
470
+ __assertType(newPath);
471
+ const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
469
472
  const src = resolveMount(oldPath, this);
470
473
  const dst = resolveMount(newPath, this);
471
- if (checkAccess && !(await stat.call(this, dirname(oldPath))).hasAccess(constants.W_OK, this)) {
472
- throw ErrnoError.With('EACCES', oldPath, 'rename');
473
- }
474
- try {
475
- if (src.mountPoint == dst.mountPoint) {
476
- await src.fs.rename(src.path, dst.path);
477
- emitChange(this, 'rename', oldPath.toString());
478
- emitChange(this, 'change', newPath.toString());
479
- return;
480
- }
481
- await writeFile.call(this, newPath, await readFile(oldPath));
482
- await unlink.call(this, oldPath);
483
- emitChange(this, 'rename', oldPath.toString());
484
- }
485
- catch (e) {
486
- throw fixError(e, { [src.path]: oldPath, [dst.path]: newPath });
487
- }
474
+ if (src.fs !== dst.fs)
475
+ throw UV('EXDEV', $ex);
476
+ if (dst.path.startsWith(src.path + '/'))
477
+ throw UV('EBUSY', $ex);
478
+ const parent = (await stat.call(this, dirname(oldPath)).catch(rethrow($ex)));
479
+ const stats = (await stat.call(this, oldPath).catch(rethrow($ex)));
480
+ const newParent = (await stat.call(this, dirname(newPath)).catch(rethrow($ex)));
481
+ const newStats = (await stat.call(this, newPath).catch((e) => {
482
+ if (e.code == 'ENOENT')
483
+ return null;
484
+ throw setUVMessage(Object.assign(e, $ex));
485
+ }));
486
+ if (checkAccess && (!parent.hasAccess(constants.R_OK, this) || !newParent.hasAccess(constants.W_OK, this)))
487
+ throw UV('EACCES', $ex);
488
+ if (newStats && !isDirectory(stats) && isDirectory(newStats))
489
+ throw UV('EISDIR', $ex);
490
+ if (newStats && isDirectory(stats) && !isDirectory(newStats))
491
+ throw UV('ENOTDIR', $ex);
492
+ await src.fs.rename(src.path, dst.path).catch(rethrow($ex));
493
+ emitChange(this, 'rename', oldPath);
494
+ emitChange(this, 'change', newPath);
488
495
  }
489
496
  rename;
490
497
  /**
@@ -496,7 +503,7 @@ export async function exists(path) {
496
503
  return await fs.exists(resolved);
497
504
  }
498
505
  catch (e) {
499
- if (e instanceof ErrnoError && e.code == 'ENOENT') {
506
+ if (e instanceof Exception && e.code == 'ENOENT') {
500
507
  return false;
501
508
  }
502
509
  throw e;
@@ -505,34 +512,23 @@ export async function exists(path) {
505
512
  export async function stat(path, options) {
506
513
  path = normalizePath(path);
507
514
  const { fs, path: resolved } = resolveMount(await realpath.call(this, path), this);
508
- try {
509
- const stats = await fs.stat(resolved);
510
- if (checkAccess && !hasAccess(this, stats, constants.R_OK)) {
511
- throw ErrnoError.With('EACCES', resolved, 'stat');
512
- }
513
- return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
514
- }
515
- catch (e) {
516
- throw fixError(e, { [resolved]: path });
517
- }
515
+ const $ex = { syscall: 'stat', path };
516
+ const stats = await fs.stat(resolved).catch(rethrow($ex));
517
+ if (checkAccess && !hasAccess(this, stats, constants.R_OK))
518
+ throw UV('EACCES', $ex);
519
+ return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
518
520
  }
519
521
  stat;
520
522
  export async function lstat(path, options) {
521
523
  path = normalizePath(path);
522
524
  const { fs, path: resolved } = resolveMount(path, this);
523
- try {
524
- const stats = await fs.stat(resolved);
525
- if (checkAccess && !hasAccess(this, stats, constants.R_OK)) {
526
- throw ErrnoError.With('EACCES', resolved, 'lstat');
527
- }
528
- return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
529
- }
530
- catch (e) {
531
- throw fixError(e, { [resolved]: path });
532
- }
525
+ const $ex = { syscall: 'lstat', path };
526
+ const stats = await fs.stat(resolved).catch(rethrow($ex));
527
+ if (checkAccess && !hasAccess(this, stats, constants.R_OK))
528
+ throw UV('EACCES', $ex);
529
+ return (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : new Stats(stats);
533
530
  }
534
531
  lstat;
535
- // FILE-ONLY METHODS
536
532
  export async function truncate(path, len = 0) {
537
533
  const env_1 = { stack: [], error: void 0, hasError: false };
538
534
  try {
@@ -553,16 +549,12 @@ truncate;
553
549
  export async function unlink(path) {
554
550
  path = normalizePath(path);
555
551
  const { fs, path: resolved } = resolveMount(path, this);
556
- try {
557
- if (checkAccess && !hasAccess(this, await fs.stat(resolved), constants.W_OK)) {
558
- throw ErrnoError.With('EACCES', resolved, 'unlink');
559
- }
560
- await fs.unlink(resolved);
561
- emitChange(this, 'rename', path.toString());
562
- }
563
- catch (e) {
564
- throw fixError(e, { [resolved]: path });
565
- }
552
+ const $ex = { syscall: 'unlink', path };
553
+ const stats = await fs.stat(resolved).catch(rethrow($ex));
554
+ if (checkAccess && !hasAccess(this, stats, constants.W_OK))
555
+ throw UV('EACCES', $ex);
556
+ await fs.unlink(resolved).catch(rethrow($ex));
557
+ emitChange(this, 'rename', path.toString());
566
558
  }
567
559
  unlink;
568
560
  /**
@@ -573,19 +565,19 @@ async function _open($, path, opt) {
573
565
  var _a;
574
566
  path = normalizePath(path);
575
567
  const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
576
- const { fullPath, fs, path: resolved, stats } = await _resolve($, path.toString(), opt.preserveSymlinks);
568
+ const $ex = { syscall: 'open', path };
569
+ const { fs, path: resolved, stats } = await _resolve($, path.toString(), opt.preserveSymlinks);
577
570
  if (!stats) {
578
- if (!(flag & constants.O_CREAT)) {
579
- throw ErrnoError.With('ENOENT', fullPath, '_open');
580
- }
571
+ if (!(flag & constants.O_CREAT))
572
+ throw UV('ENOENT', $ex);
581
573
  // Create the file
582
574
  const parentStats = await fs.stat(dirname(resolved));
583
- if (checkAccess && !hasAccess($, parentStats, constants.W_OK)) {
584
- throw ErrnoError.With('EACCES', dirname(fullPath), '_open');
585
- }
586
- if (!isDirectory(parentStats)) {
587
- throw ErrnoError.With('ENOTDIR', dirname(fullPath), '_open');
588
- }
575
+ if (checkAccess && !hasAccess($, parentStats, constants.W_OK))
576
+ throw UV('EACCES', 'open', dirname(path));
577
+ if (!isDirectory(parentStats))
578
+ throw UV('ENOTDIR', 'open', dirname(path));
579
+ if (!opt.allowDirectory && mode & constants.S_IFDIR)
580
+ throw UV('EISDIR', 'open', path);
589
581
  const { euid: uid, egid: gid } = (_a = $ === null || $ === void 0 ? void 0 : $.credentials) !== null && _a !== void 0 ? _a : defaultContext.credentials;
590
582
  const inode = await fs.createFile(resolved, {
591
583
  mode,
@@ -594,18 +586,13 @@ async function _open($, path, opt) {
594
586
  });
595
587
  return new FileHandle($, toFD(new SyncHandle($, path, fs, resolved, flag, inode)));
596
588
  }
597
- if (checkAccess && !hasAccess($, stats, flags.toMode(flag))) {
598
- throw ErrnoError.With('EACCES', fullPath, '_open');
599
- }
589
+ if (checkAccess && !hasAccess($, stats, flags.toMode(flag)))
590
+ throw UV('EACCES', $ex);
600
591
  if (flag & constants.O_EXCL)
601
- throw ErrnoError.With('EEXIST', fullPath, '_open');
592
+ throw UV('EEXIST', $ex);
602
593
  const handle = new FileHandle($, toFD(new SyncHandle($, path, fs, resolved, flag, stats)));
603
- /*
604
- In a previous implementation, we deleted the file and
605
- re-created it. However, this created a race condition if another
606
- asynchronous request was trying to read the file, as the file
607
- would not exist for a small period of time.
608
- */
594
+ if (!opt.allowDirectory && mode & constants.S_IFDIR)
595
+ throw UV('EISDIR', 'open', path);
609
596
  if (flag & constants.O_TRUNC)
610
597
  await handle.truncate(0);
611
598
  return handle;
@@ -652,9 +639,8 @@ export async function writeFile(path, data, _options) {
652
639
  const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
653
640
  const handle = __addDisposableResource(env_3, path instanceof FileHandle ? path : await open.call(this, path.toString(), options.flag, options.mode), true);
654
641
  const _data = typeof data == 'string' ? data : data instanceof DataView ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : data;
655
- if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
656
- throw new ErrnoError(Errno.EINVAL, 'The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received ' + typeof data, handle.path, 'writeFile');
657
- }
642
+ if (typeof _data != 'string' && !(_data instanceof Uint8Array))
643
+ throw new TypeError('The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received ' + typeof data);
658
644
  await handle.writeFile(_data, options);
659
645
  }
660
646
  catch (e_3) {
@@ -679,12 +665,9 @@ export async function appendFile(path, data, _options) {
679
665
  try {
680
666
  const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
681
667
  const flag = flags.parse(options.flag);
682
- if (!(flag & constants.O_APPEND)) {
683
- throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
684
- }
685
- if (typeof data != 'string' && !options.encoding) {
686
- throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
687
- }
668
+ const $ex = { syscall: 'write', path: path instanceof FileHandle ? path.path : path.toString() };
669
+ if (!(flag & constants.O_APPEND))
670
+ throw UV('EBADF', $ex);
688
671
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
689
672
  const handle = __addDisposableResource(env_4, typeof path == 'object' && 'fd' in path ? path : await open.call(this, path, options.flag, options.mode), true);
690
673
  await handle.appendFile(encodedData, options);
@@ -703,77 +686,69 @@ appendFile;
703
686
  export async function rmdir(path) {
704
687
  path = await realpath.call(this, path);
705
688
  const { fs, path: resolved } = resolveMount(path, this);
706
- try {
707
- const stats = await fs.stat(resolved);
708
- if (!stats)
709
- throw ErrnoError.With('ENOENT', path, 'rmdir');
710
- if (!isDirectory(stats))
711
- throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
712
- if (checkAccess && !hasAccess(this, stats, constants.W_OK))
713
- throw ErrnoError.With('EACCES', resolved, 'rmdir');
714
- await fs.rmdir(resolved);
715
- emitChange(this, 'rename', path.toString());
716
- }
717
- catch (e) {
718
- throw fixError(e, { [resolved]: path });
719
- }
689
+ const $ex = { syscall: 'rmdir', path };
690
+ const stats = await fs.stat(resolved).catch(rethrow($ex));
691
+ if (!stats)
692
+ throw UV('ENOENT', $ex);
693
+ if (!isDirectory(stats))
694
+ throw UV('ENOTDIR', $ex);
695
+ if (checkAccess && !hasAccess(this, stats, constants.W_OK))
696
+ throw UV('EACCES', $ex);
697
+ await fs.rmdir(resolved).catch(rethrow($ex));
698
+ emitChange(this, 'rename', path.toString());
720
699
  }
721
700
  rmdir;
722
701
  export async function mkdir(path, options) {
723
- var _a, _b;
702
+ var _a;
724
703
  const { euid: uid, egid: gid } = (_a = this === null || this === void 0 ? void 0 : this.credentials) !== null && _a !== void 0 ? _a : defaultContext.credentials;
725
704
  options = typeof options === 'object' ? options : { mode: options };
726
705
  const mode = normalizeMode(options === null || options === void 0 ? void 0 : options.mode, 0o777);
727
706
  path = await realpath.call(this, path);
728
- const { fs, path: resolved, root } = resolveMount(path, this);
729
- const errorPaths = { [resolved]: path };
730
- const __create = async (path, parent) => {
707
+ const { fs, path: resolved } = resolveMount(path, this);
708
+ const __create = async (path, resolved, parent) => {
731
709
  if (checkAccess && !hasAccess(this, parent, constants.W_OK))
732
- throw ErrnoError.With('EACCES', dirname(path), 'mkdir');
733
- const inode = await fs.mkdir(path, {
710
+ throw UV('EACCES', 'mkdir', dirname(path));
711
+ const inode = await fs
712
+ .mkdir(resolved, {
734
713
  mode,
735
714
  uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
736
715
  gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
737
- });
716
+ })
717
+ .catch(rethrow({ syscall: 'mkdir', path }));
738
718
  emitChange(this, 'rename', path);
739
719
  return inode;
740
720
  };
741
- try {
742
- if (!(options === null || options === void 0 ? void 0 : options.recursive)) {
743
- await __create(resolved, await fs.stat(dirname(resolved)));
744
- return;
745
- }
746
- const dirs = [];
747
- for (let dir = resolved, origDir = path; !(await fs.exists(dir)); dir = dirname(dir), origDir = dirname(origDir)) {
748
- dirs.unshift(dir);
749
- errorPaths[dir] = origDir;
750
- }
751
- if (!dirs.length)
752
- return;
753
- const stats = [await fs.stat(dirname(dirs[0]))];
754
- for (const [i, dir] of dirs.entries()) {
755
- stats.push(await __create(dir, stats[i]));
756
- }
757
- return root.length == 1 ? dirs[0] : (_b = dirs[0]) === null || _b === void 0 ? void 0 : _b.slice(root.length);
721
+ if (!(options === null || options === void 0 ? void 0 : options.recursive)) {
722
+ await __create(path, resolved, await fs.stat(dirname(resolved)).catch(rethrow({ path: dirname(path) })));
723
+ return;
758
724
  }
759
- catch (e) {
760
- throw fixError(e, errorPaths);
725
+ const dirs = [];
726
+ let origDir = path;
727
+ for (let dir = resolved; !(await fs.exists(dir).catch(rethrow({ syscall: 'exists', path: origDir }))); dir = dirname(dir), origDir = dirname(origDir)) {
728
+ dirs.unshift([origDir, dir]);
761
729
  }
730
+ if (!dirs.length)
731
+ return;
732
+ const stats = [await fs.stat(dirname(dirs[0][1])).catch(rethrow({ syscall: 'stat', path: dirname(dirs[0][0]) }))];
733
+ for (const [i, [path, resolved]] of dirs.entries()) {
734
+ stats.push(await __create(path, resolved, stats[i]));
735
+ }
736
+ return dirs[0][0];
762
737
  }
763
738
  mkdir;
764
739
  export async function readdir(path, options) {
765
740
  options = typeof options === 'object' ? options : { encoding: options };
766
741
  path = await realpath.call(this, path);
767
742
  const { fs, path: resolved } = resolveMount(path, this);
768
- const stats = await fs.stat(resolved).catch((e) => _throw(fixError(e, { [resolved]: path })));
769
- if (!stats) {
770
- throw ErrnoError.With('ENOENT', path, 'readdir');
771
- }
743
+ const $ex = { syscall: 'readdir', path };
744
+ const stats = await fs.stat(resolved).catch(rethrow({ syscall: 'stat', path }));
745
+ if (!stats)
746
+ throw UV('ENOENT', $ex);
772
747
  if (checkAccess && !hasAccess(this, stats, constants.R_OK))
773
- throw ErrnoError.With('EACCES', path, 'readdir');
748
+ throw UV('EACCES', $ex);
774
749
  if (!isDirectory(stats))
775
- throw ErrnoError.With('ENOTDIR', path, 'readdir');
776
- const entries = await fs.readdir(resolved).catch((e) => _throw(fixError(e, { [resolved]: path })));
750
+ throw UV('ENOTDIR', $ex);
751
+ const entries = await fs.readdir(resolved).catch(rethrow($ex));
777
752
  const values = [];
778
753
  const addEntry = async (entry) => {
779
754
  let entryStats;
@@ -781,7 +756,7 @@ export async function readdir(path, options) {
781
756
  entryStats = await fs.stat(join(resolved, entry)).catch((e) => {
782
757
  if (e.code == 'ENOENT')
783
758
  return;
784
- throw fixError(e, { [resolved]: path });
759
+ throw setUVMessage(Object.assign(e, { syscall: 'stat', path: join(path, entry) }));
785
760
  });
786
761
  if (!entryStats)
787
762
  return;
@@ -815,49 +790,41 @@ export async function readdir(path, options) {
815
790
  return values;
816
791
  }
817
792
  readdir;
818
- export async function link(targetPath, linkPath) {
819
- targetPath = normalizePath(targetPath);
820
- linkPath = normalizePath(linkPath);
821
- const { fs, path } = resolveMount(targetPath, this);
822
- const link = resolveMount(linkPath, this);
823
- if (fs != link.fs) {
824
- throw ErrnoError.With('EXDEV', linkPath, 'link');
825
- }
826
- try {
827
- if (checkAccess && !hasAccess(this, await fs.stat(dirname(path)), constants.R_OK)) {
828
- throw ErrnoError.With('EACCES', dirname(path), 'link');
829
- }
830
- // We need to use the VFS here since the link path may be a mount point
831
- if (checkAccess && !(await stat.call(this, dirname(linkPath))).hasAccess(constants.W_OK, this)) {
832
- throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
833
- }
834
- if (checkAccess && !hasAccess(this, await fs.stat(path), constants.R_OK)) {
835
- throw ErrnoError.With('EACCES', path, 'link');
836
- }
837
- return await fs.link(path, link.path);
838
- }
839
- catch (e) {
840
- throw fixError(e, { [link.path]: linkPath, [path]: targetPath });
841
- }
793
+ export async function link(path, dest) {
794
+ path = normalizePath(path);
795
+ dest = normalizePath(dest);
796
+ const { fs, path: resolved } = resolveMount(path, this);
797
+ const dst = resolveMount(dest, this);
798
+ const $ex = { syscall: 'link', path };
799
+ if (fs != dst.fs)
800
+ throw UV('EXDEV', $ex);
801
+ const stats = await fs.stat(dirname(resolved)).catch(rethrow({ syscall: 'stat', path: dirname(path) }));
802
+ if (checkAccess && !hasAccess(this, stats, constants.R_OK))
803
+ throw UV('EACCES', 'link', dirname(path));
804
+ // We need to use the VFS here since the link path may be a mount point
805
+ if (checkAccess && !(await stat.call(this, dirname(dest))).hasAccess(constants.W_OK, this))
806
+ throw UV('EACCES', 'link', dirname(dest));
807
+ if (checkAccess && !hasAccess(this, await fs.stat(resolved).catch(rethrow($ex)), constants.R_OK))
808
+ throw UV('EACCES', $ex);
809
+ return await fs.link(resolved, dst.path).catch(rethrow($ex));
842
810
  }
843
811
  link;
844
812
  /**
845
813
  * `symlink`.
846
- * @param target target path
814
+ * @param dest target path
847
815
  * @param path link path
848
816
  * @param type can be either `'dir'` or `'file'` (default is `'file'`)
849
817
  */
850
- export async function symlink(target, path, type = 'file') {
818
+ export async function symlink(dest, path, type = 'file') {
851
819
  const env_5 = { stack: [], error: void 0, hasError: false };
852
820
  try {
853
- if (!['file', 'dir', 'junction'].includes(type)) {
854
- throw new ErrnoError(Errno.EINVAL, 'Invalid symlink type: ' + type);
855
- }
821
+ if (!['file', 'dir', 'junction'].includes(type))
822
+ throw new TypeError('Invalid symlink type: ' + type);
856
823
  path = normalizePath(path);
857
824
  if (await exists.call(this, path))
858
- throw ErrnoError.With('EEXIST', path, 'symlink');
825
+ throw UV('EEXIST', 'symlink', path);
859
826
  const handle = __addDisposableResource(env_5, await _open(this, path, { flag: 'w+', mode: 0o644, preserveSymlinks: true }), true);
860
- await handle.writeFile(normalizePath(target, true));
827
+ await handle.writeFile(normalizePath(dest, true));
861
828
  await handle.chmod(constants.S_IFLNK);
862
829
  }
863
830
  catch (e_5) {
@@ -874,7 +841,11 @@ symlink;
874
841
  export async function readlink(path, options) {
875
842
  const env_6 = { stack: [], error: void 0, hasError: false };
876
843
  try {
877
- const handle = __addDisposableResource(env_6, await _open(this, normalizePath(path), { flag: 'r', mode: 0o644, preserveSymlinks: true }), true);
844
+ path = normalizePath(path);
845
+ __assertType(path);
846
+ const handle = __addDisposableResource(env_6, await _open(this, path, { flag: 'r', mode: 0o644, preserveSymlinks: true }), true);
847
+ if (!isSymbolicLink(handle.inode))
848
+ throw UV('EINVAL', 'readlink', path);
878
849
  const value = await handle.readFile();
879
850
  const encoding = typeof options == 'object' ? options === null || options === void 0 ? void 0 : options.encoding : options;
880
851
  // always defaults to utf-8 to avoid wrangler (cloudflare) worker "unknown encoding" exception
@@ -1044,20 +1015,18 @@ async function _resolve($, path, preserveSymlinks) {
1044
1015
  const realDir = dir == '/' ? '/' : await realpath.call($, dir);
1045
1016
  const maybePath = join(realDir, base);
1046
1017
  const resolved = resolveMount(maybePath, $);
1047
- try {
1048
- const stats = await resolved.fs.stat(resolved.path);
1049
- if (!isSymbolicLink(stats)) {
1050
- return { ...resolved, fullPath: maybePath, stats };
1051
- }
1052
- const target = resolve.call($, realDir, (await readlink.call($, maybePath)).toString());
1053
- return await _resolve($, target);
1054
- }
1055
- catch (e) {
1056
- if (e.code == 'ENOENT') {
1057
- return { ...resolved, fullPath: path };
1058
- }
1059
- throw fixError(e, { [resolved.path]: maybePath });
1018
+ const stats = await resolved.fs.stat(resolved.path).catch((e) => {
1019
+ if (e.code == 'ENOENT')
1020
+ return;
1021
+ throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath }));
1022
+ });
1023
+ if (!stats)
1024
+ return { ...resolved, fullPath: path };
1025
+ if (!isSymbolicLink(stats)) {
1026
+ return { ...resolved, fullPath: maybePath, stats };
1060
1027
  }
1028
+ const target = resolve.call($, realDir, (await readlink.call($, maybePath)).toString());
1029
+ return await _resolve($, target);
1061
1030
  }
1062
1031
  export async function realpath(path, options) {
1063
1032
  var _a;
@@ -1113,9 +1082,8 @@ export async function access(path, mode = constants.F_OK) {
1113
1082
  if (!checkAccess)
1114
1083
  return;
1115
1084
  const stats = await stat.call(this, path);
1116
- if (!stats.hasAccess(mode, this)) {
1117
- throw new ErrnoError(Errno.EACCES);
1118
- }
1085
+ if (!stats.hasAccess(mode, this))
1086
+ throw UV('EACCES', 'access', path.toString());
1119
1087
  }
1120
1088
  access;
1121
1089
  /**
@@ -1149,7 +1117,7 @@ export async function rm(path, options) {
1149
1117
  case constants.S_IFIFO:
1150
1118
  case constants.S_IFSOCK:
1151
1119
  default:
1152
- throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
1120
+ throw UV('ENOSYS', 'rm', path);
1153
1121
  }
1154
1122
  }
1155
1123
  rm;
@@ -1171,9 +1139,8 @@ mkdtemp;
1171
1139
  export async function copyFile(src, dest, mode) {
1172
1140
  src = normalizePath(src);
1173
1141
  dest = normalizePath(dest);
1174
- if (mode && mode & constants.COPYFILE_EXCL && (await exists.call(this, dest))) {
1175
- throw new ErrnoError(Errno.EEXIST, 'Destination file already exists', dest, 'copyFile');
1176
- }
1142
+ if (mode && mode & constants.COPYFILE_EXCL && (await exists.call(this, dest)))
1143
+ throw UV('EEXIST', 'copyFile', dest);
1177
1144
  await writeFile.call(this, dest, await readFile.call(this, src));
1178
1145
  emitChange(this, 'rename', dest.toString());
1179
1146
  }
@@ -1206,14 +1173,12 @@ export async function cp(source, destination, opts) {
1206
1173
  source = normalizePath(source);
1207
1174
  destination = normalizePath(destination);
1208
1175
  const srcStats = await lstat.call(this, source); // Use lstat to follow symlinks if not dereferencing
1209
- if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && (await exists.call(this, destination))) {
1210
- throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists', destination, 'cp');
1211
- }
1176
+ if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && (await exists.call(this, destination)))
1177
+ throw UV('EEXIST', 'cp', destination);
1212
1178
  switch (srcStats.mode & constants.S_IFMT) {
1213
1179
  case constants.S_IFDIR: {
1214
- if (!(opts === null || opts === void 0 ? void 0 : opts.recursive)) {
1215
- throw new ErrnoError(Errno.EISDIR, source + ' is a directory (not copied)', source, 'cp');
1216
- }
1180
+ if (!(opts === null || opts === void 0 ? void 0 : opts.recursive))
1181
+ throw UV('EISDIR', 'cp', source);
1217
1182
  const [entries] = await Promise.all([
1218
1183
  readdir.call(this, source, { withFileTypes: true }),
1219
1184
  mkdir.call(this, destination, { recursive: true }),
@@ -1237,7 +1202,7 @@ export async function cp(source, destination, opts) {
1237
1202
  case constants.S_IFIFO:
1238
1203
  case constants.S_IFSOCK:
1239
1204
  default:
1240
- throw new ErrnoError(Errno.EPERM, 'File type not supported', source, 'rm');
1205
+ throw UV('ENOSYS', 'cp', source);
1241
1206
  }
1242
1207
  // Optionally preserve timestamps
1243
1208
  if (opts === null || opts === void 0 ? void 0 : opts.preserveTimestamps) {