@gjsify/fs 0.0.4 → 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 -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,47 +1,98 @@
1
1
  import Gio from "@girs/gio-2.0";
2
2
  import GLib from "@girs/glib-2.0";
3
- import { warnNotImplemented } from "@gjsify/utils";
4
- import { join } from "path";
3
+ import { join, dirname } from "node:path";
5
4
  import { getEncodingFromOptions, encodeUint8Array, decode } from "./encoding.js";
6
- import { writeFileSync, mkdirSync, rmdirSync, unlinkSync } from "./sync.js";
5
+ import { realpathSync, readdirSync as readdirSyncFn, renameSync, copyFileSync, accessSync, appendFileSync, readlinkSync, truncateSync, chmodSync, chownSync, linkSync } from "./sync.js";
7
6
  import { FileHandle } from "./file-handle.js";
8
7
  import { tempDirPath } from "./utils.js";
9
8
  import { Dirent } from "./dirent.js";
10
- import { realpathPromise as realpath } from "@gjsify/deno_std/node/_fs/_fs_realpath";
11
- import { readdirPromise as readdir } from "@gjsify/deno_std/node/_fs/_fs_readdir";
12
- import { symlinkPromise as symlink } from "@gjsify/deno_std/node/_fs/_fs_symlink";
13
- import { lstatPromise as lstat } from "@gjsify/deno_std/node/_fs/_fs_lstat";
14
- import { statPromise as stat } from "@gjsify/deno_std/node/_fs/_fs_stat";
9
+ import { Stats, BigIntStats, STAT_ATTRIBUTES } from "./stats.js";
10
+ import { createNodeError } from "./errors.js";
15
11
  async function mkdir(path, options) {
16
12
  let recursive;
17
- let mode = 511;
13
+ let _mode = 511;
18
14
  if (typeof options === "object") {
19
- if (options.recursive)
20
- recursive = options.recursive;
21
- if (options.mode)
22
- mode = options.mode;
15
+ if (options.recursive) recursive = options.recursive;
16
+ if (options.mode) _mode = options.mode;
23
17
  } else {
24
- mode = options;
18
+ _mode = options;
25
19
  }
26
- const firstPath = mkdirSync(path, {
27
- recursive,
28
- mode
20
+ const pathStr = path.toString();
21
+ if (recursive) {
22
+ return mkdirRecursiveAsync(pathStr);
23
+ }
24
+ const file = Gio.File.new_for_path(pathStr);
25
+ return new Promise((resolve, reject) => {
26
+ file.make_directory_async(GLib.PRIORITY_DEFAULT, null, (_s, res) => {
27
+ try {
28
+ file.make_directory_finish(res);
29
+ resolve(void 0);
30
+ } catch (err) {
31
+ reject(createNodeError(err, "mkdir", path));
32
+ }
33
+ });
29
34
  });
30
- return firstPath;
35
+ }
36
+ async function mkdirRecursiveAsync(pathStr) {
37
+ const file = Gio.File.new_for_path(pathStr);
38
+ try {
39
+ await new Promise((resolve, reject) => {
40
+ file.make_directory_async(GLib.PRIORITY_DEFAULT, null, (_s, res) => {
41
+ try {
42
+ file.make_directory_finish(res);
43
+ resolve();
44
+ } catch (err) {
45
+ reject(err);
46
+ }
47
+ });
48
+ });
49
+ return pathStr;
50
+ } catch (err) {
51
+ const gErr = err;
52
+ if (gErr.code === Gio.IOErrorEnum.EXISTS) {
53
+ return void 0;
54
+ }
55
+ if (gErr.code === Gio.IOErrorEnum.NOT_FOUND) {
56
+ const parentPath = dirname(pathStr);
57
+ if (parentPath === pathStr) {
58
+ throw createNodeError(err, "mkdir", pathStr);
59
+ }
60
+ const firstCreated = await mkdirRecursiveAsync(parentPath);
61
+ const retryFile = Gio.File.new_for_path(pathStr);
62
+ await new Promise((resolve, reject) => {
63
+ retryFile.make_directory_async(GLib.PRIORITY_DEFAULT, null, (_s, res) => {
64
+ try {
65
+ retryFile.make_directory_finish(res);
66
+ resolve();
67
+ } catch (retryErr) {
68
+ reject(createNodeError(retryErr, "mkdir", pathStr));
69
+ }
70
+ });
71
+ });
72
+ return firstCreated ?? pathStr;
73
+ }
74
+ throw createNodeError(err, "mkdir", pathStr);
75
+ }
31
76
  }
32
77
  async function readFile(path, options = { encoding: null, flag: "r" }) {
33
78
  const file = Gio.File.new_for_path(path.toString());
34
- const [ok, data] = await new Promise((resolve, reject) => {
35
- file.load_contents_async(null, (self, res) => {
36
- try {
37
- resolve(file.load_contents_finish(res));
38
- } catch (error) {
39
- reject(error);
40
- }
79
+ let ok;
80
+ let data;
81
+ try {
82
+ [ok, data] = await new Promise((resolve, reject) => {
83
+ file.load_contents_async(null, (self, res) => {
84
+ try {
85
+ resolve(file.load_contents_finish(res));
86
+ } catch (error) {
87
+ reject(error);
88
+ }
89
+ });
41
90
  });
42
- });
91
+ } catch (error) {
92
+ throw createNodeError(error, "open", path.toString());
93
+ }
43
94
  if (!ok) {
44
- throw new Error("failed to read file");
95
+ throw createNodeError(new Error("failed to read file"), "open", path.toString());
45
96
  }
46
97
  return encodeUint8Array(getEncodingFromOptions(options, "buffer"), data);
47
98
  }
@@ -55,20 +106,103 @@ async function mkdtemp(prefix, options) {
55
106
  return decode(path, encoding);
56
107
  }
57
108
  async function writeFile(path, data) {
58
- return writeFileSync(path, data);
109
+ const file = Gio.File.new_for_path(path);
110
+ let bytes;
111
+ if (typeof data === "string") {
112
+ bytes = new TextEncoder().encode(data);
113
+ } else if (data instanceof Uint8Array) {
114
+ bytes = data;
115
+ } else {
116
+ bytes = new TextEncoder().encode(String(data));
117
+ }
118
+ const outputStream = await new Promise((resolve, reject) => {
119
+ file.replace_async(null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, GLib.PRIORITY_DEFAULT, null, (_s, res) => {
120
+ try {
121
+ resolve(file.replace_finish(res));
122
+ } catch (err) {
123
+ reject(createNodeError(err, "open", path));
124
+ }
125
+ });
126
+ });
127
+ if (bytes.length > 0) {
128
+ const glibBytes = new GLib.Bytes(bytes);
129
+ await new Promise((resolve, reject) => {
130
+ outputStream.write_bytes_async(glibBytes, GLib.PRIORITY_DEFAULT, null, (_s, res) => {
131
+ try {
132
+ outputStream.write_bytes_finish(res);
133
+ resolve();
134
+ } catch (err) {
135
+ reject(createNodeError(err, "write", path));
136
+ }
137
+ });
138
+ });
139
+ }
140
+ await new Promise((resolve, reject) => {
141
+ outputStream.close_async(GLib.PRIORITY_DEFAULT, null, (_s, res) => {
142
+ try {
143
+ outputStream.close_finish(res);
144
+ resolve();
145
+ } catch (err) {
146
+ reject(createNodeError(err, "close", path));
147
+ }
148
+ });
149
+ });
59
150
  }
60
- async function rmdir(path, options) {
61
- return rmdirSync(path, options);
151
+ async function rmdir(path, _options) {
152
+ const file = Gio.File.new_for_path(path.toString());
153
+ const info = await new Promise((resolve, reject) => {
154
+ file.query_info_async("standard::type", Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_DEFAULT, null, (_s, res) => {
155
+ try {
156
+ resolve(file.query_info_finish(res));
157
+ } catch (err) {
158
+ reject(createNodeError(err, "rmdir", path));
159
+ }
160
+ });
161
+ });
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 children = await new Promise((resolve, reject) => {
167
+ file.enumerate_children_async("standard::name", Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_DEFAULT, null, (_s, res) => {
168
+ try {
169
+ resolve(file.enumerate_children_finish(res));
170
+ } catch (err) {
171
+ reject(createNodeError(err, "rmdir", path));
172
+ }
173
+ });
174
+ });
175
+ const firstChild = children.next_file(null);
176
+ if (firstChild !== null) {
177
+ const err = Object.assign(new Error(), { code: 5 });
178
+ throw createNodeError(err, "rmdir", path);
179
+ }
180
+ await new Promise((resolve, reject) => {
181
+ file.delete_async(GLib.PRIORITY_DEFAULT, null, (_s, res) => {
182
+ try {
183
+ file.delete_finish(res);
184
+ resolve();
185
+ } catch (err) {
186
+ reject(createNodeError(err, "rmdir", path));
187
+ }
188
+ });
189
+ });
62
190
  }
63
191
  async function unlink(path) {
64
- return unlinkSync(path);
192
+ const file = Gio.File.new_for_path(path);
193
+ await new Promise((resolve, reject) => {
194
+ file.delete_async(GLib.PRIORITY_DEFAULT, null, (_s, res) => {
195
+ try {
196
+ file.delete_finish(res);
197
+ resolve();
198
+ } catch (err) {
199
+ reject(createNodeError(err, "unlink", path));
200
+ }
201
+ });
202
+ });
65
203
  }
66
204
  async function open(path, flags, mode) {
67
- return new FileHandle({
68
- path,
69
- flags,
70
- mode
71
- });
205
+ return new FileHandle({ path, flags, mode });
72
206
  }
73
207
  async function write(fd, data, positionOrOffset, encodingOrLength, position) {
74
208
  if (typeof data === "string") {
@@ -77,53 +211,122 @@ async function write(fd, data, positionOrOffset, encodingOrLength, position) {
77
211
  return _writeBuf(fd, data, positionOrOffset, encodingOrLength, position);
78
212
  }
79
213
  async function _writeBuf(fd, buffer, offset, length, position) {
80
- warnNotImplemented("fs.promises.write");
81
- return {
82
- bytesWritten: 0,
83
- buffer
84
- };
214
+ const fileHandle = FileHandle.getInstance(fd);
215
+ const result = await fileHandle.write(buffer, offset, length, position);
216
+ return { bytesWritten: result.bytesWritten, buffer };
85
217
  }
86
218
  async function _writeStr(fd, data, position, encoding) {
87
- warnNotImplemented("fs.promises.write");
88
- return {
89
- bytesWritten: 0,
90
- buffer: data
91
- };
219
+ const fileHandle = FileHandle.getInstance(fd);
220
+ const result = await fileHandle.write(data, position, encoding);
221
+ return { bytesWritten: result.bytesWritten, buffer: data };
222
+ }
223
+ function queryInfoAsync(path, flags, syscall, options) {
224
+ return new Promise((resolve, reject) => {
225
+ const file = Gio.File.new_for_path(path.toString());
226
+ file.query_info_async(STAT_ATTRIBUTES, flags, GLib.PRIORITY_DEFAULT, null, (_s, res) => {
227
+ try {
228
+ const info = file.query_info_finish(res);
229
+ resolve(options?.bigint ? new BigIntStats(info, path) : new Stats(info, path));
230
+ } catch (err) {
231
+ reject(createNodeError(err, syscall, path));
232
+ }
233
+ });
234
+ });
235
+ }
236
+ async function stat(path, options) {
237
+ return queryInfoAsync(path, Gio.FileQueryInfoFlags.NONE, "stat", options);
238
+ }
239
+ async function lstat(path, options) {
240
+ return queryInfoAsync(path, Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, "lstat", options);
241
+ }
242
+ async function readdir(path, options) {
243
+ try {
244
+ return readdirSyncFn(path, options);
245
+ } catch (error) {
246
+ throw createNodeError(error, "scandir", path);
247
+ }
248
+ }
249
+ async function realpath(path) {
250
+ return realpathSync(path);
251
+ }
252
+ async function symlink(target, path, _type) {
253
+ return new Promise((resolve, reject) => {
254
+ const file = Gio.File.new_for_path(path.toString());
255
+ file.make_symbolic_link_async(target.toString(), GLib.PRIORITY_DEFAULT, null, (_s, res) => {
256
+ try {
257
+ file.make_symbolic_link_finish(res);
258
+ resolve();
259
+ } catch (err) {
260
+ reject(createNodeError(err, "symlink", target, path));
261
+ }
262
+ });
263
+ });
92
264
  }
93
265
  async function rm(path, options) {
94
- const file = Gio.File.new_for_path(path.toString());
266
+ const pathStr = path.toString();
267
+ const file = Gio.File.new_for_path(pathStr);
95
268
  const recursive = options?.recursive || false;
96
- const dirent = new Dirent(path.toString());
269
+ const force = options?.force || false;
270
+ let dirent;
271
+ try {
272
+ dirent = new Dirent(pathStr);
273
+ } catch (err) {
274
+ if (force) return;
275
+ throw createNodeError(err, "rm", path);
276
+ }
97
277
  if (dirent.isDirectory()) {
98
278
  const childFiles = await readdir(path, { withFileTypes: true });
99
279
  if (!recursive && childFiles.length) {
100
- throw new Error("Dir is not empty!");
280
+ const err = Object.assign(new Error(), { code: 5 });
281
+ throw createNodeError(err, "rm", path);
101
282
  }
102
283
  for (const childFile of childFiles) {
103
- if (childFile.isDirectory()) {
104
- await rmdir(join(path.toString(), childFile.name), options);
105
- } else if (childFile.isFile()) {
106
- await rm(join(path.toString(), childFile.name), options);
284
+ if (typeof childFile !== "string") {
285
+ await rm(join(pathStr, childFile.name), options);
107
286
  }
108
287
  }
109
288
  }
110
- const ok = await new Promise((resolve, reject) => {
111
- try {
112
- file.delete_async(GLib.PRIORITY_DEFAULT, null, (self, res) => {
113
- try {
114
- resolve(file.delete_finish(res));
115
- } catch (error) {
116
- reject(error);
289
+ await new Promise((resolve, reject) => {
290
+ file.delete_async(GLib.PRIORITY_DEFAULT, null, (_self, res) => {
291
+ try {
292
+ file.delete_finish(res);
293
+ resolve();
294
+ } catch (err) {
295
+ if (force) {
296
+ resolve();
297
+ return;
117
298
  }
118
- });
119
- } catch (error) {
120
- reject(error);
121
- }
299
+ reject(createNodeError(err, "rm", path));
300
+ }
301
+ });
122
302
  });
123
- if (!ok) {
124
- const err = new Error("failed to remove file " + path);
125
- throw err;
126
- }
303
+ }
304
+ async function rename(oldPath, newPath) {
305
+ renameSync(oldPath, newPath);
306
+ }
307
+ async function copyFile(src, dest, mode) {
308
+ copyFileSync(src, dest, mode);
309
+ }
310
+ async function access(path, mode) {
311
+ accessSync(path, mode);
312
+ }
313
+ async function appendFile(path, data, options) {
314
+ appendFileSync(path, data, options);
315
+ }
316
+ async function readlink(path, options) {
317
+ return readlinkSync(path, options);
318
+ }
319
+ async function truncate(path, len) {
320
+ truncateSync(path, len);
321
+ }
322
+ async function chmod(path, mode) {
323
+ chmodSync(path, mode);
324
+ }
325
+ async function chown(path, uid, gid) {
326
+ chownSync(path, uid, gid);
327
+ }
328
+ async function link(existingPath, newPath) {
329
+ linkSync(existingPath, newPath);
127
330
  }
128
331
  var promises_default = {
129
332
  readFile,
@@ -139,21 +342,39 @@ var promises_default = {
139
342
  rm,
140
343
  lstat,
141
344
  symlink,
142
- stat
345
+ stat,
346
+ rename,
347
+ copyFile,
348
+ access,
349
+ appendFile,
350
+ readlink,
351
+ truncate,
352
+ chmod,
353
+ chown,
354
+ link
143
355
  };
144
356
  export {
357
+ access,
358
+ appendFile,
359
+ chmod,
360
+ chown,
361
+ copyFile,
145
362
  promises_default as default,
363
+ link,
146
364
  lstat,
147
365
  mkdir,
148
366
  mkdtemp,
149
367
  open,
150
368
  readFile,
151
369
  readdir,
370
+ readlink,
152
371
  realpath,
372
+ rename,
153
373
  rm,
154
374
  rmdir,
155
375
  stat,
156
376
  symlink,
377
+ truncate,
157
378
  unlink,
158
379
  write,
159
380
  writeFile
@@ -1,71 +1,97 @@
1
+ import Gio from "@girs/gio-2.0";
1
2
  import GLib from "@girs/glib-2.0";
2
- import { notImplemented } from "@gjsify/utils";
3
- import { Buffer } from "buffer";
4
- import { Readable } from "stream";
5
- import { URL } from "url";
6
- function fromFileUrl(url) {
7
- url = url instanceof URL ? url : new URL(url);
8
- if (url.protocol != "file:") {
9
- throw new TypeError("Must be a file URL.");
10
- }
11
- return decodeURIComponent(
12
- url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")
13
- );
14
- }
3
+ import { Buffer } from "node:buffer";
4
+ import { Readable } from "node:stream";
5
+ import { URL, fileURLToPath } from "node:url";
15
6
  class ReadStream extends Readable {
7
+ bytesRead = 0;
8
+ path;
9
+ pending = true;
10
+ fd = null;
11
+ _gioFile;
12
+ _inputStream = null;
13
+ _start;
14
+ _end;
15
+ _pos;
16
16
  close(callback) {
17
- callback(notImplemented("ReadStream.close"));
17
+ if (this._inputStream) {
18
+ try {
19
+ this._inputStream.close(null);
20
+ } catch {
21
+ }
22
+ this._inputStream = null;
23
+ }
24
+ this.destroy();
25
+ if (callback) callback(null);
18
26
  }
19
- /**
20
- * The number of bytes that have been read so far.
21
- * @since v6.4.0
22
- */
23
- bytesRead;
24
- /**
25
- * The path to the file the stream is reading from as specified in the first
26
- * 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
27
- * `Buffer`. If `fd` is specified, then`readStream.path` will be `undefined`.
28
- * @since v0.1.93
29
- */
30
- path;
31
- /**
32
- * This property is `true` if the underlying file has not been opened yet,
33
- * i.e. before the `'ready'` event is emitted.
34
- * @since v11.2.0, v10.16.0
35
- */
36
- pending;
37
27
  constructor(path, opts) {
38
- path = path instanceof URL ? fromFileUrl(path) : path;
39
- const hasBadOptions = opts && (opts.start || opts.end);
40
- if (hasBadOptions) {
41
- notImplemented(
42
- `fs.ReadStream.prototype.constructor with unsupported options (${JSON.stringify(opts)})`
43
- );
28
+ if (path instanceof URL) {
29
+ path = fileURLToPath(path);
44
30
  }
45
- const file = GLib.IOChannel.new_file(path.toString(), "r");
46
- const buffer = "";
47
31
  super({
48
- autoDestroy: true,
49
- emitClose: true,
50
- objectMode: false,
51
- read: async function(_size) {
32
+ highWaterMark: opts?.highWaterMark ?? 64 * 1024,
33
+ encoding: opts?.encoding,
34
+ objectMode: false
35
+ });
36
+ this.path = path.toString();
37
+ this._gioFile = Gio.File.new_for_path(this.path.toString());
38
+ this._start = opts?.start ?? 0;
39
+ this._end = opts?.end ?? Infinity;
40
+ this._pos = this._start;
41
+ Promise.resolve().then(() => {
42
+ if (!this._inputStream && !this.destroyed) {
52
43
  try {
53
- let n = 0;
54
- file.read(buffer, 16 * 1024, n);
55
- this.push(n ? Buffer.from(buffer.slice(0, n)) : null);
44
+ this._inputStream = this._gioFile.read(null);
45
+ this.pending = false;
46
+ this.emit("open", 0);
47
+ this.emit("ready");
48
+ if (this._start > 0 && this._inputStream.can_seek()) {
49
+ this._inputStream.seek(this._start, GLib.SeekType.SET, null);
50
+ }
56
51
  } catch (err) {
57
52
  this.destroy(err);
58
53
  }
59
- },
60
- destroy: (err, cb) => {
61
- try {
62
- file.close();
63
- } catch {
64
- }
65
- cb(err);
66
54
  }
67
55
  });
68
- this.path = path.toString();
56
+ }
57
+ _read(size) {
58
+ if (!this._inputStream) {
59
+ if (this.destroyed) return;
60
+ Promise.resolve().then(() => this._read(size));
61
+ return;
62
+ }
63
+ let toRead = size;
64
+ if (this._end !== Infinity) {
65
+ const remaining = this._end - this._pos + 1;
66
+ if (remaining <= 0) {
67
+ this.push(null);
68
+ return;
69
+ }
70
+ toRead = Math.min(size, remaining);
71
+ }
72
+ try {
73
+ const gbytes = this._inputStream.read_bytes(toRead, null);
74
+ const data = gbytes.get_data();
75
+ if (!data || data.length === 0) {
76
+ this.push(null);
77
+ return;
78
+ }
79
+ this.bytesRead += data.length;
80
+ this._pos += data.length;
81
+ this.push(Buffer.from(data));
82
+ } catch (err) {
83
+ this.destroy(err);
84
+ }
85
+ }
86
+ _destroy(error, callback) {
87
+ if (this._inputStream) {
88
+ try {
89
+ this._inputStream.close(null);
90
+ } catch {
91
+ }
92
+ this._inputStream = null;
93
+ }
94
+ callback(error);
69
95
  }
70
96
  }
71
97
  function createReadStream(path, options) {
@@ -73,6 +99,5 @@ function createReadStream(path, options) {
73
99
  }
74
100
  export {
75
101
  ReadStream,
76
- createReadStream,
77
- fromFileUrl
102
+ createReadStream
78
103
  };