@zenfs/core 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/backends/backend.d.ts +1 -1
  2. package/dist/backends/fetch.d.ts +1 -1
  3. package/dist/backends/index/fs.d.ts +2 -1
  4. package/dist/backends/index/index.d.ts +2 -1
  5. package/dist/backends/overlay.d.ts +4 -3
  6. package/dist/backends/overlay.js +1 -1
  7. package/dist/backends/port/fs.d.ts +5 -1
  8. package/dist/backends/port/fs.js +13 -10
  9. package/dist/browser.min.js +4 -8
  10. package/dist/browser.min.js.map +3 -3
  11. package/dist/config.d.ts +1 -1
  12. package/dist/config.js +1 -1
  13. package/dist/cred.d.ts +2 -1
  14. package/dist/emulation/async.d.ts +6 -5
  15. package/dist/emulation/index.d.ts +1 -1
  16. package/dist/emulation/index.js +1 -1
  17. package/dist/emulation/promises.d.ts +5 -5
  18. package/dist/emulation/promises.js +31 -51
  19. package/dist/emulation/shared.d.ts +8 -2
  20. package/dist/emulation/shared.js +18 -1
  21. package/dist/emulation/streams.d.ts +1 -1
  22. package/dist/emulation/sync.d.ts +5 -5
  23. package/dist/emulation/sync.js +94 -76
  24. package/dist/error.d.ts +1 -1
  25. package/dist/error.js +1 -1
  26. package/dist/file.d.ts +7 -23
  27. package/dist/file.js +33 -80
  28. package/dist/filesystem.d.ts +35 -31
  29. package/dist/filesystem.js +10 -14
  30. package/dist/stats.d.ts +17 -17
  31. package/dist/stats.js +42 -49
  32. package/dist/utils.d.ts +2 -2
  33. package/license.md +2 -26
  34. package/package.json +2 -2
  35. package/readme.md +1 -1
  36. package/src/backends/backend.ts +1 -1
  37. package/src/backends/fetch.ts +1 -1
  38. package/src/backends/index/fs.ts +2 -1
  39. package/src/backends/index/index.ts +2 -1
  40. package/src/backends/overlay.ts +7 -4
  41. package/src/backends/port/fs.ts +14 -10
  42. package/src/config.ts +1 -1
  43. package/src/cred.ts +2 -1
  44. package/src/emulation/async.ts +8 -7
  45. package/src/emulation/index.ts +1 -1
  46. package/src/emulation/promises.ts +40 -54
  47. package/src/emulation/shared.ts +24 -3
  48. package/src/emulation/streams.ts +1 -1
  49. package/src/emulation/sync.ts +100 -87
  50. package/src/error.ts +1 -1
  51. package/src/file.ts +35 -88
  52. package/src/filesystem.ts +40 -32
  53. package/src/stats.ts +47 -59
  54. package/src/utils.ts +2 -2
package/dist/utils.d.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  import type { OptionalTuple } from 'utilium';
4
4
  import { ErrnoError } from './error.js';
5
- import { Cred } from './cred.js';
5
+ import type { Cred } from './cred.js';
6
6
  import { type AbsolutePath } from './emulation/path.js';
7
- import { FileSystem } from './filesystem.js';
7
+ import type { FileSystem } from './filesystem.js';
8
8
  import type * as fs from 'node:fs';
