@gjsify/fs 0.0.4 → 0.1.1

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 (91) hide show
  1. package/README.md +31 -2
  2. package/lib/esm/callback.js +251 -15
  3. package/lib/esm/dirent.js +47 -6
  4. package/lib/esm/encoding.js +2 -3
  5. package/lib/esm/errors.js +13 -0
  6. package/lib/esm/file-handle.js +108 -66
  7. package/lib/esm/fs-watcher.js +44 -7
  8. package/lib/esm/index.js +140 -5
  9. package/lib/esm/promises.js +290 -69
  10. package/lib/esm/read-stream.js +82 -57
  11. package/lib/esm/stats.js +138 -18
  12. package/lib/esm/sync.js +293 -44
  13. package/lib/esm/write-stream.js +4 -4
  14. package/lib/types/callback.d.ts +233 -0
  15. package/lib/types/dirent.d.ts +77 -0
  16. package/lib/types/encoding.d.ts +6 -0
  17. package/lib/types/errors.d.ts +7 -0
  18. package/lib/types/file-handle.d.ts +367 -0
  19. package/lib/types/fs-watcher.d.ts +17 -0
  20. package/lib/types/index.d.ts +149 -0
  21. package/lib/types/promises.d.ts +158 -0
  22. package/lib/types/read-stream.d.ts +21 -0
  23. package/lib/types/stats.d.ts +67 -0
  24. package/lib/types/sync.d.ts +109 -0
  25. package/lib/types/types/encoding-option.d.ts +2 -0
  26. package/lib/types/types/file-read-options.d.ts +15 -0
  27. package/lib/types/types/file-read-result.d.ts +4 -0
  28. package/lib/types/types/flag-and-open-mode.d.ts +5 -0
  29. package/lib/types/types/index.d.ts +6 -0
  30. package/lib/types/types/open-flags.d.ts +1 -0
  31. package/lib/types/types/read-options.d.ts +5 -0
  32. package/lib/types/utils.d.ts +2 -0
  33. package/lib/types/write-stream.d.ts +45 -0
  34. package/package.json +22 -35
  35. package/src/callback.spec.ts +284 -30
  36. package/src/callback.ts +352 -39
  37. package/src/dirent.ts +56 -8
  38. package/src/encoding.ts +7 -2
  39. package/src/errors.spec.ts +389 -0
  40. package/src/errors.ts +19 -0
  41. package/src/extended.spec.ts +706 -0
  42. package/src/file-handle.spec.ts +104 -23
  43. package/src/file-handle.ts +147 -79
  44. package/src/fs-watcher.ts +55 -8
  45. package/src/index.ts +146 -2
  46. package/src/new-apis.spec.ts +505 -0
  47. package/src/promises.spec.ts +651 -11
  48. package/src/promises.ts +353 -81
  49. package/src/read-stream.ts +98 -74
  50. package/src/stat.spec.ts +22 -14
  51. package/src/stats.ts +176 -75
  52. package/src/streams.spec.ts +455 -0
  53. package/src/symlink.spec.ts +176 -26
  54. package/src/sync.spec.ts +204 -32
  55. package/src/sync.ts +363 -58
  56. package/src/test.mts +7 -2
  57. package/src/types/encoding-option.ts +1 -1
  58. package/src/types/flag-and-open-mode.ts +1 -1
  59. package/src/types/read-options.ts +2 -2
  60. package/src/utils.ts +2 -0
  61. package/src/write-stream.ts +9 -7
  62. package/tsconfig.json +23 -10
  63. package/tsconfig.tsbuildinfo +1 -0
  64. package/lib/cjs/callback.js +0 -112
  65. package/lib/cjs/dirent.js +0 -98
  66. package/lib/cjs/encoding.js +0 -34
  67. package/lib/cjs/file-handle.js +0 -444
  68. package/lib/cjs/fs-watcher.js +0 -50
  69. package/lib/cjs/index.js +0 -95
  70. package/lib/cjs/promises.js +0 -160
  71. package/lib/cjs/read-stream.js +0 -78
  72. package/lib/cjs/stats.js +0 -45
  73. package/lib/cjs/sync.js +0 -126
  74. package/lib/cjs/types/encoding-option.js +0 -0
  75. package/lib/cjs/types/file-read-options.js +0 -0
  76. package/lib/cjs/types/file-read-result.js +0 -0
  77. package/lib/cjs/types/flag-and-open-mode.js +0 -0
  78. package/lib/cjs/types/index.js +0 -6
  79. package/lib/cjs/types/open-flags.js +0 -0
  80. package/lib/cjs/types/read-options.js +0 -0
  81. package/lib/cjs/utils.js +0 -18
  82. package/lib/cjs/write-stream.js +0 -116
  83. package/test/watch.js +0 -1
  84. package/test.gjs.js +0 -35359
  85. package/test.gjs.js.map +0 -7
  86. package/test.gjs.mjs +0 -40534
  87. package/test.gjs.mjs.meta.json +0 -1
  88. package/test.node.js +0 -1479
  89. package/test.node.js.map +0 -7
  90. package/test.node.mjs +0 -710
  91. package/tsconfig.types.json +0 -8
