@gjsify/fs 0.4.0 → 0.4.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.
- package/package.json +51 -48
- package/src/callback.spec.ts +0 -296
- package/src/callback.ts +0 -684
- package/src/cp.spec.ts +0 -181
- package/src/cp.ts +0 -328
- package/src/dir.spec.ts +0 -204
- package/src/dir.ts +0 -199
- package/src/dirent.ts +0 -165
- package/src/encoding.ts +0 -45
- package/src/errors.spec.ts +0 -389
- package/src/errors.ts +0 -19
- package/src/extended.spec.ts +0 -706
- package/src/fd-ops.spec.ts +0 -234
- package/src/fd-ops.ts +0 -251
- package/src/file-handle.spec.ts +0 -115
- package/src/file-handle.ts +0 -856
- package/src/fs-watcher.ts +0 -198
- package/src/glob.spec.ts +0 -201
- package/src/glob.ts +0 -205
- package/src/index.ts +0 -313
- package/src/new-apis.spec.ts +0 -505
- package/src/promises.spec.ts +0 -812
- package/src/promises.ts +0 -686
- package/src/read-stream.ts +0 -128
- package/src/stat-watcher.ts +0 -116
- package/src/stat.spec.ts +0 -87
- package/src/statfs.spec.ts +0 -67
- package/src/statfs.ts +0 -92
- package/src/stats.ts +0 -207
- package/src/streams.spec.ts +0 -513
- package/src/symlink.spec.ts +0 -188
- package/src/sync.spec.ts +0 -377
- package/src/sync.ts +0 -568
- package/src/test.mts +0 -27
- package/src/types/encoding-option.ts +0 -3
- package/src/types/file-read-options.ts +0 -15
- package/src/types/file-read-result.ts +0 -4
- package/src/types/flag-and-open-mode.ts +0 -6
- package/src/types/index.ts +0 -6
- package/src/types/open-flags.ts +0 -14
- package/src/types/read-options.ts +0 -9
- package/src/utils.ts +0 -31
- package/src/utimes.spec.ts +0 -113
- package/src/utimes.ts +0 -97
- package/src/watch.spec.ts +0 -171
- package/src/watchfile.spec.ts +0 -185
- package/src/write-stream.ts +0 -142
- package/test/file.txt +0 -1
- package/tsconfig.json +0 -29
- package/tsconfig.tsbuildinfo +0 -1
package/src/read-stream.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
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';
|
|
6
|
-
import GLib from '@girs/glib-2.0';
|
|
7
|
-
import { Buffer } from "node:buffer";
|
|
8
|
-
import { Readable } from "node:stream";
|
|
9
|
-
import { normalizePath } from './utils.js';
|
|
10
|
-
|
|
11
|
-
import type { CreateReadStreamOptions } from 'node:fs/promises';
|
|
12
|
-
import type { PathLike, ReadStream as IReadStream } from 'node:fs';
|
|
13
|
-
|
|
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 _cancellable = new Gio.Cancellable();
|
|
23
|
-
private _start: number;
|
|
24
|
-
private _end: number;
|
|
25
|
-
private _pos: number;
|
|
26
|
-
|
|
27
|
-
close(callback?: (err?: NodeJS.ErrnoException | null) => void): void {
|
|
28
|
-
this._cancellable.cancel();
|
|
29
|
-
if (this._inputStream) {
|
|
30
|
-
this._inputStream.close_async(GLib.PRIORITY_DEFAULT, null, () => {});
|
|
31
|
-
this._inputStream = null;
|
|
32
|
-
}
|
|
33
|
-
this.destroy();
|
|
34
|
-
if (callback) callback(null);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
constructor(path: PathLike, opts?: CreateReadStreamOptions) {
|
|
38
|
-
const pathStr = normalizePath(path);
|
|
39
|
-
|
|
40
|
-
super({
|
|
41
|
-
highWaterMark: opts?.highWaterMark ?? 64 * 1024,
|
|
42
|
-
encoding: opts?.encoding as BufferEncoding | undefined,
|
|
43
|
-
objectMode: false,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
this.path = pathStr;
|
|
47
|
-
this._gioFile = Gio.File.new_for_path(pathStr);
|
|
48
|
-
this._start = (opts?.start as number) ?? 0;
|
|
49
|
-
this._end = (opts?.end as number) ?? Infinity;
|
|
50
|
-
this._pos = this._start;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Use _construct() for async file opening so the stream machinery defers
|
|
54
|
-
// _read() until the file is open. This avoids the fragile _pendingReadSize
|
|
55
|
-
// pattern and correctly handles backpressure via the constructed flag.
|
|
56
|
-
override _construct(callback: (err?: Error | null) => void): void {
|
|
57
|
-
this._gioFile.read_async(GLib.PRIORITY_DEFAULT, this._cancellable, (_source, asyncResult) => {
|
|
58
|
-
if (this.destroyed) { callback(); return; }
|
|
59
|
-
try {
|
|
60
|
-
this._inputStream = this._gioFile.read_finish(asyncResult);
|
|
61
|
-
this.pending = false;
|
|
62
|
-
this.emit('open', 0);
|
|
63
|
-
this.emit('ready');
|
|
64
|
-
if (this._start > 0 && this._inputStream!.can_seek()) {
|
|
65
|
-
this._inputStream!.seek(this._start, GLib.SeekType.SET, null);
|
|
66
|
-
}
|
|
67
|
-
callback();
|
|
68
|
-
} catch (err) {
|
|
69
|
-
if (!this._cancellable.is_cancelled()) {
|
|
70
|
-
callback(err as Error);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
override _read(size: number): void {
|
|
77
|
-
this._doRead(size);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
private _doRead(size: number): void {
|
|
81
|
-
let toRead = size;
|
|
82
|
-
if (this._end !== Infinity) {
|
|
83
|
-
const remaining = this._end - this._pos + 1;
|
|
84
|
-
if (remaining <= 0) {
|
|
85
|
-
this.push(null);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
toRead = Math.min(size, remaining);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const stream = this._inputStream!;
|
|
92
|
-
stream.read_bytes_async(toRead, GLib.PRIORITY_DEFAULT, this._cancellable, (_source, asyncResult) => {
|
|
93
|
-
try {
|
|
94
|
-
const gbytes = stream.read_bytes_finish(asyncResult);
|
|
95
|
-
const data = gbytes.get_data();
|
|
96
|
-
|
|
97
|
-
if (!data || data.length === 0) {
|
|
98
|
-
this.push(null);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
this.bytesRead += data.length;
|
|
103
|
-
this._pos += data.length;
|
|
104
|
-
this.push(Buffer.from(data));
|
|
105
|
-
} catch (err) {
|
|
106
|
-
if (!this._cancellable.is_cancelled()) {
|
|
107
|
-
this.destroy(err as Error);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
override _destroy(error: Error | null, callback: (error?: Error | null) => void): void {
|
|
114
|
-
this._cancellable.cancel();
|
|
115
|
-
if (this._inputStream) {
|
|
116
|
-
this._inputStream.close_async(GLib.PRIORITY_DEFAULT, null, () => {});
|
|
117
|
-
this._inputStream = null;
|
|
118
|
-
}
|
|
119
|
-
callback(error);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function createReadStream(
|
|
124
|
-
path: string | URL,
|
|
125
|
-
options?: CreateReadStreamOptions,
|
|
126
|
-
): ReadStream {
|
|
127
|
-
return new ReadStream(path, options);
|
|
128
|
-
}
|
package/src/stat-watcher.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
// Reference: Node.js lib/internal/fs/watchers.js (StatWatcher)
|
|
2
|
-
// Reimplemented for GJS using setInterval polling
|
|
3
|
-
|
|
4
|
-
import { EventEmitter } from 'node:events';
|
|
5
|
-
import { statSync } from './sync.js';
|
|
6
|
-
|
|
7
|
-
import type { PathLike, Stats } from 'node:fs';
|
|
8
|
-
|
|
9
|
-
function zeroedStat(): Stats {
|
|
10
|
-
return {
|
|
11
|
-
dev: 0, ino: 0, mode: 0, nlink: 0, uid: 0, gid: 0, rdev: 0,
|
|
12
|
-
size: 0, blksize: 0, blocks: 0,
|
|
13
|
-
atimeMs: 0, mtimeMs: 0, ctimeMs: 0, birthtimeMs: 0,
|
|
14
|
-
atime: new Date(0), mtime: new Date(0), ctime: new Date(0), birthtime: new Date(0),
|
|
15
|
-
isFile: () => false, isDirectory: () => false, isBlockDevice: () => false,
|
|
16
|
-
isCharacterDevice: () => false, isSymbolicLink: () => false, isFIFO: () => false,
|
|
17
|
-
isSocket: () => false,
|
|
18
|
-
} as unknown as Stats;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class StatWatcher extends EventEmitter {
|
|
22
|
-
private _path: string;
|
|
23
|
-
private _interval: number;
|
|
24
|
-
private _timerId: ReturnType<typeof setInterval> | null = null;
|
|
25
|
-
private _prev: Stats;
|
|
26
|
-
private _changeCount = 0;
|
|
27
|
-
|
|
28
|
-
constructor(path: string, interval: number) {
|
|
29
|
-
super();
|
|
30
|
-
this._path = path;
|
|
31
|
-
this._interval = interval;
|
|
32
|
-
this._prev = zeroedStat();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
start(): void {
|
|
36
|
-
try { this._prev = statSync(this._path) as unknown as Stats; } catch {}
|
|
37
|
-
this._timerId = setInterval(() => {
|
|
38
|
-
let curr: Stats;
|
|
39
|
-
try { curr = statSync(this._path) as unknown as Stats; } catch { curr = zeroedStat(); }
|
|
40
|
-
const prev = this._prev;
|
|
41
|
-
if (curr.mtimeMs !== prev.mtimeMs || curr.size !== prev.size || curr.ino !== prev.ino) {
|
|
42
|
-
this._prev = curr;
|
|
43
|
-
this.emit('change', curr, prev);
|
|
44
|
-
}
|
|
45
|
-
}, this._interval);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
stop(): void {
|
|
49
|
-
if (this._timerId !== null) {
|
|
50
|
-
clearInterval(this._timerId);
|
|
51
|
-
this._timerId = null;
|
|
52
|
-
}
|
|
53
|
-
this.emit('stop');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
addChangeListener(listener: (curr: Stats, prev: Stats) => void): void {
|
|
57
|
-
this._changeCount++;
|
|
58
|
-
this.on('change', listener);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
removeChangeListener(listener: (curr: Stats, prev: Stats) => void): void {
|
|
62
|
-
this._changeCount--;
|
|
63
|
-
this.removeListener('change', listener);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
removeAllChangeListeners(): void {
|
|
67
|
-
this._changeCount = 0;
|
|
68
|
-
this.removeAllListeners('change');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
get changeListenerCount(): number {
|
|
72
|
-
return this._changeCount;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const statWatchers = new Map<string, StatWatcher>();
|
|
77
|
-
|
|
78
|
-
export function watchFile(
|
|
79
|
-
filename: PathLike,
|
|
80
|
-
options: { persistent?: boolean; interval?: number } | ((curr: Stats, prev: Stats) => void),
|
|
81
|
-
listener?: (curr: Stats, prev: Stats) => void,
|
|
82
|
-
): StatWatcher {
|
|
83
|
-
if (typeof options === 'function') {
|
|
84
|
-
listener = options;
|
|
85
|
-
options = {};
|
|
86
|
-
}
|
|
87
|
-
const interval = (options as { interval?: number }).interval ?? 5007;
|
|
88
|
-
const resolved = filename.toString();
|
|
89
|
-
|
|
90
|
-
let watcher = statWatchers.get(resolved);
|
|
91
|
-
if (!watcher) {
|
|
92
|
-
watcher = new StatWatcher(resolved, interval);
|
|
93
|
-
watcher.start();
|
|
94
|
-
statWatchers.set(resolved, watcher);
|
|
95
|
-
}
|
|
96
|
-
if (listener) watcher.addChangeListener(listener);
|
|
97
|
-
return watcher;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function unwatchFile(
|
|
101
|
-
filename: PathLike,
|
|
102
|
-
listener?: (curr: Stats, prev: Stats) => void,
|
|
103
|
-
): void {
|
|
104
|
-
const resolved = filename.toString();
|
|
105
|
-
const watcher = statWatchers.get(resolved);
|
|
106
|
-
if (!watcher) return;
|
|
107
|
-
if (listener) {
|
|
108
|
-
watcher.removeChangeListener(listener);
|
|
109
|
-
} else {
|
|
110
|
-
watcher.removeAllChangeListeners();
|
|
111
|
-
}
|
|
112
|
-
if (watcher.changeListenerCount === 0) {
|
|
113
|
-
watcher.stop();
|
|
114
|
-
statWatchers.delete(resolved);
|
|
115
|
-
}
|
|
116
|
-
}
|
package/src/stat.spec.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from '@gjsify/unit';
|
|
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';
|
|
6
|
-
|
|
7
|
-
export default async () => {
|
|
8
|
-
await describe('fs.statSync', async () => {
|
|
9
|
-
|
|
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');
|
|
14
|
-
|
|
15
|
-
const s = statSync(filePath);
|
|
16
|
-
|
|
17
|
-
expect(s.atime instanceof Date).toBeTruthy();
|
|
18
|
-
expect(s.atimeMs).toBeGreaterThan(0);
|
|
19
|
-
expect(s.birthtime instanceof Date).toBeTruthy();
|
|
20
|
-
expect(s.birthtimeMs).toBeGreaterThan(0);
|
|
21
|
-
expect(s.blksize).toBeGreaterThan(0);
|
|
22
|
-
expect(s.blocks).toBeGreaterThan(0);
|
|
23
|
-
expect(s.ctime instanceof Date).toBeTruthy();
|
|
24
|
-
expect(s.ctimeMs).toBeGreaterThan(0);
|
|
25
|
-
expect(s.dev).toBeGreaterThan(0);
|
|
26
|
-
expect(s.gid).toBeGreaterThan(-1);
|
|
27
|
-
expect(s.ino).toBeGreaterThan(0);
|
|
28
|
-
expect(s.mode).toBeGreaterThan(0);
|
|
29
|
-
expect(s.mtime instanceof Date).toBeTruthy();
|
|
30
|
-
expect(s.mtimeMs).toBeGreaterThan(0);
|
|
31
|
-
expect(s.nlink).toBeGreaterThan(0);
|
|
32
|
-
expect(s.rdev).toBeGreaterThan(-1);
|
|
33
|
-
expect(s.size).toBeGreaterThan(0);
|
|
34
|
-
expect(s.uid).toBeGreaterThan(-1);
|
|
35
|
-
expect(s.isBlockDevice()).toBeFalsy();
|
|
36
|
-
expect(s.isCharacterDevice()).toBeFalsy();
|
|
37
|
-
expect(s.isDirectory()).toBeFalsy();
|
|
38
|
-
expect(s.isFIFO()).toBeFalsy();
|
|
39
|
-
expect(s.isFile()).toBeTruthy();
|
|
40
|
-
expect(s.isSocket()).toBeFalsy();
|
|
41
|
-
expect(s.isSymbolicLink()).toBeFalsy();
|
|
42
|
-
|
|
43
|
-
rmSync(filePath);
|
|
44
|
-
rmdirSync(dir);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
await describe('fs.stat (promise)', async () => {
|
|
49
|
-
|
|
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');
|
|
54
|
-
|
|
55
|
-
const s = await stat(filePath);
|
|
56
|
-
|
|
57
|
-
expect(s.atime instanceof Date).toBeTruthy();
|
|
58
|
-
expect(s.atimeMs).toBeGreaterThan(0);
|
|
59
|
-
expect(s.birthtime instanceof Date).toBeTruthy();
|
|
60
|
-
expect(s.birthtimeMs).toBeGreaterThan(0);
|
|
61
|
-
expect(s.blksize).toBeGreaterThan(0);
|
|
62
|
-
expect(s.blocks).toBeGreaterThan(0);
|
|
63
|
-
expect(s.ctime instanceof Date).toBeTruthy();
|
|
64
|
-
expect(s.ctimeMs).toBeGreaterThan(0);
|
|
65
|
-
expect(s.dev).toBeGreaterThan(0);
|
|
66
|
-
expect(s.gid).toBeGreaterThan(-1);
|
|
67
|
-
expect(s.ino).toBeGreaterThan(0);
|
|
68
|
-
expect(s.mode).toBeGreaterThan(0);
|
|
69
|
-
expect(s.mtime instanceof Date).toBeTruthy();
|
|
70
|
-
expect(s.mtimeMs).toBeGreaterThan(0);
|
|
71
|
-
expect(s.nlink).toBeGreaterThan(0);
|
|
72
|
-
expect(s.rdev).toBeGreaterThan(-1);
|
|
73
|
-
expect(s.size).toBeGreaterThan(0);
|
|
74
|
-
expect(s.uid).toBeGreaterThan(-1);
|
|
75
|
-
expect(s.isBlockDevice()).toBeFalsy();
|
|
76
|
-
expect(s.isCharacterDevice()).toBeFalsy();
|
|
77
|
-
expect(s.isDirectory()).toBeFalsy();
|
|
78
|
-
expect(s.isFIFO()).toBeFalsy();
|
|
79
|
-
expect(s.isFile()).toBeTruthy();
|
|
80
|
-
expect(s.isSocket()).toBeFalsy();
|
|
81
|
-
expect(s.isSymbolicLink()).toBeFalsy();
|
|
82
|
-
|
|
83
|
-
rmSync(filePath);
|
|
84
|
-
rmdirSync(dir);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
}
|
package/src/statfs.spec.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// Ported from refs/node-test/parallel/test-fs-statfs.js (behavior)
|
|
2
|
-
// Original: MIT, Node.js contributors.
|
|
3
|
-
// Rewritten for @gjsify/unit — behavior preserved, assertion dialect adapted.
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect } from '@gjsify/unit';
|
|
6
|
-
import { statfsSync, statfs, promises } from 'node:fs';
|
|
7
|
-
import { tmpdir } from 'node:os';
|
|
8
|
-
|
|
9
|
-
const TMP = tmpdir();
|
|
10
|
-
|
|
11
|
-
export default async () => {
|
|
12
|
-
await describe('fs.statfs / fs.promises.statfs', async () => {
|
|
13
|
-
await it('statfsSync returns object with expected shape', async () => {
|
|
14
|
-
const result = statfsSync(TMP);
|
|
15
|
-
expect(typeof result.type).toBe('number');
|
|
16
|
-
expect(typeof result.bsize).toBe('number');
|
|
17
|
-
expect(typeof result.blocks).toBe('number');
|
|
18
|
-
expect(typeof result.bfree).toBe('number');
|
|
19
|
-
expect(typeof result.bavail).toBe('number');
|
|
20
|
-
expect(typeof result.files).toBe('number');
|
|
21
|
-
expect(typeof result.ffree).toBe('number');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
await it('statfsSync returns plausible values', async () => {
|
|
25
|
-
const result = statfsSync(TMP);
|
|
26
|
-
expect(result.bsize).toBe(4096);
|
|
27
|
-
expect(result.blocks).toBeGreaterThan(0);
|
|
28
|
-
expect(result.bfree).toBeGreaterThanOrEqual(0);
|
|
29
|
-
expect(result.bavail).toBeGreaterThanOrEqual(0);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
await it('statfs callback form returns same shape', async () => {
|
|
33
|
-
const result = await new Promise<any>((resolve, reject) => {
|
|
34
|
-
statfs(TMP, (err, stats) => {
|
|
35
|
-
if (err) reject(err);
|
|
36
|
-
else resolve(stats);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
expect(typeof result.type).toBe('number');
|
|
40
|
-
expect(typeof result.bsize).toBe('number');
|
|
41
|
-
expect(result.bsize).toBe(4096);
|
|
42
|
-
expect(result.blocks).toBeGreaterThan(0);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
await it('promises.statfs returns same shape', async () => {
|
|
46
|
-
const result = await promises.statfs(TMP);
|
|
47
|
-
expect(typeof result.type).toBe('number');
|
|
48
|
-
expect(typeof result.bsize).toBe('number');
|
|
49
|
-
expect(result.bsize).toBe(4096);
|
|
50
|
-
expect(result.blocks).toBeGreaterThan(0);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
await it('statfsSync with bigint:true returns bigint fields', async () => {
|
|
54
|
-
const result = statfsSync(TMP, { bigint: true });
|
|
55
|
-
expect(typeof result.type).toBe('bigint');
|
|
56
|
-
expect(typeof result.bsize).toBe('bigint');
|
|
57
|
-
expect(typeof result.blocks).toBe('bigint');
|
|
58
|
-
expect(typeof result.bfree).toBe('bigint');
|
|
59
|
-
expect(result.bsize).toBe(4096n);
|
|
60
|
-
expect(result.blocks > 0n).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
await it('statfsSync throws on non-existent path', async () => {
|
|
64
|
-
expect(() => statfsSync('/nonexistent-gjsify-test-path-xyz')).toThrow();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
};
|
package/src/statfs.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
// Reference: Node.js lib/fs.js (fs.statfs / fs.promises.statfs)
|
|
2
|
-
// Reimplemented for GJS using Gio.File.query_filesystem_info
|
|
3
|
-
|
|
4
|
-
import Gio from '@girs/gio-2.0';
|
|
5
|
-
import GLib from '@girs/glib-2.0';
|
|
6
|
-
import { normalizePath } from './utils.js';
|
|
7
|
-
|
|
8
|
-
import type { PathLike } from 'node:fs';
|
|
9
|
-
|
|
10
|
-
const FS_INFO_ATTRS = [
|
|
11
|
-
'filesystem::size',
|
|
12
|
-
'filesystem::free',
|
|
13
|
-
].join(',');
|
|
14
|
-
|
|
15
|
-
// Block size used to derive block counts from byte counts.
|
|
16
|
-
// Gio does not expose the real fs block size; 4096 is a safe default.
|
|
17
|
-
const BSIZE = 4096;
|
|
18
|
-
|
|
19
|
-
export interface StatFsResult {
|
|
20
|
-
type: number;
|
|
21
|
-
bsize: number;
|
|
22
|
-
blocks: number;
|
|
23
|
-
bfree: number;
|
|
24
|
-
bavail: number;
|
|
25
|
-
files: number;
|
|
26
|
-
ffree: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface BigIntStatFsResult {
|
|
30
|
-
type: bigint;
|
|
31
|
-
bsize: bigint;
|
|
32
|
-
blocks: bigint;
|
|
33
|
-
bfree: bigint;
|
|
34
|
-
bavail: bigint;
|
|
35
|
-
files: bigint;
|
|
36
|
-
ffree: bigint;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function buildStatFs(info: Gio.FileInfo): StatFsResult {
|
|
40
|
-
const totalBytes = Number(info.get_attribute_uint64('filesystem::size') ?? 0);
|
|
41
|
-
const freeBytes = Number(info.get_attribute_uint64('filesystem::free') ?? 0);
|
|
42
|
-
const blocks = Math.floor(totalBytes / BSIZE);
|
|
43
|
-
const bfree = Math.floor(freeBytes / BSIZE);
|
|
44
|
-
return { type: 0, bsize: BSIZE, blocks, bfree, bavail: bfree, files: 0, ffree: 0 };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function buildBigIntStatFs(info: Gio.FileInfo): BigIntStatFsResult {
|
|
48
|
-
const totalBytes = BigInt(info.get_attribute_uint64('filesystem::size') ?? 0);
|
|
49
|
-
const freeBytes = BigInt(info.get_attribute_uint64('filesystem::free') ?? 0);
|
|
50
|
-
const bsize = BigInt(BSIZE);
|
|
51
|
-
const blocks = totalBytes / bsize;
|
|
52
|
-
const bfree = freeBytes / bsize;
|
|
53
|
-
return { type: 0n, bsize, blocks, bfree, bavail: bfree, files: 0n, ffree: 0n };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function statfsSync(path: PathLike, options?: { bigint?: false }): StatFsResult;
|
|
57
|
-
export function statfsSync(path: PathLike, options: { bigint: true }): BigIntStatFsResult;
|
|
58
|
-
export function statfsSync(path: PathLike, options?: { bigint?: boolean }): StatFsResult | BigIntStatFsResult {
|
|
59
|
-
const file = Gio.File.new_for_path(normalizePath(path));
|
|
60
|
-
const info = file.query_filesystem_info(FS_INFO_ATTRS, null);
|
|
61
|
-
return options?.bigint === true ? buildBigIntStatFs(info) : buildStatFs(info);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function queryFsInfoAsync(path: PathLike, useBigInt: boolean): Promise<StatFsResult | BigIntStatFsResult> {
|
|
65
|
-
return new Promise((resolve, reject) => {
|
|
66
|
-
const file = Gio.File.new_for_path(normalizePath(path));
|
|
67
|
-
file.query_filesystem_info_async(FS_INFO_ATTRS, GLib.PRIORITY_DEFAULT, null, (_s: unknown, res: Gio.AsyncResult) => {
|
|
68
|
-
try {
|
|
69
|
-
const info = file.query_filesystem_info_finish(res);
|
|
70
|
-
resolve(useBigInt ? buildBigIntStatFs(info) : buildStatFs(info));
|
|
71
|
-
} catch (err) {
|
|
72
|
-
reject(err);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function statfs(path: PathLike, callback: (err: NodeJS.ErrnoException | null, stats: StatFsResult) => void): void;
|
|
79
|
-
export function statfs(path: PathLike, options: { bigint?: false }, callback: (err: NodeJS.ErrnoException | null, stats: StatFsResult) => void): void;
|
|
80
|
-
export function statfs(path: PathLike, options: { bigint: true }, callback: (err: NodeJS.ErrnoException | null, stats: BigIntStatFsResult) => void): void;
|
|
81
|
-
export function statfs(path: PathLike, optionsOrCb: any, callback?: any): void {
|
|
82
|
-
if (typeof optionsOrCb === 'function') {
|
|
83
|
-
callback = optionsOrCb;
|
|
84
|
-
optionsOrCb = {};
|
|
85
|
-
}
|
|
86
|
-
const useBigInt = optionsOrCb?.bigint === true;
|
|
87
|
-
queryFsInfoAsync(path, useBigInt).then(result => callback(null, result), err => callback(err, null));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export async function statfsAsync(path: PathLike, options?: { bigint?: boolean }): Promise<StatFsResult | BigIntStatFsResult> {
|
|
91
|
-
return queryFsInfoAsync(path, options?.bigint === true);
|
|
92
|
-
}
|
package/src/stats.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
// Reference: Node.js lib/internal/fs/utils.js (Stats class)
|
|
2
|
-
// Reimplemented for GJS using Gio.FileInfo attributes
|
|
3
|
-
|
|
4
|
-
import Gio from '@girs/gio-2.0';
|
|
5
|
-
import { Dirent } from './dirent.js';
|
|
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::*';
|
|
12
|
-
|
|
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
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* A `fs.Stats` object provides information about a file.
|
|
52
|
-
* @since v0.1.21
|
|
53
|
-
*/
|
|
54
|
-
export class Stats extends Dirent implements NodeStats {
|
|
55
|
-
dev: number;
|
|
56
|
-
ino: number;
|
|
57
|
-
mode: number;
|
|
58
|
-
nlink: number;
|
|
59
|
-
uid: number;
|
|
60
|
-
gid: number;
|
|
61
|
-
rdev: number;
|
|
62
|
-
size: number;
|
|
63
|
-
blksize: number;
|
|
64
|
-
blocks: number;
|
|
65
|
-
atimeMs: number;
|
|
66
|
-
mtimeMs: number;
|
|
67
|
-
ctimeMs: number;
|
|
68
|
-
birthtimeMs: number;
|
|
69
|
-
atime: Date;
|
|
70
|
-
mtime: Date;
|
|
71
|
-
ctime: Date;
|
|
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;
|
|
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;
|
|
155
|
-
|
|
156
|
-
protected _info: Gio.FileInfo;
|
|
157
|
-
|
|
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);
|
|
178
|
-
}
|
|
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;
|
|
206
|
-
}
|
|
207
|
-
}
|