@gjsify/fs 0.0.4 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +31 -2
  2. package/lib/esm/callback.js +251 -15
  3. package/lib/esm/dirent.js +47 -6
  4. package/lib/esm/encoding.js +2 -3
  5. package/lib/esm/errors.js +13 -0
  6. package/lib/esm/file-handle.js +108 -66
  7. package/lib/esm/fs-watcher.js +44 -7
  8. package/lib/esm/index.js +140 -5
  9. package/lib/esm/promises.js +290 -69
  10. package/lib/esm/read-stream.js +82 -57
  11. package/lib/esm/stats.js +138 -18
  12. package/lib/esm/sync.js +293 -44
  13. package/lib/esm/write-stream.js +4 -4
  14. package/lib/types/callback.d.ts +233 -0
  15. package/lib/types/dirent.d.ts +77 -0
  16. package/lib/types/encoding.d.ts +6 -0
  17. package/lib/types/errors.d.ts +7 -0
  18. package/lib/types/file-handle.d.ts +367 -0
  19. package/lib/types/fs-watcher.d.ts +17 -0
  20. package/lib/types/index.d.ts +149 -0
  21. package/lib/types/promises.d.ts +158 -0
  22. package/lib/types/read-stream.d.ts +21 -0
  23. package/lib/types/stats.d.ts +67 -0
  24. package/lib/types/sync.d.ts +109 -0
  25. package/lib/types/types/encoding-option.d.ts +2 -0
  26. package/lib/types/types/file-read-options.d.ts +15 -0
  27. package/lib/types/types/file-read-result.d.ts +4 -0
  28. package/lib/types/types/flag-and-open-mode.d.ts +5 -0
  29. package/lib/types/types/index.d.ts +6 -0
  30. package/lib/types/types/open-flags.d.ts +1 -0
  31. package/lib/types/types/read-options.d.ts +5 -0
  32. package/lib/types/utils.d.ts +2 -0
  33. package/lib/types/write-stream.d.ts +45 -0
  34. package/package.json +22 -35
  35. package/src/callback.spec.ts +284 -30
  36. package/src/callback.ts +352 -39
  37. package/src/dirent.ts +56 -8
  38. package/src/encoding.ts +7 -2
  39. package/src/errors.spec.ts +389 -0
  40. package/src/errors.ts +19 -0
  41. package/src/extended.spec.ts +706 -0
  42. package/src/file-handle.spec.ts +104 -23
  43. package/src/file-handle.ts +147 -79
  44. package/src/fs-watcher.ts +55 -8
  45. package/src/index.ts +146 -2
  46. package/src/new-apis.spec.ts +505 -0
  47. package/src/promises.spec.ts +651 -11
  48. package/src/promises.ts +353 -81
  49. package/src/read-stream.ts +98 -74
  50. package/src/stat.spec.ts +22 -14
  51. package/src/stats.ts +176 -75
  52. package/src/streams.spec.ts +455 -0
  53. package/src/symlink.spec.ts +176 -26
  54. package/src/sync.spec.ts +204 -32
  55. package/src/sync.ts +363 -58
  56. package/src/test.mts +7 -2
  57. package/src/types/encoding-option.ts +1 -1
  58. package/src/types/flag-and-open-mode.ts +1 -1
  59. package/src/types/read-options.ts +2 -2
  60. package/src/utils.ts +2 -0
  61. package/src/write-stream.ts +9 -7
  62. package/tsconfig.json +23 -10
  63. package/tsconfig.tsbuildinfo +1 -0
  64. package/lib/cjs/callback.js +0 -112
  65. package/lib/cjs/dirent.js +0 -98
  66. package/lib/cjs/encoding.js +0 -34
  67. package/lib/cjs/file-handle.js +0 -444
  68. package/lib/cjs/fs-watcher.js +0 -50
  69. package/lib/cjs/index.js +0 -95
  70. package/lib/cjs/promises.js +0 -160
  71. package/lib/cjs/read-stream.js +0 -78
  72. package/lib/cjs/stats.js +0 -45
  73. package/lib/cjs/sync.js +0 -126
  74. package/lib/cjs/types/encoding-option.js +0 -0
  75. package/lib/cjs/types/file-read-options.js +0 -0
  76. package/lib/cjs/types/file-read-result.js +0 -0
  77. package/lib/cjs/types/flag-and-open-mode.js +0 -0
  78. package/lib/cjs/types/index.js +0 -6
  79. package/lib/cjs/types/open-flags.js +0 -0
  80. package/lib/cjs/types/read-options.js +0 -0
  81. package/lib/cjs/utils.js +0 -18
  82. package/lib/cjs/write-stream.js +0 -116
  83. package/test/watch.js +0 -1
  84. package/test.gjs.js +0 -35359
  85. package/test.gjs.js.map +0 -7
  86. package/test.gjs.mjs +0 -40534
  87. package/test.gjs.mjs.meta.json +0 -1
  88. package/test.node.js +0 -1479
  89. package/test.node.js.map +0 -7
  90. package/test.node.mjs +0 -710
  91. package/tsconfig.types.json +0 -8
