@zenfs/core 1.10.0 → 1.10.2

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.
@@ -90,7 +90,7 @@ export class FileHandle {
90
90
  async chmod(mode) {
91
91
  const numMode = normalizeMode(mode, -1);
92
92
  if (numMode < 0)
93
- throw new ErrnoError(Errno.EINVAL, 'Invalid mode.');
93
+ throw new ErrnoError(Errno.EINVAL, 'Invalid mode');
94
94
  await this.file.chmod(numMode);
95
95
  this._emitChange();
96
96
  }
@@ -140,7 +140,7 @@ export class FileHandle {
140
140
  const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
141
141
  const flag = parseFlag(options.flag);
142
142
  if (!isAppendable(flag)) {
143
- throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
143
+ throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
144
144
  }
145
145
  if (typeof data != 'string' && !options.encoding) {
146
146
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
@@ -161,7 +161,7 @@ export class FileHandle {
161
161
  offset = buffer.offset;
162
162
  buffer = buffer.buffer;
163
163
  }
164
- if (isNaN(+position)) {
164
+ if (!Number.isSafeInteger(position)) {
165
165
  position = this.file.position;
166
166
  }
167
167
  buffer || (buffer = new Uint8Array((await this.file.stat()).size));
@@ -171,7 +171,7 @@ export class FileHandle {
171
171
  const options = normalizeOptions(_options, null, 'r', 0o444);
172
172
  const flag = parseFlag(options.flag);
173
173
  if (!isReadable(flag)) {
174
- throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading.');
174
+ throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading');
175
175
  }
176
176
  const { size } = await this.stat();
177
177
  const { buffer: data } = await this.file.read(new Uint8Array(size), 0, size, 0);
@@ -179,44 +179,25 @@ export class FileHandle {
179
179
  return options.encoding ? buffer.toString(options.encoding) : buffer;
180
180
  }
181
181
  /**
182
- * Returns a `ReadableStream` that may be used to read the files data.
183
- *
184
- * An error will be thrown if this method is called more than once or is called after the `FileHandle` is closed or closing.
185
- *
186
- * While the `ReadableStream` will read the file to completion,
187
- * it will not close the `FileHandle` automatically.
188
- * User code must still call the `fileHandle.close()` method.
182
+ * Read file data using a `ReadableStream`.
183
+ * The handle will not be closed automatically.
189
184
  */
190
185
  readableWebStream(options = {}) {
191
- // Note: using an arrow function to preserve `this`
192
- const start = async (controller) => {
193
- try {
194
- const chunkSize = 64 * 1024, maxChunks = 1e7;
195
- let i = 0, position = 0, bytesRead = NaN;
196
- while (bytesRead > 0) {
197
- const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize, position);
186
+ return new ReadableStream({
187
+ start: async (controller) => {
188
+ const chunkSize = 0x1000;
189
+ for (let i = 0; i < 1e7; i++) {
190
+ const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize).catch(controller.error);
191
+ if (!result)
192
+ return;
198
193
  if (!result.bytesRead) {
199
194
  controller.close();
200
195
  return;
201
196
  }
202
197
  controller.enqueue(result.buffer.subarray(0, result.bytesRead));
203
- position += result.bytesRead;
204
- if (++i >= maxChunks) {
205
- throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
206
- }
207
- bytesRead = result.bytesRead;
208
198
  }
209
- }
210
- catch (e) {
211
- controller.error(e);
212
- }
213
- };
214
- const _gt = globalThis;
215
- if (!('ReadableStream' in _gt)) {
216
- throw new ErrnoError(Errno.ENOSYS, 'ReadableStream is missing on globalThis');
217
- }
218
- return new _gt.ReadableStream({
219
- start,
199
+ controller.error(new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'readableWebStream'));
200
+ },
220
201
  type: options.type,
221
202
  });
222
203
  }
@@ -240,26 +221,25 @@ export class FileHandle {
240
221
  * Asynchronously writes `string` to the file.
241
222
  * The `FileHandle` must have been opened for writing.
242
223
  * It is unsafe to call `write()` multiple times on the same file without waiting for the `Promise`
243
- * to be resolved (or rejected). For this scenario, `fs.createWriteStream` is strongly recommended.
224
+ * to be resolved (or rejected). For this scenario, `createWriteStream` is strongly recommended.
244
225
  */
245
226
  async write(data, options, lenOrEnc, position) {
246
227
  let buffer, offset, length;
247
- if (typeof options == 'object') {
248
- lenOrEnc = options === null || options === void 0 ? void 0 : options.length;
249
- position = options === null || options === void 0 ? void 0 : options.position;
250
- options = options === null || options === void 0 ? void 0 : options.offset;
228
+ if (typeof options == 'object' && options != null) {
229
+ lenOrEnc = options.length;
230
+ position = options.position;
231
+ options = options.offset;
251
232
  }
252
233
  if (typeof data === 'string') {
253
234
  position = typeof options === 'number' ? options : null;
254
- const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8';
255
235
  offset = 0;
256
- buffer = Buffer.from(data, encoding);
236
+ buffer = Buffer.from(data, typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
257
237
  length = buffer.length;
258
238
  }
259
239
  else {
260
240
  buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
261
- offset = options;
262
- length = lenOrEnc;
241
+ offset = options !== null && options !== void 0 ? options : 0;
242
+ length = typeof lenOrEnc == 'number' ? lenOrEnc : buffer.byteLength;
263
243
  position = typeof position === 'number' ? position : null;
264
244
  }
265
245
  position !== null && position !== void 0 ? position : (position = this.file.position);
@@ -281,7 +261,7 @@ export class FileHandle {
281
261
  const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
282
262
  const flag = parseFlag(options.flag);
283
263
  if (!isWriteable(flag)) {
284
- throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing.');
264
+ throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing');
285
265
  }
286
266
  if (typeof data != 'string' && !options.encoding) {
287
267
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
@@ -304,9 +284,11 @@ export class FileHandle {
304
284
  * @returns The number of bytes written.
305
285
  */
306
286
  async writev(buffers, position) {
287
+ if (typeof position == 'number')
288
+ this.file.position = position;
307
289
  let bytesWritten = 0;
308
290
  for (const buffer of buffers) {
309
- bytesWritten += (await this.write(buffer, 0, buffer.length, position + bytesWritten)).bytesWritten;
291
+ bytesWritten += (await this.write(buffer)).bytesWritten;
310
292
  }
311
293
  return { bytesWritten, buffers };
312
294
  }
@@ -317,9 +299,11 @@ export class FileHandle {
317
299
  * @returns The number of bytes read.
318
300
  */
319
301
  async readv(buffers, position) {
302
+ if (typeof position == 'number')
303
+ this.file.position = position;
320
304
  let bytesRead = 0;
321
305
  for (const buffer of buffers) {
322
- bytesRead += (await this.read(buffer, 0, buffer.byteLength, position + bytesRead)).bytesRead;
306
+ bytesRead += (await this.read(buffer)).bytesRead;
323
307
  }
324
308
  return { bytesRead, buffers };
325
309
  }
@@ -328,15 +312,23 @@ export class FileHandle {
328
312
  * @param options Options for the readable stream
329
313
  */
330
314
  createReadStream(options) {
315
+ var _a, _b;
316
+ const start = (_a = options === null || options === void 0 ? void 0 : options.start) !== null && _a !== void 0 ? _a : this.file.position;
331
317
  const stream = new ReadStream({
332
318
  highWaterMark: (options === null || options === void 0 ? void 0 : options.highWaterMark) || 64 * 1024,
333
- encoding: options.encoding,
319
+ encoding: (_b = options === null || options === void 0 ? void 0 : options.encoding) !== null && _b !== void 0 ? _b : undefined,
334
320
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
335
321
  read: async (size) => {
336
322
  try {
337
- const result = await this.read(new Uint8Array(size), 0, size, this.file.position);
338
- stream.push(!result.bytesRead ? null : result.buffer.subarray(0, result.bytesRead)); // Push data or null for EOF
339
- this.file.position += result.bytesRead;
323
+ if (typeof (options === null || options === void 0 ? void 0 : options.end) === 'number' && start >= options.end) {
324
+ stream.push(null);
325
+ return;
326
+ }
327
+ if (typeof (options === null || options === void 0 ? void 0 : options.end) === 'number') {
328
+ size = Math.min(size, options.end - start);
329
+ }
330
+ const result = await this.read(new Uint8Array(size), 0, size, options === null || options === void 0 ? void 0 : options.start);
331
+ stream.push(!result.bytesRead ? null : result.buffer.subarray(0, result.bytesRead));
340
332
  }
341
333
  catch (error) {
342
334
  stream.destroy(error);
@@ -351,20 +343,25 @@ export class FileHandle {
351
343
  * @param options Options for the writeable stream.
352
344
  */
353
345
  createWriteStream(options) {
354
- const streamOptions = {
346
+ if (typeof (options === null || options === void 0 ? void 0 : options.start) == 'number')
347
+ this.file.position = options.start;
348
+ const { stack } = new Error();
349
+ const stream = new WriteStream({
355
350
  highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
356
- encoding: options === null || options === void 0 ? void 0 : options.encoding,
351
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
357
352
  write: async (chunk, encoding, callback) => {
358
353
  try {
359
354
  const { bytesWritten } = await this.write(chunk, null, encoding);
360
- callback(bytesWritten == chunk.length ? null : new Error('Failed to write full chunk'));
355
+ if (bytesWritten == chunk.length)
356
+ return callback();
357
+ throw new ErrnoError(Errno.EIO, `Failed to write full chunk of write stream (wrote ${bytesWritten}/${chunk.length} bytes)`);
361
358
  }
362
359
  catch (error) {
360
+ error.stack += stack === null || stack === void 0 ? void 0 : stack.slice(5);
363
361
  callback(error);
364
362
  }
365
363
  },
366
- };
367
- const stream = new WriteStream(streamOptions);
364
+ });
368
365
  stream.path = this.file.path;
369
366
  return stream;
370
367
  }
@@ -596,7 +593,7 @@ export async function appendFile(path, data, _options) {
596
593
  const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
597
594
  const flag = parseFlag(options.flag);
598
595
  if (!isAppendable(flag)) {
599
- throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
596
+ throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
600
597
  }
601
598
  if (typeof data != 'string' && !options.encoding) {
602
599
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
@@ -1086,7 +1083,7 @@ export async function copyFile(src, dest, mode) {
1086
1083
  src = normalizePath(src);
1087
1084
  dest = normalizePath(dest);
1088
1085
  if (mode && mode & constants.COPYFILE_EXCL && (await exists.call(this, dest))) {
1089
- throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');
1086
+ throw new ErrnoError(Errno.EEXIST, 'Destination file already exists', dest, 'copyFile');
1090
1087
  }
1091
1088
  await writeFile.call(this, dest, await readFile.call(this, src));
1092
1089
  emitChange(this, 'rename', dest.toString());
@@ -1121,7 +1118,7 @@ export async function cp(source, destination, opts) {
1121
1118
  destination = normalizePath(destination);
1122
1119
  const srcStats = await lstat.call(this, source); // Use lstat to follow symlinks if not dereferencing
1123
1120
  if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && (await exists.call(this, destination))) {
1124
- throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists.', destination, 'cp');
1121
+ throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists', destination, 'cp');
1125
1122
  }
1126
1123
  switch (srcStats.mode & constants.S_IFMT) {
1127
1124
  case constants.S_IFDIR: {
@@ -47,7 +47,7 @@ export function mount(mountPoint, fs) {
47
47
  mountPoint = '/' + mountPoint;
48
48
  mountPoint = resolve(mountPoint);
49
49
  if (mounts.has(mountPoint)) {
50
- throw err(new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use.'));
50
+ throw err(new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use'));
51
51
  }
52
52
  fs._mountPoint = mountPoint;
53
53
  mounts.set(mountPoint, fs);
@@ -63,7 +63,7 @@ export function umount(mountPoint) {
63
63
  mountPoint = '/' + mountPoint;
64
64
  mountPoint = resolve(mountPoint);
65
65
  if (!mounts.has(mountPoint)) {
66
- warn(mountPoint + ' is already unmounted.');
66
+ warn(mountPoint + ' is already unmounted');
67
67
  return;
68
68
  }
69
69
  mounts.delete(mountPoint);
@@ -2,14 +2,14 @@ import type * as fs from 'node:fs';
2
2
  import type { Callback } from '../utils.js';
3
3
  import { Readable, Writable } from 'readable-stream';
4
4
  export declare class ReadStream extends Readable implements fs.ReadStream {
5
- close(callback?: Callback<[void], null>): void;
5
+ close: (callback?: Callback<[void], null>) => void;
6
6
  wrap(oldStream: NodeJS.ReadableStream): this;
7
7
  bytesRead: number;
8
8
  path: string | Buffer;
9
9
  pending: boolean;
10
10
  }
11
11
  export declare class WriteStream extends Writable implements fs.WriteStream {
12
- close(callback?: Callback<[void], null>): void;
12
+ close: (callback?: Callback<[void], null>) => void;
13
13
  bytesWritten: number;
14
14
  path: string | Buffer;
15
15
  pending: boolean;
@@ -1,15 +1,18 @@
1
1
  import { Readable, Writable } from 'readable-stream';
2
2
  import { Errno, ErrnoError } from '../internal/error.js';
3
3
  export class ReadStream extends Readable {
4
- close(callback = () => null) {
5
- try {
6
- super.destroy();
7
- super.emit('close');
8
- callback(null);
9
- }
10
- catch (err) {
11
- callback(new ErrnoError(Errno.EIO, err.toString()));
12
- }
4
+ constructor() {
5
+ super(...arguments);
6
+ this.close = (callback = () => null) => {
7
+ try {
8
+ super.destroy();
9
+ super.emit('close');
10
+ callback(null);
11
+ }
12
+ catch (err) {
13
+ callback(new ErrnoError(Errno.EIO, err.toString()));
14
+ }
15
+ };
13
16
  }
14
17
  wrap(oldStream) {
15
18
  super.wrap(oldStream);
@@ -17,14 +20,17 @@ export class ReadStream extends Readable {
17
20
  }
18
21
  }
19
22
  export class WriteStream extends Writable {
20
- close(callback = () => null) {
21
- try {
22
- super.destroy();
23
- super.emit('close');
24
- callback(null);
25
- }
26
- catch (err) {
27
- callback(new ErrnoError(Errno.EIO, err.toString()));
28
- }
23
+ constructor() {
24
+ super(...arguments);
25
+ this.close = (callback = () => null) => {
26
+ try {
27
+ super.destroy();
28
+ super.emit('close');
29
+ callback(null);
30
+ }
31
+ catch (err) {
32
+ callback(new ErrnoError(Errno.EIO, err.toString()));
33
+ }
34
+ };
29
35
  }
30
36
  }
package/dist/vfs/sync.js CHANGED
@@ -259,7 +259,7 @@ export function readFileSync(path, _options = {}) {
259
259
  const options = normalizeOptions(_options, null, 'r', 0o644);
260
260
  const flag = parseFlag(options.flag);
261
261
  if (!isReadable(flag)) {
262
- throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading.');
262
+ throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading');
263
263
  }
264
264
  const data = Buffer.from(_readFileSync.call(this, typeof path == 'number' ? fd2file(path).path : path, options.flag, false));
265
265
  return options.encoding ? data.toString(options.encoding) : data;
@@ -271,7 +271,7 @@ export function writeFileSync(path, data, _options = {}) {
271
271
  const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
272
272
  const flag = parseFlag(options.flag);
273
273
  if (!isWriteable(flag)) {
274
- throw new ErrnoError(Errno.EINVAL, 'Flag passed to writeFile must allow for writing.');
274
+ throw new ErrnoError(Errno.EINVAL, 'Flag passed to writeFile must allow for writing');
275
275
  }
276
276
  if (typeof data != 'string' && !options.encoding) {
277
277
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
@@ -309,7 +309,7 @@ export function appendFileSync(filename, data, _options = {}) {
309
309
  const options = normalizeOptions(_options, 'utf8', 'a+', 0o644);
310
310
  const flag = parseFlag(options.flag);
311
311
  if (!isAppendable(flag)) {
312
- throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
312
+ throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
313
313
  }
314
314
  if (typeof data != 'string' && !options.encoding) {
315
315
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
@@ -743,7 +743,7 @@ export function copyFileSync(source, destination, flags) {
743
743
  source = normalizePath(source);
744
744
  destination = normalizePath(destination);
745
745
  if (flags && flags & constants.COPYFILE_EXCL && existsSync(destination)) {
746
- throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', destination, 'copyFile');
746
+ throw new ErrnoError(Errno.EEXIST, 'Destination file already exists', destination, 'copyFile');
747
747
  }
748
748
  writeFileSync.call(this, destination, readFileSync(source));
749
749
  emitChange(this, 'rename', destination.toString());
@@ -810,7 +810,7 @@ export function cpSync(source, destination, opts) {
810
810
  destination = normalizePath(destination);
811
811
  const srcStats = lstatSync.call(this, source); // Use lstat to follow symlinks if not dereferencing
812
812
  if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && existsSync.call(this, destination)) {
813
- throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists.', destination, 'cp');
813
+ throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists', destination, 'cp');
814
814
  }
815
815
  switch (srcStats.mode & constants.S_IFMT) {
816
816
  case constants.S_IFDIR:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.10.0",
3
+ "version": "1.10.2",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -21,6 +21,7 @@
21
21
  "files": [
22
22
  "dist",
23
23
  "tests",
24
+ "types",
24
25
  "license.md",
25
26
  "eslint.shared.js"
26
27
  ],
@@ -66,11 +67,10 @@
66
67
  },
67
68
  "dependencies": {
68
69
  "@types/node": "^22.10.1",
69
- "@types/readable-stream": "^4.0.10",
70
70
  "buffer": "^6.0.3",
71
71
  "eventemitter3": "^5.0.1",
72
72
  "readable-stream": "^4.5.2",
73
- "utilium": "^1.3.1"
73
+ "utilium": "^1.3.3"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@eslint/js": "^9.8.0",
package/readme.md CHANGED
@@ -1,7 +1,3 @@
1
- ---
2
- title: Overview
3
- ---
4
-
5
1
  # ZenFS
6
2
 
7
3
  ZenFS is a cross-platform library that emulates the [NodeJS filesystem API](http://nodejs.org/api/fs.html). It works using a system of backends, which are used by ZenFS to store and retrieve data. ZenFS can also integrate with other tools.
@@ -15,6 +11,7 @@ ZenFS is modular and extensible. The core includes some built-in backends:
15
11
  - `Fetch`: Downloads files over HTTP with the `fetch` API
16
12
  - `Port`: Interacts with a remote over a `MessagePort`-like interface (e.g. a worker)
17
13
  - `Passthrough`: Use an existing `node:fs` interface with ZenFS
14
+ - `SingleBuffer`: A backend contained within a single buffer. Can be used for synchronous multi-threaded operations using `SharedArrayBuffer`
18
15
 
19
16
  ZenFS supports a number of other backends. Many are provided as separate packages under `@zenfs`. More backends can be defined by separate libraries by extending the `FileSystem` class and providing a `Backend` object.
20
17
 
@@ -6,7 +6,7 @@ import { suite, test } from 'node:test';
6
6
  import assert from 'node:assert/strict';
7
7
 
8
8
  suite('LockFS mutex', () => {
9
- const fs = new (Mutexed(StoreFS))(new InMemoryStore('test'));
9
+ const fs = new (Mutexed(StoreFS))(new InMemoryStore(0x10000, 'test'));
10
10
  fs._fs.checkRootSync();
11
11
 
12
12
  test('lock/unlock', () => {
@@ -12,7 +12,7 @@ try {
12
12
  execSync(`npm exec make-index -- ${data} --output ${tmp}/index.json --quiet`, { stdio: 'inherit' });
13
13
  } catch (e) {
14
14
  if (e.signal == 'SIGINT') {
15
- console.log('Aborted whilst creating index.');
15
+ console.log('Aborted whilst creating index');
16
16
  process.exit(0);
17
17
  } else {
18
18
  console.error('Index creation failed: ' + e.message);
@@ -1,6 +1,5 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { suite, test } from 'node:test';
3
- import { ErrnoError } from '../../dist/index.js';
4
3
  import { fs } from '../common.js';
5
4
 
6
5
  const testDir = 'test-dir';
@@ -25,15 +24,10 @@ suite('Directories', () => {
25
24
  await assert.rejects(fs.promises.mkdir('/one', 0o755), /EEXIST/);
26
25
  });
27
26
 
28
- test('mkdirSync', () => fs.mkdirSync('/two', 0o000));
27
+ test('mkdirSync', async () => await fs.promises.mkdir('/two', 0o000));
29
28
 
30
29
  test('mkdir, nested', async () => {
31
- try {
32
- await fs.promises.mkdir('/nested/dir');
33
- } catch (error: any) {
34
- assert(error instanceof ErrnoError);
35
- assert.equal(error.code, 'ENOENT');
36
- }
30
+ assert.rejects(fs.promises.mkdir('/nested/dir'), { code: 'ENOENT', path: '/nested' });
37
31
  assert(!(await fs.promises.exists('/nested/dir')));
38
32
  });
39
33
 
@@ -61,69 +55,27 @@ suite('Directories', () => {
61
55
  assert.equal(fs.statSync('/recursiveS/A/B/C/D').mode, fs.constants.S_IFDIR | 0o777);
62
56
  });
63
57
 
64
- test('readdirSync without permission', () => {
65
- try {
66
- fs.readdirSync('/two');
67
- } catch (error: any) {
68
- assert(error instanceof ErrnoError);
69
- assert.equal(error.code, 'EACCES');
70
- }
71
- });
72
-
73
58
  test('rmdir (non-empty)', async () => {
74
59
  await fs.promises.mkdir('/rmdirTest');
75
60
  await fs.promises.mkdir('/rmdirTest/rmdirTest2');
76
61
 
77
- try {
78
- await fs.promises.rmdir('/rmdirTest');
79
- } catch (error: any) {
80
- assert(error instanceof ErrnoError);
81
- assert.equal(error.code, 'ENOTEMPTY');
82
- }
62
+ assert.rejects(fs.promises.rmdir('/rmdirTest'), { code: 'ENOTEMPTY' });
83
63
  });
84
64
 
85
65
  test('readdirSync on file', () => {
86
- let wasThrown = false;
87
-
88
- try {
89
- fs.readdirSync('a.js');
90
- } catch (error: any) {
91
- assert(error instanceof ErrnoError);
92
- wasThrown = true;
93
- assert.equal(error.code, 'ENOTDIR');
94
- }
95
- assert(wasThrown);
66
+ assert.throws(() => fs.readdirSync('a.js'), { code: 'ENOTDIR' });
96
67
  });
97
68
 
98
- test('readdir on file', async () => {
99
- try {
100
- await fs.promises.readdir('a.js');
101
- } catch (error: any) {
102
- assert(error instanceof ErrnoError);
103
- assert.equal(error.code, 'ENOTDIR');
104
- }
69
+ test('readdir on file', () => {
70
+ assert.rejects(fs.promises.readdir('a.js'), { code: 'ENOTDIR' });
105
71
  });
106
72
 
107
73
  test('readdirSync on non-existant directory', () => {
108
- let wasThrown = false;
109
-
110
- try {
111
- fs.readdirSync('/does/not/exist');
112
- } catch (error: any) {
113
- assert(error instanceof ErrnoError);
114
- wasThrown = true;
115
- assert.equal(error.code, 'ENOENT');
116
- }
117
- assert(wasThrown);
118
- });
119
-
120
- test('readdir on non-existant directory', async () => {
121
- try {
122
- await fs.promises.readdir('/does/not/exist');
123
- } catch (error: any) {
124
- assert(error instanceof ErrnoError);
125
- assert.equal(error.code, 'ENOENT');
126
- }
74
+ assert.throws(() => fs.readdirSync('/does/not/exist'), { code: 'ENOENT' });
75
+ });
76
+
77
+ test('readdir on non-existant directory', () => {
78
+ assert.rejects(fs.promises.readdir('/does/not/exist'), { code: 'ENOENT' });
127
79
  });
128
80
 
129
81
  test('rm recursively asynchronously', async () => {
@@ -12,7 +12,7 @@ suite('Error messages', () => {
12
12
  assert.equal(error.bufferSize(), 4 + JSON.stringify(error.toJSON()).length);
13
13
  });
14
14
 
15
- const missing = { path };
15
+ const missing = { path, code: 'ENOENT' };
16
16
  const existing = { path: existingFile };
17
17
 
18
18
  test('stat', () => assert.rejects(() => fs.promises.stat(path), missing));
@@ -38,13 +38,9 @@ suite('Stats', () => {
38
38
  test('hasAccess for non-root access', () => {
39
39
  const newFile = 'new.txt';
40
40
 
41
- fs.writeFileSync(newFile, 'hello', {
42
- mode: 0o640, // allow group access
43
- });
41
+ fs.writeFileSync(newFile, 'hello', { mode: 0o640 });
44
42
 
45
- const prevCredentials = {
46
- ...credentials,
47
- };
43
+ const prevCredentials = { ...credentials };
48
44
  const uid = 33;
49
45
  const nonRootCredentials = {
50
46
  uid,