9
9
  declare global {
10
10
  function atob(data: string): string;
package/license.md CHANGED
@@ -1,6 +1,6 @@
1
- Copyright (c) 2023-2024 James P. and other ZenFS contributors.
2
-
1
+ Copyright (c) James P. and other ZenFS contributors.
3
2
  Copyright (c) 2013-2023 John Vilk and other BrowserFS contributors.
3
+ Copyright (c) Joyent, Inc. and other Node contributors for `test/fixtures/node`.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
@@ -19,27 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
-
23
- This license applies to all parts of ZenFS, except for the following items:
24
-
25
- - The test fixtures located in `test/fixtures/node`. Their license follows:
26
- """
27
- Copyright Joyent, Inc. and other Node contributors. All rights reserved.
28
- Permission is hereby granted, free of charge, to any person obtaining a copy
29
- of this software and associated documentation files (the "Software"), to
30
- deal in the Software without restriction, including without limitation the
31
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
32
- sell copies of the Software, and to permit persons to whom the Software is
33
- furnished to do so, subject to the following conditions:
34
-
35
- The above copyright notice and this permission notice shall be included in
36
- all copies or substantial portions of the Software.
37
-
38
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
43
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
44
- IN THE SOFTWARE.
45
- """
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "A filesystem in your browser",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.ts",
@@ -38,7 +38,7 @@
38
38
  "scripts": {
39
39
  "format": "prettier --write .",
40
40
  "format:check": "prettier --check .",
41
- "lint": "eslint src tests && tsc -p tsconfig.json --noEmit",
41
+ "lint": "tsc -p tsconfig.json --noEmit && eslint src tests",
42
42
  "test": "npm run build && tsc -p tests/tsconfig.json --noEmit && cross-env NODE_OPTIONS=--experimental-vm-modules npx jest",
43
43
  "build": "node scripts/build.js --globalName=ZenFS --entry src/index.ts",
44
44
  "build:docs": "typedoc --out docs --name ZenFS src/index.ts",
package/readme.md CHANGED
@@ -4,7 +4,7 @@ ZenFS is a file system that emulates the [NodeJS filesystem API](http://nodejs.o
4
4
 
5
5
  It works using a system of backends, which are used by ZenFS to store and retrieve data. ZenFS can also integrate with other tools.
6
6
 
7
- ZenFS is a fork of [BrowserFS](https://github.com/jvilk/BrowserFS). If you are using ZenFS in a research paper, you may want to [cite BrowserFS](https://github.com/jvilk/BrowserFS?tab=readme-ov-file#citing).
7
+ ZenFS is a fork of [BrowserFS](https://github.com/jvilk/BrowserFS). If you are using ZenFS in a research paper, you may want to [cite BrowserFS](https://github.com/jvilk/BrowserFS#citing).
8
8
 
9
9
  ## Backends
10
10
 
@@ -1,6 +1,6 @@
1
1
  import type { RequiredKeys } from 'utilium';
2
2
  import { ErrnoError, Errno } from '../error.js';
3
- import { FileSystem } from '../filesystem.js';
3
+ import type { FileSystem } from '../filesystem.js';
4
4
  import { levenshtein } from '../utils.js';
5
5
 
6
6
  type OptionType = 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function';
@@ -1,6 +1,6 @@
1
1
  import { Errno, ErrnoError } from '../error.js';
2
2
  import type { FileSystemMetadata } from '../filesystem.js';
3
- import { Stats } from '../stats.js';
3
+ import type { Stats } from '../stats.js';
4
4
  import type { Backend } from './backend.js';
5
5
  import { IndexFS } from './index/fs.js';
6
6
  import type { IndexData } from './index/index.js';
@@ -4,7 +4,8 @@ import { NoSyncFile, isWriteable, flagToMode } from '../../file.js';
4
4
  import { Readonly, FileSystem } from '../../filesystem.js';
5
5
  import type { Stats } from '../../stats.js';
6
6
  import { decode } from '../../utils.js';
7
- import { Index, IndexData } from './index.js';
7
+ import type { IndexData } from './index.js';
8
+ import { Index } from './index.js';
8
9
 
9
10
  export abstract class IndexFS extends Readonly(FileSystem) {
10
11
  protected index: Index = new Index();
@@ -1,6 +1,7 @@
1
1
  import { isJSON } from 'utilium';
2
2
  import { Errno, ErrnoError } from '../../error.js';
3
- import { Stats, StatsLike } from '../../stats.js';
3
+ import type { StatsLike } from '../../stats.js';
4
+ import { Stats } from '../../stats.js';
4
5
  import { encode } from '../../utils.js';
5
6
  import { basename, dirname } from '../../emulation/path.js';
6
7
 
@@ -1,10 +1,13 @@
1
- import { FileSystem, FileSystemMetadata } from '../filesystem.js';
1
+ import type { FileSystemMetadata } from '../filesystem.js';
2
+ import { FileSystem } from '../filesystem.js';
2
3
  import { ErrnoError, Errno } from '../error.js';
3
- import { File, PreloadFile, parseFlag } from '../file.js';
4
+ import type { File } from '../file.js';
5
+ import { PreloadFile, parseFlag } from '../file.js';
4
6
  import { Stats } from '../stats.js';
5
7
  import { LockedFS } from './locked.js';
6
8
  import { dirname } from '../emulation/path.js';
7
- import { Cred, rootCred } from '../cred.js';
9
+ import type { Cred } from '../cred.js';
10
+ import { rootCred } from '../cred.js';
8
11
  import { decode, encode } from '../utils.js';
9
12
  import type { Backend } from './backend.js';
10
13
  /**
@@ -201,7 +204,7 @@ export class UnlockedOverlayFS extends FileSystem {
201
204
  }
202
205
  // Create an OverlayFile.
203
206
  const file = this._readable.openFileSync(path, parseFlag('r'), cred);
204
- const stats = Stats.clone(file.statSync());
207
+ const stats = new Stats(file.statSync());
205
208
  const data = new Uint8Array(stats.size);
206
209
  file.readSync(data);
207
210
  return new PreloadFile(this, path, flag, stats, data);
@@ -38,12 +38,16 @@ export class PortFile extends File {
38
38
  );
39
39
  }
40
40
 
41
+ protected _throwNoSync(syscall: string): never {
42
+ throw new ErrnoError(Errno.ENOTSUP, 'Syncrohnous operations not support on PortFile', this.path, syscall);
43
+ }
44
+
41
45
  public stat(): Promise<Stats> {
42
46
  return this.rpc('stat');
43
47
  }
44
48
 
45
49
  public statSync(): Stats {
46
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.stat');
50
+ this._throwNoSync('stat');
47
51
  }
48
52
 
49
53
  public truncate(len: number): Promise<void> {
@@ -51,7 +55,7 @@ export class PortFile extends File {
51
55
  }
52
56
 
53
57
  public truncateSync(): void {
54
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.truncate');
58
+ this._throwNoSync('truncate');
55
59
  }
56
60
 
57
61
  public write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number> {
@@ -59,7 +63,7 @@ export class PortFile extends File {
59
63
  }
60
64
 
61
65
  public writeSync(): number {
62
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.write');
66
+ this._throwNoSync('write');
63
67
  }
64
68
 
65
69
  public async read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>> {
@@ -68,7 +72,7 @@ export class PortFile extends File {
68
72
  }
69
73
 
70
74
  public readSync(): number {
71
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.read');
75
+ this._throwNoSync('read');
72
76
  }
73
77
 
74
78
  public chown(uid: number, gid: number): Promise<void> {
@@ -76,7 +80,7 @@ export class PortFile extends File {
76
80
  }
77
81
 
78
82
  public chownSync(): void {
79
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.chown');
83
+ this._throwNoSync('chown');
80
84
  }
81
85
 
82
86
  public chmod(mode: number): Promise<void> {
@@ -84,7 +88,7 @@ export class PortFile extends File {
84
88
  }
85
89
 
86
90
  public chmodSync(): void {
87
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.chmod');
91
+ this._throwNoSync('chmod');
88
92
  }
89
93
 
90
94
  public utimes(atime: Date, mtime: Date): Promise<void> {
@@ -92,7 +96,7 @@ export class PortFile extends File {
92
96
  }
93
97
 
94
98
  public utimesSync(): void {
95
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.utimes');
99
+ this._throwNoSync('utimes');
96
100
  }
97
101
 
98
102
  public _setType(type: FileType): Promise<void> {
@@ -100,7 +104,7 @@ export class PortFile extends File {
100
104
  }
101
105
 
102
106
  public _setTypeSync(): void {
103
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile._setType');
107
+ this._throwNoSync('_setType');
104
108
  }
105
109
 
106
110
  public close(): Promise<void> {
@@ -108,7 +112,7 @@ export class PortFile extends File {
108
112
  }
109
113
 
110
114
  public closeSync(): void {
111
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.close');
115
+ this._throwNoSync('close');
112
116
  }
113
117
 
114
118
  public sync(): Promise<void> {
@@ -116,7 +120,7 @@ export class PortFile extends File {
116
120
  }
117
121
 
118
122
  public syncSync(): void {
119
- throw ErrnoError.With('ENOSYS', this.path, 'PortFile.sync');
123
+ this._throwNoSync('sync');
120
124
  }
121
125
  }
122
126
 
package/src/config.ts CHANGED
@@ -18,7 +18,7 @@ function isMountConfig<T extends Backend>(arg: unknown): arg is MountConfigurati
18
18
 
19
19
  /**
20
20
  * Retrieve a file system with the given configuration.
21
- * @param config A BackendConfig object.
21
+ * @see MountConfiguration
22
22
  */
23
23
  export async function resolveMountConfig<T extends Backend>(config: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
24
24
  if (typeof config !== 'object' || config == null) {
package/src/cred.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Credentials used for various operations.
3
- * Similar to Linux's cred struct. See https://github.com/torvalds/linux/blob/master/include/linux/cred.h
3
+ * Similar to Linux's cred struct.
4
+ * @see https://github.com/torvalds/linux/blob/master/include/linux/cred.h
4
5
  */
5
6
  export interface Cred {
6
7
  uid: number;
@@ -2,10 +2,11 @@ import { Buffer } from 'buffer';
2
2
  import type * as fs from 'node:fs';
3
3
  import { ErrnoError, Errno } from '../error.js';
4
4
  import type { FileContents } from '../filesystem.js';
5
- import { BigIntStats, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
5
+ import { BigIntStats, type Stats } from '../stats.js';
6
6
  import { nop, normalizeMode, type Callback } from '../utils.js';
7
7
  import { R_OK } from './constants.js';
8
- import { Dirent, type Dir } from './dir.js';
8
+ import type { Dirent } from './dir.js';
9
+ import { type Dir } from './dir.js';
9
10
  import * as promises from './promises.js';
10
11
  import { fd2file } from './shared.js';
11
12
  import { ReadStream, WriteStream } from './streams.js';
@@ -882,14 +883,14 @@ export function cp(source: fs.PathLike, destination: fs.PathLike, opts: fs.CopyO
882
883
  }
883
884
  cp satisfies Omit<typeof fs.cp, '__promisify__'>;
884
885
 
885
- export function statfs(path: fs.PathLike, callback: Callback<[StatsFs]>): void;
886
- export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint?: false }, callback: Callback<[StatsFs]>): void;
887
- export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint: true }, callback: Callback<[BigIntStatsFs]>): void;
888
- export function statfs(path: fs.PathLike, options?: fs.StatFsOptions | Callback<[StatsFs]>, callback: Callback<[StatsFs]> | Callback<[BigIntStatsFs]> = nop): void {
886
+ export function statfs(path: fs.PathLike, callback: Callback<[fs.StatsFs]>): void;
887
+ export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint?: false }, callback: Callback<[fs.StatsFs]>): void;
888
+ export function statfs(path: fs.PathLike, options: fs.StatFsOptions & { bigint: true }, callback: Callback<[fs.BigIntStatsFs]>): void;
889
+ export function statfs(path: fs.PathLike, options?: fs.StatFsOptions | Callback<[fs.StatsFs]>, callback: Callback<[fs.StatsFs]> | Callback<[fs.BigIntStatsFs]> = nop): void {
889
890
  callback = typeof options === 'function' ? options : callback;
890
891
  promises
891
892
  .statfs(path, typeof options === 'function' ? undefined : options)
892
- .then(result => (callback as Callback<[StatsFs | BigIntStatsFs]>)(undefined, result))
893
+ .then(result => (callback as Callback<[fs.StatsFs | fs.BigIntStatsFs]>)(undefined, result))
893
894
  .catch(callback);
894
895
  }
895
896
  statfs satisfies Omit<typeof fs.statfs, '__promisify__'>;
@@ -5,4 +5,4 @@ export * as constants from './constants.js';
5
5
  export * from './streams.js';
6
6
  export * from './dir.js';
7
7
  export { mountObject, mounts, mount, umount } from './shared.js';
8
- export { Stats, BigIntStats, StatsFs } from '../stats.js';
8
+ export { Stats, StatsFs, BigIntStatsFs } from '../stats.js';
@@ -6,15 +6,16 @@ import type { Stream } from 'node:stream';
6
6
  import type { ReadableStream as TReadableStream } from 'node:stream/web';
7
7
  import type { Interface as ReadlineInterface } from 'readline';
8
8
  import type { ReadableStreamController } from 'stream/web';
9
- import { ErrnoError, Errno } from '../error.js';
10
- import { ActionType, File, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
9
+ import { Errno, ErrnoError } from '../error.js';
10
+ import type { File } from '../file.js';
11
+ import { isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
11
12
  import type { FileContents } from '../filesystem.js';
12
- import { BigIntStats, FileType, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
13
+ import { BigIntStats, FileType, type Stats } from '../stats.js';
13
14
  import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
14
15
  import * as constants from './constants.js';
15
16
  import { Dir, Dirent } from './dir.js';
16
17
  import { dirname, join, parse } from './path.js';
17
- import { cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
18
+ import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
18
19
  import { ReadStream, WriteStream } from './streams.js';
19
20
  export * as constants from './constants.js';
20
21
 
@@ -110,7 +111,7 @@ export class FileHandle implements promises.FileHandle {
110
111
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
111
112
  }
112
113
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : data;
113
- await this.file.write(encodedData, 0, encodedData.length, null);
114
+ await this.file.write(encodedData, 0, encodedData.length);
114
115
  }
115
116
 
116
117
  /**
@@ -491,53 +492,36 @@ async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o6
491
492
  path = resolveSymlinks && (await exists(path)) ? await realpath(path) : path;
492
493
  const { fs, path: resolved } = resolveMount(path);
493
494
 
494
- try {
495
- switch (pathExistsAction(flag)) {
496
- case ActionType.THROW:
497
- throw ErrnoError.With('EEXIST', path, '_open');
498
- case ActionType.TRUNCATE:
499
- /*
500
- In a previous implementation, we deleted the file and
501
- re-created it. However, this created a race condition if another
502
- asynchronous request was trying to read the file, as the file
503
- would not exist for a small period of time.
504
- */
505
- const file: File = await fs.openFile(resolved, flag, cred);
506
- await file.truncate(0);
507
- await file.sync();
508
- return new FileHandle(file);
509
- case ActionType.NOP:
510
- // Must await so thrown errors are caught by the catch below
511
- return new FileHandle(await fs.openFile(resolved, flag, cred));
512
- default:
513
- throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
495
+ if (!(await fs.exists(path, cred))) {
496
+ if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
497
+ throw ErrnoError.With('ENOENT', path, '_open');
514
498
  }
515
- } catch (_) {
516
- const original = _ as ErrnoError;
517
- if (original.code != 'ENOENT') {
518
- throw original;
519
- }
520
- try {
521
- switch (pathNotExistsAction(flag)) {
522
- case ActionType.CREATE:
523
- // Ensure parent exists.
524
- const parentStats: Stats = await fs.stat(dirname(resolved), cred);
525
- if (parentStats && !parentStats.isDirectory()) {
526
- throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
527
- }
528
- return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
529
- case ActionType.THROW:
530
- throw ErrnoError.With('ENOENT', path, '_open');
531
- default:
532
- throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
533
- }
534
- } catch (_) {
535
- const ex = _ as ErrnoError;
536
- ex.stack += '\n<original>\n';
537
- ex.stack += (original as Error).stack;
538
- throw ex;
499
+ // Create the file
500
+ const parentStats: Stats = await fs.stat(dirname(resolved), cred);
501
+ if (parentStats && !parentStats.isDirectory()) {
502
+ throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
539
503
  }
504
+ return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
505
+ }
506
+
507
+ if (isExclusive(flag)) {
508
+ throw ErrnoError.With('EEXIST', path, '_open');
509
+ }
510
+
511
+ if (!isTruncating(flag)) {
512
+ return new FileHandle(await fs.openFile(resolved, flag, cred));
540
513
  }
514
+
515
+ /*
516
+ In a previous implementation, we deleted the file and
517
+ re-created it. However, this created a race condition if another
518
+ asynchronous request was trying to read the file, as the file
519
+ would not exist for a small period of time.
520
+ */
521
+ const file: File = await fs.openFile(resolved, flag, cred);
522
+ await file.truncate(0);
523
+ await file.sync();
524
+ return new FileHandle(file);
541
525
  }
542
526
 
543
527
  /**
@@ -1072,9 +1056,11 @@ cp satisfies typeof promises.cp;
1072
1056
  * @since v18.15.0
1073
1057
  * @return Fulfills with an {fs.StatFs} for the file system.
1074
1058
  */
1075
- export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions & { bigint?: false }): Promise<StatsFs>;
1076
- export async function statfs(path: fs.PathLike, opts: fs.StatFsOptions & { bigint: true }): Promise<BigIntStatsFs>;
1077
- export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<StatsFs | BigIntStatsFs>;
1078
- export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<StatsFs | BigIntStatsFs> {
1079
- throw ErrnoError.With('ENOSYS', path.toString(), 'statfs');
1059
+ export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions & { bigint?: false }): Promise<fs.StatsFs>;
1060
+ export async function statfs(path: fs.PathLike, opts: fs.StatFsOptions & { bigint: true }): Promise<fs.BigIntStatsFs>;
1061
+ export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs>;
1062
+ export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs> {
1063
+ path = normalizePath(path);
1064
+ const { fs } = resolveMount(path);
1065
+ return _statfs(fs, opts?.bigint);
1080
1066
  }
@@ -1,10 +1,13 @@
1
1
  // Utilities and shared data
2
2
 
3
- import { ErrnoError, Errno } from '../error.js';
3
+ import type { BigIntStatsFs, StatsFs } from 'node:fs';
4
4
  import { InMemory } from '../backends/memory.js';
5
- import { Cred, rootCred } from '../cred.js';
5
+ import type { Cred } from '../cred.js';
6
+ import { rootCred } from '../cred.js';
7
+ import { Errno, ErrnoError } from '../error.js';
6
8
  import type { File } from '../file.js';
7
- import { FileSystem } from '../filesystem.js';
9
+ import type { FileSystem } from '../filesystem.js';
10
+ import { size_max } from '../inode.js';
8
11
  import { normalizePath } from '../utils.js';
9
12
  import { resolve, type AbsolutePath } from './path.js';
10
13
 
@@ -117,3 +120,21 @@ export function mountObject(mounts: MountObject): void {
117
120
  mount(point, fs);
118
121
  }
119
122
  }
123
+
124
+ /**
125
+ * @hidden
126
+ */
127
+ export function _statfs<const T extends boolean>(fs: FileSystem, bigint?: T): T extends true ? BigIntStatsFs : StatsFs {
128
+ const md = fs.metadata();
129
+ const bs = md.blockSize || 4096;
130
+
131
+ return {
132
+ type: (bigint ? BigInt : Number)(md.type),
133
+ bsize: (bigint ? BigInt : Number)(bs),
134
+ ffree: (bigint ? BigInt : Number)(md.freeNodes || size_max),
135
+ files: (bigint ? BigInt : Number)(md.totalNodes || size_max),
136
+ bavail: (bigint ? BigInt : Number)(md.freeSpace / bs),
137
+ bfree: (bigint ? BigInt : Number)(md.freeSpace / bs),
138
+ blocks: (bigint ? BigInt : Number)(md.totalSpace / bs),
139
+ } as T extends true ? BigIntStatsFs : StatsFs;
140
+ }
@@ -1,6 +1,6 @@
1
1
  import type * as Node from 'fs';
2
2
  import { Readable, Writable } from 'readable-stream';
3
- import { Callback } from '../utils.js';
3
+ import type { Callback } from '../utils.js';
4
4
  import { ErrnoError, Errno } from '../error.js';
5
5
 
6
6
  export class ReadStream extends Readable implements Node.ReadStream {