@@ -0,0 +1,389 @@
1
+ // Extended fs tests — error handling, constants, path edge cases, readdir options
2
+ // Ported from refs/node-test/parallel/test-fs-*.js
3
+ // Original: MIT license, Node.js contributors
4
+
5
+ import { describe, it, expect } from '@gjsify/unit';
6
+ import * as fs from 'node:fs';
7
+ import * as path from 'node:path';
8
+ import { tmpdir } from 'node:os';
9
+
10
+ export default async () => {
11
+
12
+ // ===================== Module exports =====================
13
+ await describe('fs module exports', async () => {
14
+ await it('should export readFileSync', async () => {
15
+ expect(typeof fs.readFileSync).toBe('function');
16
+ });
17
+ await it('should export writeFileSync', async () => {
18
+ expect(typeof fs.writeFileSync).toBe('function');
19
+ });
20
+ await it('should export existsSync', async () => {
21
+ expect(typeof fs.existsSync).toBe('function');
22
+ });
23
+ await it('should export mkdirSync', async () => {
24
+ expect(typeof fs.mkdirSync).toBe('function');
25
+ });
26
+ await it('should export rmdirSync', async () => {
27
+ expect(typeof fs.rmdirSync).toBe('function');
28
+ });
29
+ await it('should export unlinkSync', async () => {
30
+ expect(typeof fs.unlinkSync).toBe('function');
31
+ });
32
+ await it('should export statSync', async () => {
33
+ expect(typeof fs.statSync).toBe('function');
34
+ });
35
+ await it('should export lstatSync', async () => {
36
+ expect(typeof fs.lstatSync).toBe('function');
37
+ });
38
+ await it('should export readdirSync', async () => {
39
+ expect(typeof fs.readdirSync).toBe('function');
40
+ });
41
+ await it('should export renameSync', async () => {
42
+ expect(typeof fs.renameSync).toBe('function');
43
+ });
44
+ await it('should export copyFileSync', async () => {
45
+ expect(typeof fs.copyFileSync).toBe('function');
46
+ });
47
+ await it('should export accessSync', async () => {
48
+ expect(typeof fs.accessSync).toBe('function');
49
+ });
50
+ await it('should export chmodSync', async () => {
51
+ expect(typeof fs.chmodSync).toBe('function');
52
+ });
53
+ await it('should export realpathSync', async () => {
54
+ expect(typeof fs.realpathSync).toBe('function');
55
+ });
56
+ await it('should export createReadStream', async () => {
57
+ expect(typeof fs.createReadStream).toBe('function');
58
+ });
59
+ await it('should export createWriteStream', async () => {
60
+ expect(typeof fs.createWriteStream).toBe('function');
61
+ });
62
+ await it('should export promises', async () => {
63
+ expect(typeof fs.promises).toBe('object');
64
+ });
65
+
66
+ // Callback exports
67
+ await it('should export readFile (callback)', async () => {
68
+ expect(typeof fs.readFile).toBe('function');
69
+ });
70
+ await it('should export writeFile (callback)', async () => {
71
+ expect(typeof fs.writeFile).toBe('function');
72
+ });
73
+ await it('should export stat (callback)', async () => {
74
+ expect(typeof fs.stat).toBe('function');
75
+ });
76
+ await it('should export mkdir (callback)', async () => {
77
+ expect(typeof fs.mkdir).toBe('function');
78
+ });
79
+ await it('should export readdir (callback)', async () => {
80
+ expect(typeof fs.readdir).toBe('function');
81
+ });
82
+ await it('should export rename (callback)', async () => {
83
+ expect(typeof fs.rename).toBe('function');
84
+ });
85
+ await it('should export unlink (callback)', async () => {
86
+ expect(typeof fs.unlink).toBe('function');
87
+ });
88
+
89
+ // watch/watchFile
90
+ await it('should export watch', async () => {
91
+ expect(typeof fs.watch).toBe('function');
92
+ });
93
+ // watchFile/unwatchFile not yet implemented in GJS
94
+ // await it('should export watchFile', ...)
95
+ // await it('should export unwatchFile', ...)
96
+ });
97
+
98
+ // ===================== fs.constants =====================
99
+ await describe('fs.constants comprehensive', async () => {
100
+ await it('should have F_OK', async () => {
101
+ expect(typeof fs.constants.F_OK).toBe('number');
102
+ expect(fs.constants.F_OK).toBe(0);
103
+ });
104
+ await it('should have R_OK', async () => {
105
+ expect(typeof fs.constants.R_OK).toBe('number');
106
+ });
107
+ await it('should have W_OK', async () => {
108
+ expect(typeof fs.constants.W_OK).toBe('number');
109
+ });
110
+ await it('should have X_OK', async () => {
111
+ expect(typeof fs.constants.X_OK).toBe('number');
112
+ });
113
+ await it('should have O_RDONLY', async () => {
114
+ expect(fs.constants.O_RDONLY).toBe(0);
115
+ });
116
+ await it('should have O_WRONLY', async () => {
117
+ expect(fs.constants.O_WRONLY).toBe(1);
118
+ });
119
+ await it('should have O_RDWR', async () => {
120
+ expect(fs.constants.O_RDWR).toBe(2);
121
+ });
122
+ await it('should have O_CREAT', async () => {
123
+ expect(typeof fs.constants.O_CREAT).toBe('number');
124
+ });
125
+ await it('should have O_EXCL', async () => {
126
+ expect(typeof fs.constants.O_EXCL).toBe('number');
127
+ });
128
+ await it('should have O_TRUNC', async () => {
129
+ expect(typeof fs.constants.O_TRUNC).toBe('number');
130
+ });
131
+ await it('should have O_APPEND', async () => {
132
+ expect(typeof fs.constants.O_APPEND).toBe('number');
133
+ });
134
+ await it('should have S_IFMT', async () => {
135
+ expect(typeof fs.constants.S_IFMT).toBe('number');
136
+ });
137
+ await it('should have S_IFREG', async () => {
138
+ expect(typeof fs.constants.S_IFREG).toBe('number');
139
+ });
140
+ await it('should have S_IFDIR', async () => {
141
+ expect(typeof fs.constants.S_IFDIR).toBe('number');
142
+ });
143
+ await it('should have S_IFLNK', async () => {
144
+ expect(typeof fs.constants.S_IFLNK).toBe('number');
145
+ });
146
+ await it('should have COPYFILE_EXCL', async () => {
147
+ expect(typeof fs.constants.COPYFILE_EXCL).toBe('number');
148
+ });
149
+ });
150
+
151
+ // ===================== ENOENT error handling =====================
152
+ await describe('fs ENOENT error handling', async () => {
153
+ const nonExistent = path.join(tmpdir(), 'gjsify-test-nonexistent-' + Date.now());
154
+
155
+ await it('readFileSync should throw ENOENT for missing file', async () => {
156
+ let thrown = false;
157
+ try {
158
+ fs.readFileSync(nonExistent);
159
+ } catch (err: any) {
160
+ thrown = true;
161
+ expect(err.code).toBe('ENOENT');
162
+ }
163
+ expect(thrown).toBe(true);
164
+ });
165
+
166
+ await it('statSync should throw ENOENT for missing file', async () => {
167
+ let thrown = false;
168
+ try {
169
+ fs.statSync(nonExistent);
170
+ } catch (err: any) {
171
+ thrown = true;
172
+ expect(err.code).toBe('ENOENT');
173
+ }
174
+ expect(thrown).toBe(true);
175
+ });
176
+
177
+ await it('unlinkSync should throw ENOENT for missing file', async () => {
178
+ let thrown = false;
179
+ try {
180
+ fs.unlinkSync(nonExistent);
181
+ } catch (err: any) {
182
+ thrown = true;
183
+ expect(err.code).toBe('ENOENT');
184
+ }
185
+ expect(thrown).toBe(true);
186
+ });
187
+
188
+ await it('readFile callback should receive ENOENT', async () => {
189
+ const err = await new Promise<NodeJS.ErrnoException | null>((resolve) => {
190
+ fs.readFile(nonExistent, (e) => resolve(e));
191
+ });
192
+ expect(err).toBeDefined();
193
+ expect(err!.code).toBe('ENOENT');
194
+ });
195
+
196
+ await it('stat callback should receive ENOENT', async () => {
197
+ const err = await new Promise<NodeJS.ErrnoException | null>((resolve) => {
198
+ fs.stat(nonExistent, (e) => resolve(e));
199
+ });
200
+ expect(err).toBeDefined();
201
+ expect(err!.code).toBe('ENOENT');
202
+ });
203
+
204
+ await it('fs.promises.readFile should reject with ENOENT', async () => {
205
+ let caught = false;
206
+ try {
207
+ await fs.promises.readFile(nonExistent);
208
+ } catch (err: any) {
209
+ caught = true;
210
+ expect(err.code).toBe('ENOENT');
211
+ }
212
+ expect(caught).toBe(true);
213
+ });
214
+
215
+ await it('fs.promises.stat should reject with ENOENT', async () => {
216
+ let caught = false;
217
+ try {
218
+ await fs.promises.stat(nonExistent);
219
+ } catch (err: any) {
220
+ caught = true;
221
+ expect(err.code).toBe('ENOENT');
222
+ }
223
+ expect(caught).toBe(true);
224
+ });
225
+
226
+ await it('existsSync should return false for missing file', async () => {
227
+ expect(fs.existsSync(nonExistent)).toBe(false);
228
+ });
229
+
230
+ await it('accessSync should throw ENOENT for missing file', async () => {
231
+ let thrown = false;
232
+ try {
233
+ fs.accessSync(nonExistent);
234
+ } catch (err: any) {
235
+ thrown = true;
236
+ expect(err.code).toBe('ENOENT');
237
+ }
238
+ expect(thrown).toBe(true);
239
+ });
240
+ });
241
+
242
+ // ===================== existsSync edge cases =====================
243
+ await describe('fs.existsSync edge cases', async () => {
244
+ await it('should return true for /', async () => {
245
+ expect(fs.existsSync('/')).toBe(true);
246
+ });
247
+ await it('should return true for /tmp', async () => {
248
+ expect(fs.existsSync('/tmp')).toBe(true);
249
+ });
250
+ await it('should return true for tmpdir()', async () => {
251
+ expect(fs.existsSync(tmpdir())).toBe(true);
252
+ });
253
+ await it('should return false for empty string', async () => {
254
+ expect(fs.existsSync('')).toBe(false);
255
+ });
256
+ });
257
+
258
+ // ===================== readdir with options =====================
259
+ await describe('fs.readdirSync options', async () => {
260
+ await it('should return string array by default', async () => {
261
+ const entries = fs.readdirSync('/tmp');
262
+ expect(Array.isArray(entries)).toBe(true);
263
+ if (entries.length > 0) {
264
+ expect(typeof entries[0]).toBe('string');
265
+ }
266
+ });
267
+
268
+ await it('should return Dirent array with withFileTypes', async () => {
269
+ const entries = fs.readdirSync('/tmp', { withFileTypes: true });
270
+ expect(Array.isArray(entries)).toBe(true);
271
+ if (entries.length > 0) {
272
+ const d = entries[0];
273
+ expect(typeof d.name).toBe('string');
274
+ expect(typeof d.isFile).toBe('function');
275
+ expect(typeof d.isDirectory).toBe('function');
276
+ expect(typeof d.isSymbolicLink).toBe('function');
277
+ }
278
+ });
279
+
280
+ await it('should return string with utf8 encoding', async () => {
281
+ const entries = fs.readdirSync('/tmp', { encoding: 'utf8' });
282
+ expect(Array.isArray(entries)).toBe(true);
283
+ if (entries.length > 0) {
284
+ expect(typeof entries[0]).toBe('string');
285
+ }
286
+ });
287
+ });
288
+
289
+ // ===================== mkdirSync recursive =====================
290
+ await describe('fs.mkdirSync recursive', async () => {
291
+ const baseDir = path.join(tmpdir(), 'gjsify-mkdir-recursive-' + Date.now());
292
+
293
+ await it('should create nested directories', async () => {
294
+ const nested = path.join(baseDir, 'a', 'b', 'c');
295
+ fs.mkdirSync(nested, { recursive: true });
296
+ expect(fs.existsSync(nested)).toBe(true);
297
+ expect(fs.statSync(nested).isDirectory()).toBe(true);
298
+ });
299
+
300
+ await it('should not throw if directory already exists with recursive', async () => {
301
+ expect(() => fs.mkdirSync(baseDir, { recursive: true })).not.toThrow();
302
+ });
303
+
304
+ await it('should throw EEXIST without recursive for existing dir', async () => {
305
+ let thrown = false;
306
+ try {
307
+ fs.mkdirSync(baseDir);
308
+ } catch (err: any) {
309
+ thrown = true;
310
+ expect(err.code).toBe('EEXIST');
311
+ }
312
+ expect(thrown).toBe(true);
313
+ });
314
+
315
+ // Cleanup
316
+ await it('cleanup', async () => {
317
+ fs.rmSync(baseDir, { recursive: true, force: true });
318
+ expect(fs.existsSync(baseDir)).toBe(false);
319
+ });
320
+ });
321
+
322
+ // ===================== writeFileSync/readFileSync round-trip =====================
323
+ await describe('fs read/write round-trip', async () => {
324
+ const testFile = path.join(tmpdir(), 'gjsify-roundtrip-' + Date.now() + '.txt');
325
+
326
+ await it('should write and read string content', async () => {
327
+ fs.writeFileSync(testFile, 'hello world');
328
+ const content = fs.readFileSync(testFile, 'utf8');
329
+ expect(content).toBe('hello world');
330
+ });
331
+
332
+ await it('should write and read with encoding', async () => {
333
+ fs.writeFileSync(testFile, 'überschreiben');
334
+ const content = fs.readFileSync(testFile, 'utf8');
335
+ expect(content).toBe('überschreiben');
336
+ });
337
+
338
+ await it('should write and read Buffer content', async () => {
339
+ const buf = Buffer.from([0x01, 0x02, 0x03, 0x04]);
340
+ fs.writeFileSync(testFile, buf);
341
+ const content = fs.readFileSync(testFile);
342
+ expect(content.length).toBe(4);
343
+ expect(content[0]).toBe(1);
344
+ expect(content[3]).toBe(4);
345
+ });
346
+
347
+ await it('should overwrite existing file', async () => {
348
+ fs.writeFileSync(testFile, 'first');
349
+ fs.writeFileSync(testFile, 'second');
350
+ expect(fs.readFileSync(testFile, 'utf8')).toBe('second');
351
+ });
352
+
353
+ await it('should write empty file', async () => {
354
+ fs.writeFileSync(testFile, '');
355
+ expect(fs.readFileSync(testFile, 'utf8')).toBe('');
356
+ });
357
+
358
+ // Cleanup
359
+ await it('cleanup', async () => {
360
+ if (fs.existsSync(testFile)) fs.unlinkSync(testFile);
361
+ });
362
+ });
363
+
364
+ // ===================== appendFileSync =====================
365
+ await describe('fs.appendFileSync', async () => {
366
+ const testFile = path.join(tmpdir(), 'gjsify-append-' + Date.now() + '.txt');
367
+
368
+ await it('should create file if not exists', async () => {
369
+ fs.appendFileSync(testFile, 'hello');
370
+ expect(fs.readFileSync(testFile, 'utf8')).toBe('hello');
371
+ });
372
+
373
+ await it('should append to existing file', async () => {
374
+ fs.appendFileSync(testFile, ' world');
375
+ expect(fs.readFileSync(testFile, 'utf8')).toBe('hello world');
376
+ });
377
+
378
+ await it('should append multiple times', async () => {
379
+ fs.appendFileSync(testFile, '!');
380
+ fs.appendFileSync(testFile, '!');
381
+ expect(fs.readFileSync(testFile, 'utf8')).toBe('hello world!!');
382
+ });
383
+
384
+ // Cleanup
385
+ await it('cleanup', async () => {
386
+ if (fs.existsSync(testFile)) fs.unlinkSync(testFile);
387
+ });
388
+ });
389
+ };
package/src/errors.ts ADDED
@@ -0,0 +1,19 @@
1
+ // Reference: Node.js lib/internal/errors.js — filesystem error helpers
2
+ // Reimplemented for GJS using Gio error codes
3
+
4
+ import type { PathLike } from 'node:fs';
5
+ import { createNodeError as createNodeErrorGeneric, isNotFoundError } from '@gjsify/utils';
6
+
7
+ export { isNotFoundError };
8
+
9
+ /**
10
+ * Create a Node.js-style ErrnoException from a Gio error, with fs-specific path/dest fields.
11
+ */
12
+ export function createNodeError(err: any, syscall: string, path: PathLike, dest?: PathLike): NodeJS.ErrnoException {
13
+ const pathStr = path.toString();
14
+ const error = createNodeErrorGeneric(err, syscall, {
15
+ path: pathStr,
16
+ dest: dest?.toString(),
17
+ });
18
+ return error;
19
+ }