@gjsify/fs 0.0.3 → 0.1.0

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 -34
  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 -40570
  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
package/lib/esm/stats.js CHANGED
@@ -1,6 +1,41 @@
1
1
  import Gio from "@girs/gio-2.0";
2
2
  import { Dirent } from "./dirent.js";
3
- import { basename } from "path";
3
+ import { basename } from "node:path";
4
+ const STAT_ATTRIBUTES = "standard::*,time::*,unix::*";
5
+ function populateFromInfo(info) {
6
+ const atimeSec = info.get_attribute_uint64("time::access");
7
+ const atimeUsec = info.get_attribute_uint32("time::access-usec") || 0;
8
+ const mtimeSec = info.get_attribute_uint64("time::modified");
9
+ const mtimeUsec = info.get_attribute_uint32("time::modified-usec") || 0;
10
+ const ctimeSec = info.get_attribute_uint64("time::changed");
11
+ const ctimeUsec = info.get_attribute_uint32("time::changed-usec") || 0;
12
+ const createdSec = info.get_attribute_uint64("time::created");
13
+ const createdUsec = info.get_attribute_uint32("time::created-usec") || 0;
14
+ const atimeMs = atimeSec * 1e3 + atimeUsec / 1e3;
15
+ const mtimeMs = mtimeSec * 1e3 + mtimeUsec / 1e3;
16
+ const ctimeMs = ctimeSec ? ctimeSec * 1e3 + ctimeUsec / 1e3 : mtimeMs;
17
+ const birthtimeMs = createdSec ? createdSec * 1e3 + createdUsec / 1e3 : ctimeMs;
18
+ return {
19
+ dev: info.get_attribute_uint32("unix::device") || 0,
20
+ ino: Number(info.get_attribute_uint64("unix::inode") || 0),
21
+ mode: info.get_attribute_uint32("unix::mode") || 0,
22
+ nlink: info.get_attribute_uint32("unix::nlink") || 0,
23
+ uid: info.get_attribute_uint32("unix::uid") || 0,
24
+ gid: info.get_attribute_uint32("unix::gid") || 0,
25
+ rdev: info.get_attribute_uint32("unix::rdev") || 0,
26
+ size: Number(info.get_size() || 0),
27
+ blksize: info.get_attribute_uint32("unix::block-size") || 4096,
28
+ blocks: Number(info.get_attribute_uint64("unix::blocks") || 0),
29
+ atimeMs,
30
+ mtimeMs,
31
+ ctimeMs,
32
+ birthtimeMs,
33
+ atime: new Date(atimeMs),
34
+ mtime: new Date(mtimeMs),
35
+ ctime: new Date(ctimeMs),
36
+ birthtime: new Date(birthtimeMs)
37
+ };
38
+ }
4
39
  class Stats extends Dirent {
5
40
  dev;
6
41
  ino;
@@ -15,31 +50,116 @@ class Stats extends Dirent {
15
50
  atimeMs;
16
51
  mtimeMs;
17
52
  ctimeMs;
18
- /** The timestamp indicating the creation time of this file expressed in milliseconds since the POSIX Epoch. */
19
- get birthtimeMs() {
20
- const creationDateTime = this._info.get_creation_date_time();
21
- return creationDateTime.get_microsecond() * 1e3;
22
- }
53
+ birthtimeMs;
23
54
  atime;
24
55
  mtime;
25
56
  ctime;
26
- /** The timestamp indicating the creation time of this file. */
27
- get birthtime() {
28
- const creationDateTime = this._info.get_creation_date_time();
29
- return new Date(creationDateTime.format_iso8601());
57
+ birthtime;
58
+ _info;
59
+ constructor(infoOrPath, pathOrFilename, filename) {
60
+ let info;
61
+ let pathStr;
62
+ if (infoOrPath instanceof Gio.FileInfo) {
63
+ info = infoOrPath;
64
+ pathStr = pathOrFilename.toString();
65
+ if (!filename) filename = basename(pathStr);
66
+ } else {
67
+ pathStr = infoOrPath.toString();
68
+ if (typeof pathOrFilename === "string") filename = pathOrFilename;
69
+ if (!filename) filename = basename(pathStr);
70
+ const file = Gio.File.new_for_path(pathStr);
71
+ info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
72
+ }
73
+ super(pathStr, filename, info.get_file_type());
74
+ this._info = info;
75
+ const data = populateFromInfo(info);
76
+ this.dev = data.dev;
77
+ this.ino = data.ino;
78
+ this.mode = data.mode;
79
+ this.nlink = data.nlink;
80
+ this.uid = data.uid;
81
+ this.gid = data.gid;
82
+ this.rdev = data.rdev;
83
+ this.size = data.size;
84
+ this.blksize = data.blksize;
85
+ this.blocks = data.blocks;
86
+ this.atimeMs = data.atimeMs;
87
+ this.mtimeMs = data.mtimeMs;
88
+ this.ctimeMs = data.ctimeMs;
89
+ this.birthtimeMs = data.birthtimeMs;
90
+ this.atime = data.atime;
91
+ this.mtime = data.mtime;
92
+ this.ctime = data.ctime;
93
+ this.birthtime = data.birthtime;
30
94
  }
95
+ }
96
+ class BigIntStats extends Dirent {
97
+ dev;
98
+ ino;
99
+ mode;
100
+ nlink;
101
+ uid;
102
+ gid;
103
+ rdev;
104
+ size;
105
+ blksize;
106
+ blocks;
107
+ atimeMs;
108
+ mtimeMs;
109
+ ctimeMs;
110
+ birthtimeMs;
111
+ atimeNs;
112
+ mtimeNs;
113
+ ctimeNs;
114
+ birthtimeNs;
115
+ atime;
116
+ mtime;
117
+ ctime;
118
+ birthtime;
31
119
  _info;
32
- constructor(path, filename) {
33
- const pathStr = path.toString();
34
- if (!filename)
35
- filename = basename(pathStr);
36
- super(pathStr, filename);
37
- if (!this._file) {
38
- throw new TypeError("this._file is not defined!");
120
+ constructor(infoOrPath, pathOrFilename, filename) {
121
+ let info;
122
+ let pathStr;
123
+ if (infoOrPath instanceof Gio.FileInfo) {
124
+ info = infoOrPath;
125
+ pathStr = pathOrFilename.toString();
126
+ if (!filename) filename = basename(pathStr);
127
+ } else {
128
+ pathStr = infoOrPath.toString();
129
+ if (typeof pathOrFilename === "string") filename = pathOrFilename;
130
+ if (!filename) filename = basename(pathStr);
131
+ const file = Gio.File.new_for_path(pathStr);
132
+ info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
39
133
  }
40
- this._info = this._file.query_info("standard::", Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
134
+ super(pathStr, filename, info.get_file_type());
135
+ this._info = info;
136
+ const data = populateFromInfo(info);
137
+ this.dev = BigInt(data.dev);
138
+ this.ino = BigInt(data.ino);
139
+ this.mode = BigInt(data.mode);
140
+ this.nlink = BigInt(data.nlink);
141
+ this.uid = BigInt(data.uid);
142
+ this.gid = BigInt(data.gid);
143
+ this.rdev = BigInt(data.rdev);
144
+ this.size = BigInt(data.size);
145
+ this.blksize = BigInt(data.blksize);
146
+ this.blocks = BigInt(data.blocks);
147
+ this.atimeMs = BigInt(Math.trunc(data.atimeMs));
148
+ this.mtimeMs = BigInt(Math.trunc(data.mtimeMs));
149
+ this.ctimeMs = BigInt(Math.trunc(data.ctimeMs));
150
+ this.birthtimeMs = BigInt(Math.trunc(data.birthtimeMs));
151
+ this.atimeNs = this.atimeMs * 1000000n;
152
+ this.mtimeNs = this.mtimeMs * 1000000n;
153
+ this.ctimeNs = this.ctimeMs * 1000000n;
154
+ this.birthtimeNs = this.birthtimeMs * 1000000n;
155
+ this.atime = data.atime;
156
+ this.mtime = data.mtime;
157
+ this.ctime = data.ctime;
158
+ this.birthtime = data.birthtime;
41
159
  }
42
160
  }
43
161
  export {
162
+ BigIntStats,
163
+ STAT_ATTRIBUTES,
44
164
  Stats
45
165
  };
package/lib/esm/sync.js CHANGED
@@ -1,33 +1,112 @@
1
1
  import GLib from "@girs/glib-2.0";
2
2
  import Gio from "@girs/gio-2.0";
3
3
  import { existsSync } from "@gjsify/utils";
4
- import { join } from "path";
4
+ import { Buffer } from "node:buffer";
5
+ import { join } from "node:path";
5
6
  import FSWatcher from "./fs-watcher.js";
6
7
  import { getEncodingFromOptions, encodeUint8Array, decode } from "./encoding.js";
7
8
  import { FileHandle } from "./file-handle.js";
8
9
  import { Dirent } from "./dirent.js";
10
+ import { Stats, BigIntStats, STAT_ATTRIBUTES } from "./stats.js";
11
+ import { createNodeError, isNotFoundError } from "./errors.js";
9
12
  import { tempDirPath } from "./utils.js";
10
- import { realpathSync } from "@gjsify/deno_std/node/_fs/_fs_realpath";
11
- import { readdirSync } from "@gjsify/deno_std/node/_fs/_fs_readdir";
12
- import { symlinkSync } from "@gjsify/deno_std/node/_fs/_fs_symlink";
13
- import { lstatSync } from "@gjsify/deno_std/node/_fs/_fs_lstat";
14
- import { statSync } from "@gjsify/deno_std/node/_fs/_fs_stat";
13
+ function statSync(path, options) {
14
+ try {
15
+ const file = Gio.File.new_for_path(path.toString());
16
+ const info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NONE, null);
17
+ return options?.bigint ? new BigIntStats(info, path) : new Stats(info, path);
18
+ } catch (err) {
19
+ if (options?.throwIfNoEntry === false && isNotFoundError(err)) return void 0;
20
+ throw createNodeError(err, "stat", path);
21
+ }
22
+ }
23
+ function lstatSync(path, options) {
24
+ try {
25
+ const file = Gio.File.new_for_path(path.toString());
26
+ const info = file.query_info(STAT_ATTRIBUTES, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
27
+ return options?.bigint ? new BigIntStats(info, path) : new Stats(info, path);
28
+ } catch (err) {
29
+ if (options?.throwIfNoEntry === false && isNotFoundError(err)) return void 0;
30
+ throw createNodeError(err, "lstat", path);
31
+ }
32
+ }
33
+ function readdirSync(path, options) {
34
+ const pathStr = path.toString();
35
+ const file = Gio.File.new_for_path(pathStr);
36
+ const enumerator = file.enumerate_children(
37
+ "standard::name,standard::type",
38
+ Gio.FileQueryInfoFlags.NONE,
39
+ null
40
+ );
41
+ const result = [];
42
+ let info = enumerator.next_file(null);
43
+ while (info !== null) {
44
+ const childName = info.get_name();
45
+ const childPath = join(pathStr, childName);
46
+ if (options?.withFileTypes) {
47
+ result.push(new Dirent(childPath, childName));
48
+ } else {
49
+ result.push(childName);
50
+ }
51
+ if (options?.recursive && info.get_file_type() === Gio.FileType.DIRECTORY) {
52
+ const subEntries = readdirSync(childPath, options);
53
+ for (const entry of subEntries) {
54
+ if (typeof entry === "string") {
55
+ result.push(join(childName, entry));
56
+ } else {
57
+ result.push(entry);
58
+ }
59
+ }
60
+ }
61
+ info = enumerator.next_file(null);
62
+ }
63
+ return result;
64
+ }
65
+ const MAX_SYMLINK_DEPTH = 40;
66
+ function realpathSync(path) {
67
+ let current = Gio.File.new_for_path(path.toString());
68
+ let depth = 0;
69
+ while (true) {
70
+ const info = current.query_info(
71
+ "standard::is-symlink,standard::symlink-target",
72
+ Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
73
+ null
74
+ );
75
+ if (!info.get_is_symlink()) {
76
+ return current.get_path();
77
+ }
78
+ const target = info.get_symlink_target();
79
+ const parent = current.get_parent();
80
+ current = parent ? parent.resolve_relative_path(target) : Gio.File.new_for_path(target);
81
+ if (++depth > MAX_SYMLINK_DEPTH) {
82
+ throw new Error(`ELOOP: too many levels of symbolic links, realpath '${path}'`);
83
+ }
84
+ }
85
+ }
86
+ realpathSync.native = realpathSync;
87
+ function symlinkSync(target, path, _type) {
88
+ const file = Gio.File.new_for_path(path.toString());
89
+ file.make_symbolic_link(target.toString(), null);
90
+ }
15
91
  function readFileSync(path, options = { encoding: null, flag: "r" }) {
16
92
  const file = Gio.File.new_for_path(path);
17
- const [ok, data] = file.load_contents(null);
18
- if (!ok) {
19
- throw new Error("failed to read file");
93
+ try {
94
+ const [ok, data] = file.load_contents(null);
95
+ if (!ok) {
96
+ throw createNodeError(new Error("failed to read file"), "read", path);
97
+ }
98
+ return encodeUint8Array(getEncodingFromOptions(options, "buffer"), data);
99
+ } catch (err) {
100
+ if (err.code && typeof err.code === "string") throw err;
101
+ throw createNodeError(err, "read", path);
20
102
  }
21
- return encodeUint8Array(getEncodingFromOptions(options, "buffer"), data);
22
103
  }
23
104
  function mkdirSync(path, options) {
24
105
  let recursive = false;
25
106
  let mode = 511;
26
107
  if (typeof options === "object") {
27
- if (options?.recursive)
28
- recursive = options.recursive;
29
- if (options?.mode)
30
- mode = options.mode;
108
+ if (options?.recursive) recursive = options.recursive;
109
+ if (options?.mode) mode = options.mode;
31
110
  } else {
32
111
  mode = options || 511;
33
112
  }
@@ -37,38 +116,191 @@ function mkdirSync(path, options) {
37
116
  if (typeof mode === "string") {
38
117
  throw new TypeError("mode as string is currently not supported!");
39
118
  }
40
- if (GLib.mkdir_with_parents(path, mode) !== 0) {
41
- throw new Error(`failed to make ${path} directory`);
42
- }
43
119
  if (recursive) {
44
- return path.split("/")[0];
120
+ return mkdirSyncRecursive(path, mode);
121
+ }
122
+ const file = Gio.File.new_for_path(path);
123
+ try {
124
+ file.make_directory(null);
125
+ } catch (err) {
126
+ throw createNodeError(err, "mkdir", path);
45
127
  }
46
128
  return void 0;
47
129
  }
48
- function rmdirSync(path, options) {
49
- const recursive = options?.recursive || false;
50
- const childFiles = readdirSync(path, { withFileTypes: true });
51
- if (!recursive && childFiles.length) {
52
- throw new Error("Dir is not empty!");
53
- }
54
- for (const childFile of childFiles) {
55
- if (childFile.isDirectory()) {
56
- rmdirSync(join(path.toString(), childFile.name));
57
- } else if (childFile.isFile()) {
58
- rmSync(join(path.toString(), childFile.name));
130
+ function mkdirSyncRecursive(pathStr, mode) {
131
+ const file = Gio.File.new_for_path(pathStr);
132
+ try {
133
+ file.make_directory(null);
134
+ return pathStr;
135
+ } catch (err) {
136
+ const gErr = err;
137
+ if (gErr.code === Gio.IOErrorEnum.EXISTS) {
138
+ return void 0;
59
139
  }
140
+ if (gErr.code === Gio.IOErrorEnum.NOT_FOUND) {
141
+ const parentPath = join(pathStr, "..");
142
+ const resolvedParent = Gio.File.new_for_path(parentPath).get_path();
143
+ if (resolvedParent === pathStr) {
144
+ throw createNodeError(err, "mkdir", pathStr);
145
+ }
146
+ const firstCreated = mkdirSyncRecursive(resolvedParent, mode);
147
+ const retryFile = Gio.File.new_for_path(pathStr);
148
+ try {
149
+ retryFile.make_directory(null);
150
+ } catch (retryErr) {
151
+ throw createNodeError(retryErr, "mkdir", pathStr);
152
+ }
153
+ return firstCreated ?? pathStr;
154
+ }
155
+ throw createNodeError(err, "mkdir", pathStr);
60
156
  }
61
- const result = GLib.rmdir(path.toString());
62
- if (result !== 0) {
63
- throw new Error(`Failed to remove ${path} directory`);
157
+ }
158
+ function rmdirSync(path, _options) {
159
+ const file = Gio.File.new_for_path(path.toString());
160
+ try {
161
+ const info = file.query_info("standard::type", Gio.FileQueryInfoFlags.NONE, null);
162
+ if (info.get_file_type() !== Gio.FileType.DIRECTORY) {
163
+ const err = Object.assign(new Error(), { code: 4 });
164
+ throw createNodeError(err, "rmdir", path);
165
+ }
166
+ const enumerator = file.enumerate_children("standard::name", Gio.FileQueryInfoFlags.NONE, null);
167
+ if (enumerator.next_file(null) !== null) {
168
+ const err = Object.assign(new Error(), { code: 5 });
169
+ throw createNodeError(err, "rmdir", path);
170
+ }
171
+ file.delete(null);
172
+ } catch (err) {
173
+ if (err.code && typeof err.code === "string") throw err;
174
+ throw createNodeError(err, "rmdir", path);
64
175
  }
65
176
  }
66
177
  function unlinkSync(path) {
67
- GLib.unlink(path);
178
+ const file = Gio.File.new_for_path(path.toString());
179
+ try {
180
+ file.delete(null);
181
+ } catch (err) {
182
+ throw createNodeError(err, "unlink", path);
183
+ }
68
184
  }
69
185
  function writeFileSync(path, data) {
70
186
  GLib.file_set_contents(path, data);
71
187
  }
188
+ function renameSync(oldPath, newPath) {
189
+ const src = Gio.File.new_for_path(oldPath.toString());
190
+ const dest = Gio.File.new_for_path(newPath.toString());
191
+ try {
192
+ src.move(dest, Gio.FileCopyFlags.OVERWRITE, null, null);
193
+ } catch (err) {
194
+ throw createNodeError(err, "rename", oldPath, newPath);
195
+ }
196
+ }
197
+ function copyFileSync(src, dest, mode) {
198
+ const srcFile = Gio.File.new_for_path(src.toString());
199
+ const destFile = Gio.File.new_for_path(dest.toString());
200
+ let flags = Gio.FileCopyFlags.NONE;
201
+ if (mode && (mode & 1) === 0) {
202
+ flags = Gio.FileCopyFlags.OVERWRITE;
203
+ } else if (!mode) {
204
+ flags = Gio.FileCopyFlags.OVERWRITE;
205
+ }
206
+ try {
207
+ srcFile.copy(destFile, flags, null, null);
208
+ } catch (err) {
209
+ throw createNodeError(err, "copyfile", src, dest);
210
+ }
211
+ }
212
+ function accessSync(path, mode) {
213
+ const file = Gio.File.new_for_path(path.toString());
214
+ try {
215
+ const info = file.query_info("access::*", Gio.FileQueryInfoFlags.NONE, null);
216
+ if (mode !== void 0 && mode !== 0) {
217
+ const permErr = { code: 14, message: `permission denied, access '${path}'` };
218
+ if (mode & 4 && !info.get_attribute_boolean("access::can-read")) {
219
+ throw createNodeError(permErr, "access", path);
220
+ }
221
+ if (mode & 2 && !info.get_attribute_boolean("access::can-write")) {
222
+ throw createNodeError(permErr, "access", path);
223
+ }
224
+ if (mode & 1 && !info.get_attribute_boolean("access::can-execute")) {
225
+ throw createNodeError(permErr, "access", path);
226
+ }
227
+ }
228
+ } catch (err) {
229
+ if (err.code && typeof err.code === "string") throw err;
230
+ throw createNodeError(err, "access", path);
231
+ }
232
+ }
233
+ function appendFileSync(path, data, options) {
234
+ const file = Gio.File.new_for_path(path.toString());
235
+ let bytes;
236
+ if (typeof data === "string") {
237
+ bytes = new TextEncoder().encode(data);
238
+ } else {
239
+ bytes = data;
240
+ }
241
+ try {
242
+ const stream = file.append_to(Gio.FileCreateFlags.NONE, null);
243
+ if (bytes.length > 0) {
244
+ stream.write_bytes(new GLib.Bytes(bytes), null);
245
+ }
246
+ stream.close(null);
247
+ } catch (err) {
248
+ throw createNodeError(err, "appendfile", path);
249
+ }
250
+ }
251
+ function readlinkSync(path, options) {
252
+ const file = Gio.File.new_for_path(path.toString());
253
+ try {
254
+ const info = file.query_info("standard::symlink-target", Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, null);
255
+ const target = info.get_symlink_target();
256
+ if (!target) {
257
+ throw Object.assign(new Error(`EINVAL: invalid argument, readlink '${path}'`), { code: "EINVAL", errno: -22, syscall: "readlink", path: path.toString() });
258
+ }
259
+ const encoding = typeof options === "string" ? options : options?.encoding;
260
+ if (encoding === "buffer") {
261
+ return Buffer.from(target);
262
+ }
263
+ return target;
264
+ } catch (err) {
265
+ if (typeof err.code === "string") throw err;
266
+ throw createNodeError(err, "readlink", path);
267
+ }
268
+ }
269
+ function linkSync(existingPath, newPath) {
270
+ const result = GLib.spawn_command_line_sync(`ln ${existingPath.toString()} ${newPath.toString()}`);
271
+ if (!result[0]) {
272
+ throw Object.assign(new Error(`EPERM: operation not permitted, link '${existingPath}' -> '${newPath}'`), { code: "EPERM", errno: -1, syscall: "link", path: existingPath.toString(), dest: newPath.toString() });
273
+ }
274
+ }
275
+ function truncateSync(path, len) {
276
+ const file = Gio.File.new_for_path(path.toString());
277
+ try {
278
+ const stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
279
+ if (len && len > 0) {
280
+ const [, data] = file.load_contents(null);
281
+ const truncated = data.slice(0, len);
282
+ if (truncated.length > 0) {
283
+ stream.write_bytes(new GLib.Bytes(truncated), null);
284
+ }
285
+ }
286
+ stream.close(null);
287
+ } catch (err) {
288
+ throw createNodeError(err, "truncate", path);
289
+ }
290
+ }
291
+ function chmodSync(path, mode) {
292
+ const modeNum = typeof mode === "string" ? parseInt(mode, 8) : mode;
293
+ const result = GLib.spawn_command_line_sync(`chmod ${modeNum.toString(8)} ${path.toString()}`);
294
+ if (!result[0]) {
295
+ throw Object.assign(new Error(`EPERM: operation not permitted, chmod '${path}'`), { code: "EPERM", errno: -1, syscall: "chmod", path: path.toString() });
296
+ }
297
+ }
298
+ function chownSync(path, uid, gid) {
299
+ const result = GLib.spawn_command_line_sync(`chown ${uid}:${gid} ${path.toString()}`);
300
+ if (!result[0]) {
301
+ throw Object.assign(new Error(`EPERM: operation not permitted, chown '${path}'`), { code: "EPERM", errno: -1, syscall: "chown", path: path.toString() });
302
+ }
303
+ }
72
304
  function watch(filename, options, listener) {
73
305
  return new FSWatcher(filename, options, listener);
74
306
  }
@@ -85,41 +317,58 @@ function mkdtempSync(prefix, options) {
85
317
  return decode(path, encoding);
86
318
  }
87
319
  function rmSync(path, options) {
88
- const file = Gio.File.new_for_path(path.toString());
320
+ const pathStr = path.toString();
321
+ const file = Gio.File.new_for_path(pathStr);
89
322
  const recursive = options?.recursive || false;
90
- const dirent = new Dirent(path.toString());
323
+ const force = options?.force || false;
324
+ let dirent;
325
+ try {
326
+ dirent = new Dirent(pathStr);
327
+ } catch (err) {
328
+ if (force && isNotFoundError(err)) return;
329
+ throw createNodeError(err, "rm", path);
330
+ }
91
331
  if (dirent.isDirectory()) {
92
332
  const childFiles = readdirSync(path, { withFileTypes: true });
93
333
  if (!recursive && childFiles.length) {
94
- throw new Error("Dir is not empty!");
334
+ const err = Object.assign(new Error(), { code: 5 });
335
+ throw createNodeError(err, "rm", path);
95
336
  }
96
337
  for (const childFile of childFiles) {
97
- if (childFile.isDirectory()) {
98
- rmdirSync(join(path.toString(), childFile.name), options);
99
- } else if (childFile.isFile()) {
100
- rmSync(join(path.toString(), childFile.name), options);
338
+ if (typeof childFile !== "string") {
339
+ rmSync(join(pathStr, childFile.name), options);
101
340
  }
102
341
  }
103
342
  }
104
- const ok = file.delete(null);
105
- if (!ok) {
106
- const err = new Error("failed to remove file " + path);
107
- throw err;
343
+ try {
344
+ file.delete(null);
345
+ } catch (err) {
346
+ if (force && isNotFoundError(err)) return;
347
+ throw createNodeError(err, "rm", path);
108
348
  }
109
349
  }
110
350
  export {
351
+ accessSync,
352
+ appendFileSync,
353
+ chmodSync,
354
+ chownSync,
355
+ copyFileSync,
111
356
  existsSync,
357
+ linkSync,
112
358
  lstatSync,
113
359
  mkdirSync,
114
360
  mkdtempSync,
115
361
  openSync,
116
362
  readFileSync,
117
363
  readdirSync,
364
+ readlinkSync,
118
365
  realpathSync,
366
+ renameSync,
119
367
  rmSync,
120
368
  rmdirSync,
121
369
  statSync,
122
370
  symlinkSync,
371
+ truncateSync,
123
372
  unlinkSync,
124
373
  watch,
125
374
  writeFileSync
@@ -1,8 +1,8 @@
1
- import { Writable } from "stream";
2
- import { fileURLToPath, URL } from "url";
1
+ import { Writable } from "node:stream";
2
+ import { fileURLToPath, URL } from "node:url";
3
3
  import { open, write, close } from "./callback.js";
4
- const kIsPerformingIO = Symbol("kIsPerformingIO");
5
- const kIoDone = Symbol("kIoDone");
4
+ const kIsPerformingIO = /* @__PURE__ */ Symbol("kIsPerformingIO");
5
+ const kIoDone = /* @__PURE__ */ Symbol("kIoDone");
6
6
  function toPathIfFileURL(fileURLOrPath) {
7
7
  if (!(fileURLOrPath instanceof URL)) {
8
8
  return fileURLOrPath;