@@ -1,92 +1,116 @@
1
- // Credits https://github.com/denoland/deno_std/blob/main/node/_fs/_fs_streams.ts
1
+ // SPDX-License-Identifier: MIT
2
+ // Adapted from Deno (refs/deno/ext/node/polyfills/_fs/_fs_streams.ts)
3
+ // Copyright (c) 2018-2026 the Deno authors. MIT license.
4
+ // Modifications: Rewritten to use Gio.File / Gio.FileInputStream for GJS
5
+ import Gio from '@girs/gio-2.0';
2
6
  import GLib from '@girs/glib-2.0';
3
- import { notImplemented } from "@gjsify/utils";
4
- import { Buffer } from "buffer";
5
- import { Readable } from "stream";
6
- import { URL } from "url";
7
+ import { Buffer } from "node:buffer";
8
+ import { Readable } from "node:stream";
9
+ import { URL, fileURLToPath } from "node:url";
7
10
 
8
- import type { CreateReadStreamOptions } from 'fs/promises'; // Types from @types/node
9
- import type { PathLike, ReadStream as IReadStream } from 'fs'; // Types from @types/node
10
-
11
- /**
12
- * Converts a file URL to a path string.
13
- *
14
- * ```ts
15
- * import { fromFileUrl } from "./posix.ts";
16
- * fromFileUrl("file:///home/foo"); // "/home/foo"
17
- * ```
18
- * @param url of a file URL
19
- * @credits https://github.com/denoland/deno_std/blob/44d05e7a8d445888d989d49eb3e59eee3055f2c5/node/path/posix.ts#L486
20
- */
21
- export function fromFileUrl(url: string | URL): string {
22
- url = url instanceof URL ? url : new URL(url);
23
- if (url.protocol != "file:") {
24
- throw new TypeError("Must be a file URL.");
25
- }
26
- return decodeURIComponent(
27
- url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"),
28
- );
29
- }
11
+ import type { CreateReadStreamOptions } from 'node:fs/promises';
12
+ import type { PathLike, ReadStream as IReadStream } from 'node:fs';
30
13
 
