@gjsify/fs 0.3.21 → 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/lib/esm/_virtual/_rolldown/runtime.js +1 -1
- package/lib/esm/callback.js +1 -1
- package/lib/esm/cp.js +1 -1
- package/lib/esm/dir.js +1 -1
- package/lib/esm/dirent.js +1 -1
- package/lib/esm/encoding.js +1 -1
- package/lib/esm/errors.js +1 -1
- package/lib/esm/fd-ops.js +1 -1
- package/lib/esm/file-handle.js +1 -1
- package/lib/esm/fs-watcher.js +1 -1
- package/lib/esm/glob.js +1 -1
- package/lib/esm/read-stream.js +1 -1
- package/lib/esm/stat-watcher.js +1 -1
- package/lib/esm/statfs.js +1 -1
- package/lib/esm/stats.js +1 -1
- package/lib/esm/sync.js +1 -1
- package/lib/esm/utils.js +1 -1
- package/lib/esm/utimes.js +1 -1
- package/lib/esm/write-stream.js +1 -1
- 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 -562
- 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/sync.ts
DELETED
|
@@ -1,562 +0,0 @@
|
|
|
1
|
-
// Reference: Node.js lib/fs.js (sync API)
|
|
2
|
-
// Reimplemented for GJS using Gio.File synchronous operations
|
|
3
|
-
|
|
4
|
-
import GLib from '@girs/glib-2.0';
|
|
5
|
-
import Gio from '@girs/gio-2.0';
|
|
6
|
-
import { existsSync } from '@gjsify/utils';
|
|
7
|
-
import { Buffer } from 'node:buffer';
|
|
8
|
-
import { join } from 'node:path';
|
|
9
|
-
|
|
10
|
-
import FSWatcher from './fs-watcher.js';
|
|
11
|
-
import { getEncodingFromOptions, encodeUint8Array, decode } from './encoding.js';
|
|
12
|
-
import { FileHandle } from './file-handle.js';
|
|
13
|
-
import { Dirent } from './dirent.js';
|
|
14
|
-
import { Stats, BigIntStats, STAT_ATTRIBUTES } from './stats.js';
|
|
15
|
-
import { createNodeError, isNotFoundError } from './errors.js';
|
|
16
|
-
import { tempDirPath, normalizePath } from './utils.js';
|
|
17
|
-
|
|
18
|
-
import type { OpenFlags, EncodingOption } from './types/index.js';
|
|
19
|
-
import type {
|
|
20
|
-
PathLike,
|
|
21
|
-
Mode,
|
|
22
|
-
MakeDirectoryOptions,
|
|
23
|
-
BufferEncodingOption,
|
|
24
|
-
RmOptions,
|
|
25
|
-
RmDirOptions,
|
|
26
|
-
StatSyncOptions,
|
|
27
|
-
} from 'node:fs'; // Types from @types/node
|
|
28
|
-
|
|
29
|
-
export { existsSync }
|
|
30
|
-
|
|
31
|
-
// --- stat / lstat ---
|
|
32
|
-
|
|
33
|
-
export function statSync(path: PathLike, options?: StatSyncOptions): Stats | BigIntStats | undefined {
|
|
34
|
-
const pathStr = normalizePath(path);
|
|
35
|
-
try {
|
|
36
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
37
|
-
const info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NONE, null);
|
|
38
|
-
return options?.bigint ? new BigIntStats(info, pathStr) : new Stats(info, pathStr);
|
|
39
|
-
} catch (err: unknown) {
|
|
40
|
-
if (options?.throwIfNoEntry === false && isNotFoundError(err)) return undefined;
|
|
41
|
-
throw createNodeError(err, 'stat', pathStr);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function lstatSync(path: PathLike, options?: StatSyncOptions): Stats | BigIntStats | undefined {
|
|
46
|
-
const pathStr = normalizePath(path);
|
|
47
|
-
try {
|
|
48
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
49
|
-
const info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
|
|
50
|
-
return options?.bigint ? new BigIntStats(info, pathStr) : new Stats(info, pathStr);
|
|
51
|
-
} catch (err: unknown) {
|
|
52
|
-
if (options?.throwIfNoEntry === false && isNotFoundError(err)) return undefined;
|
|
53
|
-
throw createNodeError(err, 'lstat', pathStr);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// --- readdir ---
|
|
58
|
-
|
|
59
|
-
export function readdirSync(
|
|
60
|
-
path: PathLike,
|
|
61
|
-
options?: { withFileTypes?: boolean; encoding?: string; recursive?: boolean }
|
|
62
|
-
): string[] | Dirent[] {
|
|
63
|
-
const pathStr = normalizePath(path);
|
|
64
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
65
|
-
const enumerator = file.enumerate_children(
|
|
66
|
-
'standard::name,standard::type',
|
|
67
|
-
Gio.FileQueryInfoFlags.NONE,
|
|
68
|
-
null,
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
const result: (string | Dirent)[] = [];
|
|
72
|
-
let info = enumerator.next_file(null);
|
|
73
|
-
|
|
74
|
-
while (info !== null) {
|
|
75
|
-
const childName = info.get_name();
|
|
76
|
-
const childPath = join(pathStr, childName);
|
|
77
|
-
|
|
78
|
-
if (options?.withFileTypes) {
|
|
79
|
-
result.push(new Dirent(childPath, childName));
|
|
80
|
-
} else {
|
|
81
|
-
result.push(childName);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (options?.recursive && info.get_file_type() === Gio.FileType.DIRECTORY) {
|
|
85
|
-
const subEntries = readdirSync(childPath, options);
|
|
86
|
-
for (const entry of subEntries) {
|
|
87
|
-
if (typeof entry === 'string') {
|
|
88
|
-
result.push(join(childName, entry));
|
|
89
|
-
} else {
|
|
90
|
-
result.push(entry);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
info = enumerator.next_file(null);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return result as string[] | Dirent[];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// --- realpath ---
|
|
102
|
-
|
|
103
|
-
const MAX_SYMLINK_DEPTH = 40; // matches Linux MAXSYMLINKS
|
|
104
|
-
|
|
105
|
-
export function realpathSync(path: PathLike): string {
|
|
106
|
-
const pathStr = normalizePath(path);
|
|
107
|
-
let current = Gio.File.new_for_path(pathStr);
|
|
108
|
-
let depth = 0;
|
|
109
|
-
|
|
110
|
-
while (true) {
|
|
111
|
-
const info = current.query_info(
|
|
112
|
-
'standard::is-symlink,standard::symlink-target',
|
|
113
|
-
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
|
|
114
|
-
null,
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
if (!info.get_is_symlink()) {
|
|
118
|
-
return current.get_path()!;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const target = info.get_symlink_target()!;
|
|
122
|
-
const parent = current.get_parent();
|
|
123
|
-
current = parent ? parent.resolve_relative_path(target) : Gio.File.new_for_path(target);
|
|
124
|
-
|
|
125
|
-
if (++depth > MAX_SYMLINK_DEPTH) {
|
|
126
|
-
throw new Error(`ELOOP: too many levels of symbolic links, realpath '${pathStr}'`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
(realpathSync as unknown as { native: typeof realpathSync }).native = realpathSync;
|
|
131
|
-
|
|
132
|
-
// --- symlink ---
|
|
133
|
-
|
|
134
|
-
export function symlinkSync(target: PathLike, path: PathLike, _type?: 'file' | 'dir' | 'junction'): void {
|
|
135
|
-
const pathStr = normalizePath(path);
|
|
136
|
-
const targetStr = normalizePath(target);
|
|
137
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
138
|
-
file.make_symbolic_link(targetStr, null);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function readFileSync(path: PathLike, options: any = { encoding: null, flag: 'r' }) {
|
|
142
|
-
const pathStr = normalizePath(path);
|
|
143
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
const [ok, data] = file.load_contents(null);
|
|
147
|
-
|
|
148
|
-
if (!ok) {
|
|
149
|
-
throw createNodeError(new Error('failed to read file'), 'read', pathStr);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return encodeUint8Array(getEncodingFromOptions(options, "buffer"), data);
|
|
153
|
-
} catch (err: unknown) {
|
|
154
|
-
if ((err as { code?: unknown }).code && typeof (err as { code?: unknown }).code === 'string') throw err; // Already a Node error
|
|
155
|
-
throw createNodeError(err, 'read', pathStr);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Synchronously creates a directory. Returns `undefined`, or if `recursive` is`true`, the first directory path created.
|
|
161
|
-
* This is the synchronous version of {@link mkdir}.
|
|
162
|
-
*
|
|
163
|
-
* See the POSIX [`mkdir(2)`](http://man7.org/linux/man-pages/man2/mkdir.2.html) documentation for more details.
|
|
164
|
-
* @since v0.1.21
|
|
165
|
-
*/
|
|
166
|
-
export function mkdirSync(
|
|
167
|
-
path: PathLike,
|
|
168
|
-
options: MakeDirectoryOptions & {
|
|
169
|
-
recursive: true;
|
|
170
|
-
}
|
|
171
|
-
): string | undefined;
|
|
172
|
-
/**
|
|
173
|
-
* Synchronous mkdir(2) - create a directory.
|
|
174
|
-
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
|
|
175
|
-
* @param options Either the file mode, or an object optionally specifying the file mode and whether parent folders
|
|
176
|
-
* should be created. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`.
|
|
177
|
-
*/
|
|
178
|
-
export function mkdirSync(
|
|
179
|
-
path: PathLike,
|
|
180
|
-
options?:
|
|
181
|
-
| Mode
|
|
182
|
-
| (MakeDirectoryOptions & {
|
|
183
|
-
recursive?: false | undefined;
|
|
184
|
-
})
|
|
185
|
-
| null
|
|
186
|
-
): void;
|
|
187
|
-
/**
|
|
188
|
-
* Synchronous mkdir(2) - create a directory.
|
|
189
|
-
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
|
|
190
|
-
* @param options Either the file mode, or an object optionally specifying the file mode and whether parent folders
|
|
191
|
-
* should be created. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`.
|
|
192
|
-
*/
|
|
193
|
-
export function mkdirSync(path: PathLike, options?: Mode | MakeDirectoryOptions | null): string | undefined | void
|
|
194
|
-
/**
|
|
195
|
-
* Synchronous mkdir(2) - create a directory.
|
|
196
|
-
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
|
|
197
|
-
* @param options Either the file mode, or an object optionally specifying the file mode and whether parent folders
|
|
198
|
-
* should be created. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`.
|
|
199
|
-
*/
|
|
200
|
-
export function mkdirSync(path: PathLike, options?: Mode | MakeDirectoryOptions | null): string | undefined | void {
|
|
201
|
-
|
|
202
|
-
let recursive = false
|
|
203
|
-
let mode: Mode | undefined = 0o777;
|
|
204
|
-
|
|
205
|
-
if (typeof options === 'object') {
|
|
206
|
-
if(options?.recursive) recursive = options.recursive;
|
|
207
|
-
if(options?.mode) mode = options.mode;
|
|
208
|
-
} else {
|
|
209
|
-
mode = options || 0o777;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
path = normalizePath(path);
|
|
213
|
-
|
|
214
|
-
if(typeof mode === 'string') {
|
|
215
|
-
throw new TypeError("mode as string is currently not supported!");
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (recursive) {
|
|
219
|
-
return mkdirSyncRecursive(path, mode as number);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Non-recursive: create a single directory
|
|
223
|
-
const file = Gio.File.new_for_path(path);
|
|
224
|
-
try {
|
|
225
|
-
file.make_directory(null);
|
|
226
|
-
} catch (err: unknown) {
|
|
227
|
-
throw createNodeError(err, 'mkdir', path);
|
|
228
|
-
}
|
|
229
|
-
return undefined;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Recursively creates directories, similar to `mkdir -p`.
|
|
234
|
-
* Returns the first directory path created, or undefined if all directories already existed.
|
|
235
|
-
*/
|
|
236
|
-
function mkdirSyncRecursive(pathStr: string, mode: number): string | undefined {
|
|
237
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
238
|
-
|
|
239
|
-
// Try to create the directory directly
|
|
240
|
-
try {
|
|
241
|
-
file.make_directory(null);
|
|
242
|
-
// This directory was created successfully — it's a candidate for "first created"
|
|
243
|
-
return pathStr;
|
|
244
|
-
} catch (err: unknown) {
|
|
245
|
-
const gErr = err as { code?: number };
|
|
246
|
-
// If it already exists, nothing to create
|
|
247
|
-
if (gErr.code === Gio.IOErrorEnum.EXISTS) {
|
|
248
|
-
return undefined;
|
|
249
|
-
}
|
|
250
|
-
// If parent doesn't exist, create parent first then retry
|
|
251
|
-
if (gErr.code === Gio.IOErrorEnum.NOT_FOUND) {
|
|
252
|
-
const parentPath = join(pathStr, '..');
|
|
253
|
-
const resolvedParent = Gio.File.new_for_path(parentPath).get_path()!;
|
|
254
|
-
if (resolvedParent === pathStr) {
|
|
255
|
-
// Reached root, cannot go further
|
|
256
|
-
throw createNodeError(err, 'mkdir', pathStr);
|
|
257
|
-
}
|
|
258
|
-
const firstCreated = mkdirSyncRecursive(resolvedParent, mode);
|
|
259
|
-
// Now create this directory
|
|
260
|
-
const retryFile = Gio.File.new_for_path(pathStr);
|
|
261
|
-
try {
|
|
262
|
-
retryFile.make_directory(null);
|
|
263
|
-
} catch (retryErr: unknown) {
|
|
264
|
-
throw createNodeError(retryErr, 'mkdir', pathStr);
|
|
265
|
-
}
|
|
266
|
-
return firstCreated ?? pathStr;
|
|
267
|
-
}
|
|
268
|
-
throw createNodeError(err, 'mkdir', pathStr);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Synchronous [`rmdir(2)`](http://man7.org/linux/man-pages/man2/rmdir.2.html). Returns `undefined`.
|
|
274
|
-
*
|
|
275
|
-
* Using `fs.rmdirSync()` on a file (not a directory) results in an `ENOENT` error
|
|
276
|
-
* on Windows and an `ENOTDIR` error on POSIX.
|
|
277
|
-
*
|
|
278
|
-
* To get a behavior similar to the `rm -rf` Unix command, use {@link rmSync} with options `{ recursive: true, force: true }`.
|
|
279
|
-
* @since v0.1.21
|
|
280
|
-
*/
|
|
281
|
-
export function rmdirSync(path: PathLike, _options?: RmDirOptions): void {
|
|
282
|
-
const pathStr = normalizePath(path);
|
|
283
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
284
|
-
try {
|
|
285
|
-
// Check if it's a directory
|
|
286
|
-
const info = file.query_info('standard::type', Gio.FileQueryInfoFlags.NONE, null);
|
|
287
|
-
if (info.get_file_type() !== Gio.FileType.DIRECTORY) {
|
|
288
|
-
const err = Object.assign(new Error(), { code: 4 }); // Gio.IOErrorEnum.NOT_DIRECTORY
|
|
289
|
-
throw createNodeError(err, 'rmdir', pathStr);
|
|
290
|
-
}
|
|
291
|
-
// Check if empty — rmdir only removes empty directories (use rmSync for recursive)
|
|
292
|
-
const enumerator = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
|
293
|
-
if (enumerator.next_file(null) !== null) {
|
|
294
|
-
const err = Object.assign(new Error(), { code: 5 }); // Gio.IOErrorEnum.NOT_EMPTY
|
|
295
|
-
throw createNodeError(err, 'rmdir', pathStr);
|
|
296
|
-
}
|
|
297
|
-
file.delete(null);
|
|
298
|
-
} catch (err: unknown) {
|
|
299
|
-
if ((err as { code?: unknown }).code && typeof (err as { code?: unknown }).code === 'string') throw err; // Already a Node error
|
|
300
|
-
throw createNodeError(err, 'rmdir', pathStr);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
export function unlinkSync(path: PathLike): void {
|
|
305
|
-
const pathStr = normalizePath(path);
|
|
306
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
307
|
-
try {
|
|
308
|
-
file.delete(null);
|
|
309
|
-
} catch (err: unknown) {
|
|
310
|
-
throw createNodeError(err, 'unlink', pathStr);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
export function writeFileSync(path: PathLike, data: string | Uint8Array) {
|
|
315
|
-
GLib.file_set_contents(normalizePath(path), data);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// --- rename ---
|
|
319
|
-
|
|
320
|
-
export function renameSync(oldPath: PathLike, newPath: PathLike): void {
|
|
321
|
-
const oldStr = normalizePath(oldPath);
|
|
322
|
-
const newStr = normalizePath(newPath);
|
|
323
|
-
const src = Gio.File.new_for_path(oldStr);
|
|
324
|
-
const dest = Gio.File.new_for_path(newStr);
|
|
325
|
-
try {
|
|
326
|
-
src.move(dest, Gio.FileCopyFlags.OVERWRITE, null, null);
|
|
327
|
-
} catch (err: unknown) {
|
|
328
|
-
throw createNodeError(err, 'rename', oldStr, newStr);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// --- copyFile ---
|
|
333
|
-
|
|
334
|
-
export function copyFileSync(src: PathLike, dest: PathLike, mode?: number): void {
|
|
335
|
-
const srcStr = normalizePath(src);
|
|
336
|
-
const destStr = normalizePath(dest);
|
|
337
|
-
const srcFile = Gio.File.new_for_path(srcStr);
|
|
338
|
-
const destFile = Gio.File.new_for_path(destStr);
|
|
339
|
-
let flags = Gio.FileCopyFlags.NONE;
|
|
340
|
-
// mode 0 = default (overwrite), COPYFILE_EXCL (1) = no overwrite
|
|
341
|
-
if (mode && (mode & 1) === 0) {
|
|
342
|
-
flags = Gio.FileCopyFlags.OVERWRITE;
|
|
343
|
-
} else if (!mode) {
|
|
344
|
-
flags = Gio.FileCopyFlags.OVERWRITE;
|
|
345
|
-
}
|
|
346
|
-
try {
|
|
347
|
-
srcFile.copy(destFile, flags, null, null);
|
|
348
|
-
} catch (err: unknown) {
|
|
349
|
-
throw createNodeError(err, 'copyfile', srcStr, destStr);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// --- access ---
|
|
354
|
-
|
|
355
|
-
export function accessSync(path: PathLike, mode?: number): void {
|
|
356
|
-
const pathStr = normalizePath(path);
|
|
357
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
358
|
-
try {
|
|
359
|
-
const info = file.query_info('access::*', Gio.FileQueryInfoFlags.NONE, null);
|
|
360
|
-
// mode: F_OK=0, R_OK=4, W_OK=2, X_OK=1
|
|
361
|
-
if (mode !== undefined && mode !== 0) {
|
|
362
|
-
// Gio.IOErrorEnum.PERMISSION_DENIED = 14 → maps to EACCES via createNodeError
|
|
363
|
-
const permErr = { code: 14, message: `permission denied, access '${pathStr}'` };
|
|
364
|
-
if ((mode & 4) && !info.get_attribute_boolean('access::can-read')) {
|
|
365
|
-
throw createNodeError(permErr, 'access', pathStr);
|
|
366
|
-
}
|
|
367
|
-
if ((mode & 2) && !info.get_attribute_boolean('access::can-write')) {
|
|
368
|
-
throw createNodeError(permErr, 'access', pathStr);
|
|
369
|
-
}
|
|
370
|
-
if ((mode & 1) && !info.get_attribute_boolean('access::can-execute')) {
|
|
371
|
-
throw createNodeError(permErr, 'access', pathStr);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
} catch (err: unknown) {
|
|
375
|
-
if ((err as { code?: unknown }).code && typeof (err as { code?: unknown }).code === 'string') throw err; // Already a Node-style error
|
|
376
|
-
throw createNodeError(err, 'access', pathStr);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// --- appendFile ---
|
|
381
|
-
|
|
382
|
-
export function appendFileSync(path: PathLike, data: string | Uint8Array, options?: { encoding?: string; mode?: number; flag?: string } | string): void {
|
|
383
|
-
const pathStr = normalizePath(path);
|
|
384
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
385
|
-
let bytes: Uint8Array;
|
|
386
|
-
if (typeof data === 'string') {
|
|
387
|
-
bytes = new TextEncoder().encode(data);
|
|
388
|
-
} else {
|
|
389
|
-
bytes = data;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
try {
|
|
393
|
-
const stream = file.append_to(Gio.FileCreateFlags.NONE, null);
|
|
394
|
-
if (bytes.length > 0) {
|
|
395
|
-
stream.write_bytes(new GLib.Bytes(bytes), null);
|
|
396
|
-
}
|
|
397
|
-
stream.close(null);
|
|
398
|
-
} catch (err: unknown) {
|
|
399
|
-
throw createNodeError(err, 'appendfile', pathStr);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// --- readlink ---
|
|
404
|
-
|
|
405
|
-
export function readlinkSync(path: PathLike, options?: { encoding?: string } | string): string | Buffer {
|
|
406
|
-
const pathStr = normalizePath(path);
|
|
407
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
408
|
-
try {
|
|
409
|
-
const info = file.query_info('standard::symlink-target', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
|
|
410
|
-
const target = info.get_symlink_target();
|
|
411
|
-
if (!target) {
|
|
412
|
-
throw Object.assign(new Error(`EINVAL: invalid argument, readlink '${pathStr}'`), { code: 'EINVAL', errno: -22, syscall: 'readlink', path: pathStr });
|
|
413
|
-
}
|
|
414
|
-
const encoding = typeof options === 'string' ? options : options?.encoding;
|
|
415
|
-
if (encoding === 'buffer') {
|
|
416
|
-
return Buffer.from(target);
|
|
417
|
-
}
|
|
418
|
-
return target;
|
|
419
|
-
} catch (err: unknown) {
|
|
420
|
-
if (typeof (err as { code?: unknown }).code === 'string') throw err;
|
|
421
|
-
throw createNodeError(err, 'readlink', pathStr);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// --- link ---
|
|
426
|
-
|
|
427
|
-
export function linkSync(existingPath: PathLike, newPath: PathLike): void {
|
|
428
|
-
const existingStr = normalizePath(existingPath);
|
|
429
|
-
const newStr = normalizePath(newPath);
|
|
430
|
-
// Gio doesn't have a direct hard link API, use GLib
|
|
431
|
-
const result = GLib.spawn_command_line_sync(`ln ${existingStr} ${newStr}`);
|
|
432
|
-
if (!result[0]) {
|
|
433
|
-
throw Object.assign(new Error(`EPERM: operation not permitted, link '${existingStr}' -> '${newStr}'`), { code: 'EPERM', errno: -1, syscall: 'link', path: existingStr, dest: newStr });
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// --- truncate ---
|
|
438
|
-
|
|
439
|
-
export function truncateSync(path: PathLike, len?: number): void {
|
|
440
|
-
const pathStr = normalizePath(path);
|
|
441
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
442
|
-
try {
|
|
443
|
-
const stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
|
|
444
|
-
if (len && len > 0) {
|
|
445
|
-
// Read existing content, truncate to len
|
|
446
|
-
const [, data] = file.load_contents(null);
|
|
447
|
-
const truncated = data.slice(0, len);
|
|
448
|
-
if (truncated.length > 0) {
|
|
449
|
-
stream.write_bytes(new GLib.Bytes(truncated), null);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
stream.close(null);
|
|
453
|
-
} catch (err: unknown) {
|
|
454
|
-
throw createNodeError(err, 'truncate', pathStr);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// --- chmodSync ---
|
|
459
|
-
|
|
460
|
-
export function chmodSync(path: PathLike, mode: Mode): void {
|
|
461
|
-
const pathStr = normalizePath(path);
|
|
462
|
-
const modeNum = typeof mode === 'string' ? parseInt(mode, 8) : mode;
|
|
463
|
-
const result = GLib.spawn_command_line_sync(`chmod ${modeNum.toString(8)} ${pathStr}`);
|
|
464
|
-
if (!result[0]) {
|
|
465
|
-
throw Object.assign(new Error(`EPERM: operation not permitted, chmod '${pathStr}'`), { code: 'EPERM', errno: -1, syscall: 'chmod', path: pathStr });
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// --- chownSync ---
|
|
470
|
-
|
|
471
|
-
export function chownSync(path: PathLike, uid: number, gid: number): void {
|
|
472
|
-
const pathStr = normalizePath(path);
|
|
473
|
-
const result = GLib.spawn_command_line_sync(`chown ${uid}:${gid} ${pathStr}`);
|
|
474
|
-
if (!result[0]) {
|
|
475
|
-
throw Object.assign(new Error(`EPERM: operation not permitted, chown '${pathStr}'`), { code: 'EPERM', errno: -1, syscall: 'chown', path: pathStr });
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
export function watch(filename: PathLike, options: { persistent?: boolean; recursive?: boolean; encoding?: string } | undefined, listener: ((eventType: string, filename: string | null) => void) | undefined) {
|
|
480
|
-
return new FSWatcher(normalizePath(filename), options, listener);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
export function openSync(path: PathLike, flags?: OpenFlags | number, mode?: Mode): FileHandle {
|
|
484
|
-
return new FileHandle({ path, flags: flags as OpenFlags | undefined, mode });
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Returns the created directory path.
|
|
489
|
-
*
|
|
490
|
-
* For detailed information, see the documentation of the asynchronous version of
|
|
491
|
-
* this API: {@link mkdtemp}.
|
|
492
|
-
*
|
|
493
|
-
* The optional `options` argument can be a string specifying an encoding, or an
|
|
494
|
-
* object with an `encoding` property specifying the character encoding to use.
|
|
495
|
-
* @since v5.10.0
|
|
496
|
-
*/
|
|
497
|
-
export function mkdtempSync(prefix: string, options?: EncodingOption): string;
|
|
498
|
-
/**
|
|
499
|
-
* Synchronously creates a unique temporary directory.
|
|
500
|
-
* Generates six random characters to be appended behind a required prefix to create a unique temporary directory.
|
|
501
|
-
* @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used.
|
|
502
|
-
*/
|
|
503
|
-
export function mkdtempSync(prefix: string, options: BufferEncodingOption): Buffer;
|
|
504
|
-
/**
|
|
505
|
-
* Synchronously creates a unique temporary directory.
|
|
506
|
-
* Generates six random characters to be appended behind a required prefix to create a unique temporary directory.
|
|
507
|
-
* @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used.
|
|
508
|
-
*/
|
|
509
|
-
export function mkdtempSync(prefix: string, options?: EncodingOption): string | Buffer;
|
|
510
|
-
|
|
511
|
-
export function mkdtempSync(prefix: string, options?: EncodingOption | BufferEncodingOption): string | Buffer {
|
|
512
|
-
const encoding: string | undefined = getEncodingFromOptions(options);
|
|
513
|
-
const path = tempDirPath(prefix);
|
|
514
|
-
|
|
515
|
-
mkdirSync(
|
|
516
|
-
path,
|
|
517
|
-
{ recursive: false, mode: 0o777 }
|
|
518
|
-
)
|
|
519
|
-
|
|
520
|
-
return decode(path, encoding);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Synchronously removes files and directories (modeled on the standard POSIX `rm`utility). Returns `undefined`.
|
|
525
|
-
* @since v14.14.0
|
|
526
|
-
*/
|
|
527
|
-
export function rmSync(path: PathLike, options?: RmOptions): void {
|
|
528
|
-
const pathStr = normalizePath(path);
|
|
529
|
-
const file = Gio.File.new_for_path(pathStr);
|
|
530
|
-
const recursive = options?.recursive || false;
|
|
531
|
-
const force = options?.force || false;
|
|
532
|
-
|
|
533
|
-
let dirent: Dirent;
|
|
534
|
-
try {
|
|
535
|
-
dirent = new Dirent(pathStr);
|
|
536
|
-
} catch (err: unknown) {
|
|
537
|
-
if (force && isNotFoundError(err)) return;
|
|
538
|
-
throw createNodeError(err, 'rm', path);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if (dirent.isDirectory()) {
|
|
542
|
-
const childFiles = readdirSync(path, { withFileTypes: true });
|
|
543
|
-
|
|
544
|
-
if (!recursive && childFiles.length) {
|
|
545
|
-
const err = Object.assign(new Error(), { code: 5 }); // Gio.IOErrorEnum.NOT_EMPTY
|
|
546
|
-
throw createNodeError(err, 'rm', path);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
for (const childFile of childFiles) {
|
|
550
|
-
if (typeof childFile !== 'string') {
|
|
551
|
-
rmSync(join(pathStr, childFile.name), options);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
try {
|
|
557
|
-
file.delete(null);
|
|
558
|
-
} catch (err: unknown) {
|
|
559
|
-
if (force && isNotFoundError(err)) return;
|
|
560
|
-
throw createNodeError(err, 'rm', path);
|
|
561
|
-
}
|
|
562
|
-
}
|
package/src/test.mts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import '@gjsify/node-globals/register/process';
|
|
2
|
-
import '@gjsify/node-globals/register/buffer';
|
|
3
|
-
import '@gjsify/node-globals/register/timers';
|
|
4
|
-
import '@gjsify/node-globals/register/url';
|
|
5
|
-
import { run } from '@gjsify/unit';
|
|
6
|
-
|
|
7
|
-
import testSuiteCallback from './callback.spec.js';
|
|
8
|
-
import testSuiteFileHandle from './file-handle.spec.js';
|
|
9
|
-
import testSuitePromise from './promises.spec.js';
|
|
10
|
-
import testSuiteSync from './sync.spec.js';
|
|
11
|
-
import testSuiteSymlink from './symlink.spec.js';
|
|
12
|
-
import testSuiteStat from './stat.spec.js';
|
|
13
|
-
import testSuiteNewApis from './new-apis.spec.js';
|
|
14
|
-
import testSuiteExtended from './extended.spec.js';
|
|
15
|
-
|
|
16
|
-
import testSuiteErrors from './errors.spec.js';
|
|
17
|
-
import testSuiteStreams from './streams.spec.js';
|
|
18
|
-
import testSuiteCp from './cp.spec.js';
|
|
19
|
-
import testSuiteDir from './dir.spec.js';
|
|
20
|
-
import testSuiteGlob from './glob.spec.js';
|
|
21
|
-
import testSuiteWatch from './watch.spec.js';
|
|
22
|
-
import testSuiteWatchFile from './watchfile.spec.js';
|
|
23
|
-
import testSuiteStatFs from './statfs.spec.js';
|
|
24
|
-
import testSuiteUtimes from './utimes.spec.js';
|
|
25
|
-
import testSuiteFdOps from './fd-ops.spec.js';
|
|
26
|
-
|
|
27
|
-
run({testSuiteCallback, testSuiteFileHandle, testSuitePromise, testSuiteSync, testSuiteSymlink, testSuiteStat, testSuiteNewApis, testSuiteExtended, testSuiteErrors, testSuiteStreams, testSuiteCp, testSuiteDir, testSuiteGlob, testSuiteWatch, testSuiteWatchFile, testSuiteStatFs, testSuiteUtimes, testSuiteFdOps});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface FileReadOptions<T extends NodeJS.ArrayBufferView = Buffer> {
|
|
2
|
-
/**
|
|
3
|
-
* @default `Buffer.alloc(0xffff)`
|
|
4
|
-
*/
|
|
5
|
-
buffer?: T;
|
|
6
|
-
/**
|
|
7
|
-
* @default 0
|
|
8
|
-
*/
|
|
9
|
-
offset?: number | null;
|
|
10
|
-
/**
|
|
11
|
-
* @default `buffer.byteLength`
|
|
12
|
-
*/
|
|
13
|
-
length?: number | null;
|
|
14
|
-
position?: number | null;
|
|
15
|
-
}
|
package/src/types/index.ts
DELETED
package/src/types/open-flags.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Abortable } from 'node:events';
|
|
2
|
-
import type { ObjectEncodingOptions, OpenMode } from 'node:fs'; // Types from @types/node
|
|
3
|
-
|
|
4
|
-
export type ReadOptions =
|
|
5
|
-
| (ObjectEncodingOptions & Abortable & {
|
|
6
|
-
flag?: OpenMode | undefined;
|
|
7
|
-
})
|
|
8
|
-
| BufferEncoding
|
|
9
|
-
| null
|
package/src/utils.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// Shared filesystem utilities for GJS — original implementation using Gio
|
|
2
|
-
|
|
3
|
-
import { existsSync } from './sync.js';
|
|
4
|
-
import { fileURLToPath, URL as NodeURL } from 'node:url';
|
|
5
|
-
|
|
6
|
-
import type { PathLike } from 'node:fs';
|
|
7
|
-
|
|
8
|
-
// Gio.File.new_for_path only accepts strings; convert URL/Buffer accordingly.
|
|
9
|
-
export function normalizePath(path: PathLike): string {
|
|
10
|
-
if (path instanceof URL || path instanceof NodeURL) return fileURLToPath(path as URL);
|
|
11
|
-
if (typeof path === 'string') return path;
|
|
12
|
-
return (path as Buffer).toString();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
16
|
-
|
|
17
|
-
export function randomName(): string {
|
|
18
|
-
return [...Array(6)].map(() =>
|
|
19
|
-
CHARS[Math.floor(Math.random() * CHARS.length)]
|
|
20
|
-
).join("");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// credits: https://github.com/denoland/deno_std/blob/63be40277264e71af60f9b118a2cb569e02beeab/node/_fs/_fs_mkdtemp.ts#L98
|
|
24
|
-
export function tempDirPath(prefix: string): string {
|
|
25
|
-
let path: string;
|
|
26
|
-
do {
|
|
27
|
-
path = prefix + randomName();
|
|
28
|
-
} while (existsSync(path));
|
|
29
|
-
|
|
30
|
-
return path;
|
|
31
|
-
}
|