◐ Shell
clean mode source ↗

fs: add signal option to fs.stat() · nodejs/node@9705f62

Original file line numberDiff line numberDiff line change

@@ -630,15 +630,17 @@ Read from a file and write to an array of {ArrayBufferView}s

630630

<!-- YAML

631631

added: v10.0.0

632632

changes:

633+

- version: REPLACEME

634+

pr-url: https://github.com/nodejs/node/pull/57775

635+

description: Now accepts an additional `signal` property to allow aborting the operation.

633636

- version: v10.5.0

634637

pr-url: https://github.com/nodejs/node/pull/20220

635-

description: Accepts an additional `options` object to specify whether

636-

the numeric values returned should be bigint.

638+

description: Accepts an additional `options` object to specify whether the numeric values returned should be bigint.

637639

-->

638640
639641

* `options` {Object}

640-

* `bigint` {boolean} Whether the numeric values in the returned

641-

{fs.Stats} object should be `bigint`. **Default:** `false`.

642+

* `bigint` {boolean} Whether the numeric values in the returned {fs.Stats} object should be `bigint`. **Default:** `false`.

643+

* `signal` {AbortSignal} An AbortSignal to cancel the operation. **Default:** `undefined`.

642644

* Returns: {Promise} Fulfills with an {fs.Stats} for the file.

643645
644646

#### `filehandle.sync()`

Original file line numberDiff line numberDiff line change

@@ -1631,7 +1631,7 @@ function lstat(path, options = { bigint: false }, callback) {

16311631

/**

16321632

* Asynchronously gets the stats of a file.

16331633

* @param {string | Buffer | URL} path

1634-

* @param {{ bigint?: boolean; }} [options]

1634+

* @param {{ bigint?: boolean, signal?: AbortSignal }} [options]

16351635

* @param {(

16361636

* err?: Error,

16371637

* stats?: Stats

@@ -1642,8 +1642,16 @@ function stat(path, options = { bigint: false, throwIfNoEntry: true }, callback)

16421642

if (typeof options === 'function') {

16431643

callback = options;

16441644

options = kEmptyObject;

1645+

} else if (options === null || typeof options !== 'object') {

1646+

options = kEmptyObject;

1647+

} else {

1648+

options = getOptions(options, { bigint: false });

16451649

}

1650+
16461651

callback = makeStatsCallback(callback);

1652+

path = getValidatedPath(path);

1653+
1654+

if (checkAborted(options.signal, callback)) return;

16471655
16481656

const req = new FSReqCallback(options.bigint);

16491657

req.oncomplete = callback;

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,34 @@

1+

'use strict';

2+
3+

require('../common');

4+

const test = require('node:test');

5+

const assert = require('node:assert');

6+

const fs = require('node:fs');

7+

const tmpdir = require('../common/tmpdir');

8+
9+

test('fs.stat should throw AbortError when called with an already aborted AbortSignal', async () => {

10+

// This test verifies that fs.stat immediately throws an AbortError if the provided AbortSignal

11+

// has already been canceled. This approach is used because trying to abort an fs.stat call in flight

12+

// is unreliable given that file system operations tend to complete very quickly on many platforms.

13+

tmpdir.refresh();

14+
15+

const filePath = tmpdir.resolve('temp.txt');

16+

fs.writeFileSync(filePath, 'Test');

17+
18+

// Create an already aborted AbortSignal.

19+

const signal = AbortSignal.abort();

20+
21+

const { promise, resolve, reject } = Promise.withResolvers();

22+

fs.stat(filePath, { signal }, (err, stats) => {

23+

if (err) {

24+

return reject(err);

25+

}

26+

resolve(stats);

27+

});

28+
29+

// Assert that the promise is rejected with an AbortError.

30+

await assert.rejects(promise, { name: 'AbortError' });

31+
32+

fs.unlinkSync(filePath);

33+

tmpdir.refresh();

34+

});