31
14
  export class ReadStream extends Readable implements IReadStream {
15
+ bytesRead = 0;
16
+ path: string | Buffer;
17
+ pending = true;
18
+ fd: number | null = null;
19
+
20
+ private _gioFile: Gio.File;
21
+ private _inputStream: Gio.FileInputStream | null = null;
22
+ private _start: number;
23
+ private _end: number;
24
+ private _pos: number;
25
+
32
26
  close(callback?: (err?: NodeJS.ErrnoException | null) => void): void {
33
- // TODO
34
- callback(notImplemented('ReadStream.close'));
27
+ if (this._inputStream) {
28
+ try { this._inputStream.close(null); } catch {}
29
+ this._inputStream = null;
30
+ }
31
+ this.destroy();
32
+ if (callback) callback(null);
35
33
  }
36
- /**
37
- * The number of bytes that have been read so far.
38
- * @since v6.4.0
39
- */
40
- bytesRead: number;
41
- /**
42
- * The path to the file the stream is reading from as specified in the first
43
- * argument to `fs.createReadStream()`. If `path` is passed as a string, then`readStream.path` will be a string. If `path` is passed as a `Buffer`, then`readStream.path` will be a
44
- * `Buffer`. If `fd` is specified, then`readStream.path` will be `undefined`.
45
- * @since v0.1.93
46
- */
47
- path: string | Buffer;
48
- /**
49
- * This property is `true` if the underlying file has not been opened yet,
50
- * i.e. before the `'ready'` event is emitted.
51
- * @since v11.2.0, v10.16.0
52
- */
53
- pending: boolean;
54
34
 
55
35
  constructor(path: PathLike, opts?: CreateReadStreamOptions) {
56
- path = path instanceof URL ? fromFileUrl(path) : path;
57
- const hasBadOptions = opts && (
58
- opts.start || opts.end
59
- );
60
- if (hasBadOptions) {
61
- notImplemented(
62
- `fs.ReadStream.prototype.constructor with unsupported options (${
63
- JSON.stringify(opts)
64
- })`,
65
- );
36
+ if (path instanceof URL) {
37
+ path = fileURLToPath(path);
66
38
  }
67
- const file = GLib.IOChannel.new_file(path.toString(), "r")
68
- const buffer = "";
39
+
69
40
  super({
70
- autoDestroy: true,
71
- emitClose: true,
41
+ highWaterMark: opts?.highWaterMark ?? 64 * 1024,
42
+ encoding: opts?.encoding as BufferEncoding | undefined,
72
43
  objectMode: false,
73
- read: async function (_size) {
44
+ });
45
+
46
+ this.path = path.toString();
47
+ this._gioFile = Gio.File.new_for_path(this.path.toString());
48
+ this._start = (opts?.start as number) ?? 0;
49
+ this._end = (opts?.end as number) ?? Infinity;
50
+ this._pos = this._start;
51
+
52
+ // Validate file existence eagerly (like Node.js) to emit error event
53
+ Promise.resolve().then(() => {
54
+ if (!this._inputStream && !this.destroyed) {
74
55
  try {
75
- let n = 0
76
- file.read(buffer, 16 * 1024, n);
77
- this.push(n ? Buffer.from(buffer.slice(0, n)) : null);
56
+ this._inputStream = this._gioFile.read(null);
57
+ this.pending = false;
58
+ this.emit('open', 0);
59
+ this.emit('ready');
60
+ if (this._start > 0 && this._inputStream.can_seek()) {
61
+ this._inputStream.seek(this._start, GLib.SeekType.SET, null);
62
+ }
78
63
  } catch (err) {
79
64
  this.destroy(err as Error);
80
65
  }
81
- },
82
- destroy: (err, cb) => {
83
- try {
84
- file.close();
85
- } catch {}
86
- cb(err);
87
- },
66
+ }
88
67
  });
89
- this.path = path.toString();
68
+ }
69
+
70
+ override _read(size: number): void {
71
+ // Stream is opened eagerly in constructor; if not yet ready, wait
72
+ if (!this._inputStream) {
73
+ if (this.destroyed) return;
74
+ // Retry on next tick (constructor's async open hasn't completed yet)
75
+ Promise.resolve().then(() => this._read(size));
76
+ return;
77
+ }
78
+
79
+ // Calculate how many bytes to read
80
+ let toRead = size;
81
+ if (this._end !== Infinity) {
82
+ const remaining = this._end - this._pos + 1;
83
+ if (remaining <= 0) {
84
+ this.push(null);
85
+ return;
86
+ }
87
+ toRead = Math.min(size, remaining);
88
+ }
89
+
90
+ try {
91
+ const gbytes = this._inputStream!.read_bytes(toRead, null);
92
+ const data = gbytes.get_data();
93
+
94
+ if (!data || data.length === 0) {
95
+ // EOF
96
+ this.push(null);
97
+ return;
98
+ }
99
+
100
+ this.bytesRead += data.length;
101
+ this._pos += data.length;
102
+ this.push(Buffer.from(data));
103
+ } catch (err) {
104
+ this.destroy(err as Error);
105
+ }
106
+ }
107
+
108
+ override _destroy(error: Error | null, callback: (error?: Error | null) => void): void {
109
+ if (this._inputStream) {
110
+ try { this._inputStream.close(null); } catch {}
111
+ this._inputStream = null;
112
+ }
113
+ callback(error);
90
114
  }
91
115
  }
92
116
 
@@ -95,4 +119,4 @@ export function createReadStream(
95
119
  options?: CreateReadStreamOptions,
96
120
  ): ReadStream {
97
121
  return new ReadStream(path, options);
98
- }
122
+ }
package/src/stat.spec.ts CHANGED
@@ -1,19 +1,18 @@
1
1
  import { describe, it, expect } from '@gjsify/unit';
2
- // import { join, dirname } from 'path';
3
- // import { fileURLToPath } from "url";
4
-
5
- // const __filename = fileURLToPath(import.meta.url)
6
- // const __dirname = dirname(__filename)
7
-
8
- import { statSync } from 'fs';
9
- import { stat } from 'fs/promises';
2
+ import { statSync, mkdtempSync, writeFileSync, rmSync, rmdirSync } from 'node:fs';
3
+ import { stat } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
10
6
 
11
7
  export default async () => {
12
8
  await describe('fs.statSync', async () => {
13
9
 
14
10
  await it('Should return the file stat', async () => {
11
+ const dir = mkdtempSync(join(tmpdir(), 'fs-stat-'));
12
+ const filePath = join(dir, 'test.txt');
13
+ writeFileSync(filePath, 'stat test data');
15
14
 
16
- const s = statSync('tsconfig.json');
15
+ const s = statSync(filePath);
17
16
 
18
17
  expect(s.atime instanceof Date).toBeTruthy();
19
18
  expect(s.atimeMs).toBeGreaterThan(0);
@@ -24,7 +23,7 @@ export default async () => {
24
23
  expect(s.ctime instanceof Date).toBeTruthy();
25
24
  expect(s.ctimeMs).toBeGreaterThan(0);
26
25
  expect(s.dev).toBeGreaterThan(0);
27
- expect(s.gid).toBeGreaterThan(0);
26
+ expect(s.gid).toBeGreaterThan(-1);
28
27
  expect(s.ino).toBeGreaterThan(0);
29
28
  expect(s.mode).toBeGreaterThan(0);
30
29
  expect(s.mtime instanceof Date).toBeTruthy();
@@ -32,7 +31,7 @@ export default async () => {
32
31
  expect(s.nlink).toBeGreaterThan(0);
33
32
  expect(s.rdev).toBeGreaterThan(-1);
34
33
  expect(s.size).toBeGreaterThan(0);
35
- expect(s.uid).toBeGreaterThan(0);
34
+ expect(s.uid).toBeGreaterThan(-1);
36
35
  expect(s.isBlockDevice()).toBeFalsy();
37
36
  expect(s.isCharacterDevice()).toBeFalsy();
38
37
  expect(s.isDirectory()).toBeFalsy();
@@ -40,14 +39,20 @@ export default async () => {
40
39
  expect(s.isFile()).toBeTruthy();
41
40
  expect(s.isSocket()).toBeFalsy();
42
41
  expect(s.isSymbolicLink()).toBeFalsy();
42
+
43
+ rmSync(filePath);
44
+ rmdirSync(dir);
43
45
  });
44
46
  });
45
47
 
46
48
  await describe('fs.stat (promise)', async () => {
47
49
 
48
50
  await it('Should return the file stat', async () => {
51
+ const dir = mkdtempSync(join(tmpdir(), 'fs-pstat-'));
52
+ const filePath = join(dir, 'test.txt');
53
+ writeFileSync(filePath, 'stat test data');
49
54
 
50
- const s = await stat('tsconfig.json');
55
+ const s = await stat(filePath);
51
56
 
52
57
  expect(s.atime instanceof Date).toBeTruthy();
53
58
  expect(s.atimeMs).toBeGreaterThan(0);
@@ -58,7 +63,7 @@ export default async () => {
58
63
  expect(s.ctime instanceof Date).toBeTruthy();
59
64
  expect(s.ctimeMs).toBeGreaterThan(0);
60
65
  expect(s.dev).toBeGreaterThan(0);
61
- expect(s.gid).toBeGreaterThan(0);
66
+ expect(s.gid).toBeGreaterThan(-1);
62
67
  expect(s.ino).toBeGreaterThan(0);
63
68
  expect(s.mode).toBeGreaterThan(0);
64
69
  expect(s.mtime instanceof Date).toBeTruthy();
@@ -66,7 +71,7 @@ export default async () => {
66
71
  expect(s.nlink).toBeGreaterThan(0);
67
72
  expect(s.rdev).toBeGreaterThan(-1);
68
73
  expect(s.size).toBeGreaterThan(0);
69
- expect(s.uid).toBeGreaterThan(0);
74
+ expect(s.uid).toBeGreaterThan(-1);
70
75
  expect(s.isBlockDevice()).toBeFalsy();
71
76
  expect(s.isCharacterDevice()).toBeFalsy();
72
77
  expect(s.isDirectory()).toBeFalsy();
@@ -74,6 +79,9 @@ export default async () => {
74
79
  expect(s.isFile()).toBeTruthy();
75
80
  expect(s.isSocket()).toBeFalsy();
76
81
  expect(s.isSymbolicLink()).toBeFalsy();
82
+
83
+ rmSync(filePath);
84
+ rmdirSync(dir);
77
85
  });
78
86
  });
79
87
  }
package/src/stats.ts CHANGED
@@ -1,67 +1,54 @@
1
+ // Reference: Node.js lib/internal/fs/utils.js (Stats class)
2
+ // Reimplemented for GJS using Gio.FileInfo attributes
3
+
1
4
  import Gio from '@girs/gio-2.0';
2
5
  import { Dirent } from './dirent.js';
3
- import { basename } from 'path';
6
+ import { basename } from 'node:path';
7
+
8
+ import type { Stats as NodeStats, BigIntStats as NodeBigIntStats, PathLike } from 'node:fs';
9
+
10
+ // Query all attributes needed for a full Node.js Stats object
11
+ export const STAT_ATTRIBUTES = 'standard::*,time::*,unix::*';
4
12
 
5
- import type { Stats as NodeStats, PathLike } from 'fs';
13
+ function populateFromInfo(info: Gio.FileInfo) {
14
+ const atimeSec = info.get_attribute_uint64('time::access');
15
+ const atimeUsec = info.get_attribute_uint32('time::access-usec') || 0;
16
+ const mtimeSec = info.get_attribute_uint64('time::modified');
17
+ const mtimeUsec = info.get_attribute_uint32('time::modified-usec') || 0;
18
+ const ctimeSec = info.get_attribute_uint64('time::changed');
19
+ const ctimeUsec = info.get_attribute_uint32('time::changed-usec') || 0;
20
+ const createdSec = info.get_attribute_uint64('time::created');
21
+ const createdUsec = info.get_attribute_uint32('time::created-usec') || 0;
22
+
23
+ const atimeMs = atimeSec * 1000 + atimeUsec / 1000;
24
+ const mtimeMs = mtimeSec * 1000 + mtimeUsec / 1000;
25
+ const ctimeMs = ctimeSec ? ctimeSec * 1000 + ctimeUsec / 1000 : mtimeMs;
26
+ const birthtimeMs = createdSec ? createdSec * 1000 + createdUsec / 1000 : ctimeMs;
27
+
28
+ return {
29
+ dev: info.get_attribute_uint32('unix::device') || 0,
30
+ ino: Number(info.get_attribute_uint64('unix::inode') || 0),
31
+ mode: info.get_attribute_uint32('unix::mode') || 0,
32
+ nlink: info.get_attribute_uint32('unix::nlink') || 0,
33
+ uid: info.get_attribute_uint32('unix::uid') || 0,
34
+ gid: info.get_attribute_uint32('unix::gid') || 0,
35
+ rdev: info.get_attribute_uint32('unix::rdev') || 0,
36
+ size: Number(info.get_size() || 0),
37
+ blksize: info.get_attribute_uint32('unix::block-size') || 4096,
38
+ blocks: Number(info.get_attribute_uint64('unix::blocks') || 0),
39
+ atimeMs,
40
+ mtimeMs,
41
+ ctimeMs,
42
+ birthtimeMs,
43
+ atime: new Date(atimeMs),
44
+ mtime: new Date(mtimeMs),
45
+ ctime: new Date(ctimeMs),
46
+ birthtime: new Date(birthtimeMs),
47
+ };
48
+ }
6
49
 
7
50
  /**
8
51
  * A `fs.Stats` object provides information about a file.
9
- *
10
- * Objects returned from {@link stat}, {@link lstat} and {@link fstat} and
11
- * their synchronous counterparts are of this type.
12
- * If `bigint` in the `options` passed to those methods is true, the numeric values
13
- * will be `bigint` instead of `number`, and the object will contain additional
14
- * nanosecond-precision properties suffixed with `Ns`.
15
- *
16
- * ```console
17
- * Stats {
18
- * dev: 2114,
19
- * ino: 48064969,
20
- * mode: 33188,
21
- * nlink: 1,
22
- * uid: 85,
23
- * gid: 100,
24
- * rdev: 0,
25
- * size: 527,
26
- * blksize: 4096,
27
- * blocks: 8,
28
- * atimeMs: 1318289051000.1,
29
- * mtimeMs: 1318289051000.1,
30
- * ctimeMs: 1318289051000.1,
31
- * birthtimeMs: 1318289051000.1,
32
- * atime: Mon, 10 Oct 2011 23:24:11 GMT,
33
- * mtime: Mon, 10 Oct 2011 23:24:11 GMT,
34
- * ctime: Mon, 10 Oct 2011 23:24:11 GMT,
35
- * birthtime: Mon, 10 Oct 2011 23:24:11 GMT }
36
- * ```
37
- *
38
- * `bigint` version:
39
- *
40
- * ```console
41
- * BigIntStats {
42
- * dev: 2114n,
43
- * ino: 48064969n,
44
- * mode: 33188n,
45
- * nlink: 1n,
46
- * uid: 85n,
47
- * gid: 100n,
48
- * rdev: 0n,
49
- * size: 527n,
50
- * blksize: 4096n,
51
- * blocks: 8n,
52
- * atimeMs: 1318289051000n,
53
- * mtimeMs: 1318289051000n,
54
- * ctimeMs: 1318289051000n,
55
- * birthtimeMs: 1318289051000n,
56
- * atimeNs: 1318289051000000000n,
57
- * mtimeNs: 1318289051000000000n,
58
- * ctimeNs: 1318289051000000000n,
59
- * birthtimeNs: 1318289051000000000n,
60
- * atime: Mon, 10 Oct 2011 23:24:11 GMT,
61
- * mtime: Mon, 10 Oct 2011 23:24:11 GMT,
62
- * ctime: Mon, 10 Oct 2011 23:24:11 GMT,
63
- * birthtime: Mon, 10 Oct 2011 23:24:11 GMT }
64
- * ```
65
52
  * @since v0.1.21
66
53
  */
67
54
  export class Stats extends Dirent implements NodeStats {
@@ -78,29 +65,143 @@ export class Stats extends Dirent implements NodeStats {
78
65
  atimeMs: number;
79
66
  mtimeMs: number;
80
67
  ctimeMs: number;
81
- /** The timestamp indicating the creation time of this file expressed in milliseconds since the POSIX Epoch. */
82
- get birthtimeMs(): number {
83
- const creationDateTime = this._info.get_creation_date_time();
84
- return creationDateTime.get_microsecond() * 1000;
85
- }
68
+ birthtimeMs: number;
86
69
  atime: Date;
87
70
  mtime: Date;
88
71
  ctime: Date;
89
- /** The timestamp indicating the creation time of this file. */
90
- get birthtime(): Date {
91
- const creationDateTime = this._info.get_creation_date_time();
92
- return new Date(creationDateTime.format_iso8601());
72
+ birthtime: Date;
73
+
74
+ protected _info: Gio.FileInfo;
75
+
76
+ /**
77
+ * Create Stats from a pre-queried FileInfo, or from a path.
78
+ * @param infoOrPath - A Gio.FileInfo or a file path
79
+ * @param pathOrFilename - The file path (when first arg is FileInfo) or filename
80
+ * @param filename - Optional filename (when first arg is FileInfo)
81
+ */
82
+ constructor(info: Gio.FileInfo, path: PathLike, filename?: string);
83
+ constructor(path: PathLike, filename?: string);
84
+ constructor(
85
+ infoOrPath: Gio.FileInfo | PathLike,
86
+ pathOrFilename?: PathLike | string,
87
+ filename?: string
88
+ ) {
89
+ let info: Gio.FileInfo;
90
+ let pathStr: string;
91
+
92
+ if (infoOrPath instanceof Gio.FileInfo) {
93
+ info = infoOrPath;
94
+ pathStr = (pathOrFilename as PathLike).toString();
95
+ if (!filename) filename = basename(pathStr);
96
+ } else {
97
+ pathStr = infoOrPath.toString();
98
+ if (typeof pathOrFilename === 'string') filename = pathOrFilename;
99
+ if (!filename) filename = basename(pathStr);
100
+ const file = Gio.File.new_for_path(pathStr);
101
+ info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
102
+ }
103
+
104
+ super(pathStr, filename, info.get_file_type());
105
+ this._info = info;
106
+
107
+ const data = populateFromInfo(info);
108
+ this.dev = data.dev;
109
+ this.ino = data.ino;
110
+ this.mode = data.mode;
111
+ this.nlink = data.nlink;
112
+ this.uid = data.uid;
113
+ this.gid = data.gid;
114
+ this.rdev = data.rdev;
115
+ this.size = data.size;
116
+ this.blksize = data.blksize;
117
+ this.blocks = data.blocks;
118
+ this.atimeMs = data.atimeMs;
119
+ this.mtimeMs = data.mtimeMs;
120
+ this.ctimeMs = data.ctimeMs;
121
+ this.birthtimeMs = data.birthtimeMs;
122
+ this.atime = data.atime;
123
+ this.mtime = data.mtime;
124
+ this.ctime = data.ctime;
125
+ this.birthtime = data.birthtime;
93
126
  }
127
+ }
128
+
129
+ /**
130
+ * BigIntStats — same as Stats but with bigint fields and nanosecond precision.
131
+ */
132
+ export class BigIntStats extends Dirent implements NodeBigIntStats {
133
+ dev: bigint;
134
+ ino: bigint;
135
+ mode: bigint;
136
+ nlink: bigint;
137
+ uid: bigint;
138
+ gid: bigint;
139
+ rdev: bigint;
140
+ size: bigint;
141
+ blksize: bigint;
142
+ blocks: bigint;
143
+ atimeMs: bigint;
144
+ mtimeMs: bigint;
145
+ ctimeMs: bigint;
146
+ birthtimeMs: bigint;
147
+ atimeNs: bigint;
148
+ mtimeNs: bigint;
149
+ ctimeNs: bigint;
150
+ birthtimeNs: bigint;
151
+ atime: Date;
152
+ mtime: Date;
153
+ ctime: Date;
154
+ birthtime: Date;
94
155
 
95
156
  protected _info: Gio.FileInfo;
96
157
 
97
- constructor(path: PathLike, filename?: string) {
98
- const pathStr = path.toString();
99
- if (!filename) filename = basename(pathStr);
100
- super(pathStr, filename);
101
- if(!this._file) {
102
- throw new TypeError('this._file is not defined!');
158
+ constructor(info: Gio.FileInfo, path: PathLike, filename?: string);
159
+ constructor(path: PathLike, filename?: string);
160
+ constructor(
161
+ infoOrPath: Gio.FileInfo | PathLike,
162
+ pathOrFilename?: PathLike | string,
163
+ filename?: string
164
+ ) {
165
+ let info: Gio.FileInfo;
166
+ let pathStr: string;
167
+
168
+ if (infoOrPath instanceof Gio.FileInfo) {
169
+ info = infoOrPath;
170
+ pathStr = (pathOrFilename as PathLike).toString();
171
+ if (!filename) filename = basename(pathStr);
172
+ } else {
173
+ pathStr = infoOrPath.toString();
174
+ if (typeof pathOrFilename === 'string') filename = pathOrFilename;
175
+ if (!filename) filename = basename(pathStr);
176
+ const file = Gio.File.new_for_path(pathStr);
177
+ info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
103
178
  }
104
- this._info = this._file.query_info('standard::', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
179
+
180
+ super(pathStr, filename, info.get_file_type());
181
+ this._info = info;
182
+
183
+ const data = populateFromInfo(info);
184
+ this.dev = BigInt(data.dev);
185
+ this.ino = BigInt(data.ino);
186
+ this.mode = BigInt(data.mode);
187
+ this.nlink = BigInt(data.nlink);
188
+ this.uid = BigInt(data.uid);
189
+ this.gid = BigInt(data.gid);
190
+ this.rdev = BigInt(data.rdev);
191
+ this.size = BigInt(data.size);
192
+ this.blksize = BigInt(data.blksize);
193
+ this.blocks = BigInt(data.blocks);
194
+ this.atimeMs = BigInt(Math.trunc(data.atimeMs));
195
+ this.mtimeMs = BigInt(Math.trunc(data.mtimeMs));
196
+ this.ctimeMs = BigInt(Math.trunc(data.ctimeMs));
197
+ this.birthtimeMs = BigInt(Math.trunc(data.birthtimeMs));
198
+ this.atimeNs = this.atimeMs * 1000000n;
199
+ this.mtimeNs = this.mtimeMs * 1000000n;
200
+ this.ctimeNs = this.ctimeMs * 1000000n;
201
+ this.birthtimeNs = this.birthtimeMs * 1000000n;
202
+ this.atime = data.atime;
203
+ this.mtime = data.mtime;
204
+ this.ctime = data.ctime;
205
+ this.birthtime = data.birthtime;
105
206
  }
106
- }
207
+ }