@zenfs/core 2.4.1 → 2.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.
Files changed (101) hide show
  1. package/COPYING.md +24 -0
  2. package/{readme.md → README.md} +16 -74
  3. package/dist/backends/backend.d.ts +1 -1
  4. package/dist/backends/fetch.js +1 -1
  5. package/dist/backends/memory.js +1 -1
  6. package/dist/backends/passthrough.d.ts +1 -2
  7. package/dist/backends/single_buffer.d.ts +1 -1
  8. package/dist/backends/single_buffer.js +27 -24
  9. package/dist/backends/store/fs.js +4 -4
  10. package/dist/config.js +15 -15
  11. package/dist/context.js +3 -2
  12. package/dist/index.d.ts +9 -3
  13. package/dist/index.js +9 -4
  14. package/dist/internal/contexts.d.ts +5 -4
  15. package/dist/internal/devices.js +1 -1
  16. package/dist/internal/error.d.ts +11 -2
  17. package/dist/internal/error.js +38 -2
  18. package/dist/internal/file_index.js +1 -1
  19. package/dist/internal/index.d.ts +1 -0
  20. package/dist/internal/index.js +2 -1
  21. package/dist/internal/index_fs.js +1 -1
  22. package/dist/internal/inode.d.ts +51 -2
  23. package/dist/internal/inode.js +18 -2
  24. package/dist/mixins/shared.js +1 -1
  25. package/dist/node/async.d.ts +278 -0
  26. package/dist/node/async.js +518 -0
  27. package/dist/node/compat.d.ts +4 -0
  28. package/dist/node/compat.js +6 -0
  29. package/dist/node/dir.d.ts +78 -0
  30. package/dist/node/dir.js +150 -0
  31. package/dist/node/index.d.ts +8 -0
  32. package/dist/node/index.js +8 -0
  33. package/dist/{vfs → node}/promises.d.ts +10 -66
  34. package/dist/{vfs → node}/promises.js +141 -478
  35. package/dist/{vfs → node}/stats.d.ts +0 -4
  36. package/dist/{vfs → node}/stats.js +1 -16
  37. package/dist/{vfs → node}/streams.js +2 -2
  38. package/dist/node/sync.d.ts +252 -0
  39. package/dist/node/sync.js +682 -0
  40. package/dist/node/types.d.ts +21 -0
  41. package/dist/utils.d.ts +1 -7
  42. package/dist/utils.js +0 -6
  43. package/dist/vfs/acl.js +1 -1
  44. package/dist/vfs/async.d.ts +22 -278
  45. package/dist/vfs/async.js +212 -501
  46. package/dist/vfs/dir.d.ts +5 -82
  47. package/dist/vfs/dir.js +5 -233
  48. package/dist/vfs/file.d.ts +52 -13
  49. package/dist/vfs/file.js +167 -25
  50. package/dist/vfs/flags.js +1 -1
  51. package/dist/vfs/index.d.ts +2 -5
  52. package/dist/vfs/index.js +2 -5
  53. package/dist/vfs/shared.d.ts +25 -1
  54. package/dist/vfs/shared.js +6 -4
  55. package/dist/vfs/sync.d.ts +17 -245
  56. package/dist/vfs/sync.js +129 -773
  57. package/dist/vfs/watchers.d.ts +1 -1
  58. package/dist/vfs/watchers.js +2 -2
  59. package/dist/vfs/xattr.js +1 -1
  60. package/eslint.shared.js +1 -0
  61. package/package.json +7 -5
  62. package/scripts/make-index.js +5 -29
  63. package/scripts/test.js +59 -51
  64. package/tests/backend/fetch.test.ts +2 -2
  65. package/tests/backend/port.test.ts +2 -3
  66. package/tests/backend/single-buffer.test.ts +48 -1
  67. package/tests/common/casefold.test.ts +1 -1
  68. package/tests/common/context.test.ts +11 -4
  69. package/tests/common/devices.test.ts +3 -3
  70. package/tests/common/handle.test.ts +4 -3
  71. package/tests/common/inode.test.ts +2 -2
  72. package/tests/common/mounts.test.ts +1 -3
  73. package/tests/common/mutex.test.ts +1 -3
  74. package/tests/common/path.test.ts +2 -2
  75. package/tests/common/readline.test.ts +1 -1
  76. package/tests/common.ts +5 -4
  77. package/tests/fetch/fetch.ts +1 -1
  78. package/tests/fs/dir.test.ts +3 -43
  79. package/tests/fs/directory.test.ts +4 -4
  80. package/tests/fs/errors.test.ts +2 -2
  81. package/tests/fs/links.test.ts +1 -1
  82. package/tests/fs/permissions.test.ts +3 -3
  83. package/tests/fs/read.test.ts +1 -1
  84. package/tests/fs/scaling.test.ts +1 -1
  85. package/tests/fs/stat.test.ts +1 -2
  86. package/tests/fs/times.test.ts +1 -1
  87. package/tests/fs/watch.test.ts +3 -2
  88. package/tests/setup/context.ts +1 -2
  89. package/tests/setup/cow.ts +1 -1
  90. package/tests/setup/index.ts +2 -2
  91. package/tests/setup/port.ts +1 -1
  92. package/tests/setup/single-buffer.ts +1 -1
  93. package/tests/setup.ts +4 -3
  94. package/dist/vfs/types.d.ts +0 -24
  95. package/tests/assignment.ts +0 -21
  96. /package/dist/{vfs/constants.d.ts → constants.d.ts} +0 -0
  97. /package/dist/{vfs/constants.js → constants.js} +0 -0
  98. /package/dist/{readline.d.ts → node/readline.d.ts} +0 -0
  99. /package/dist/{readline.js → node/readline.js} +0 -0
  100. /package/dist/{vfs → node}/streams.d.ts +0 -0
  101. /package/dist/{vfs → node}/types.js +0 -0
package/dist/vfs/async.js CHANGED
@@ -1,518 +1,229 @@
1
- import { Buffer } from 'buffer';
2
- import { UV, withErrno } from 'kerium';
1
+ import { setUVMessage, UV } from 'kerium';
2
+ import { decodeUTF8 } from 'utilium';
3
+ import * as constants from '../constants.js';
4
+ import { defaultContext } from '../internal/contexts.js';
5
+ import { hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
6
+ import { basename, dirname, join, parse, resolve as resolvePath } from '../path.js';
3
7
  import { normalizeMode, normalizePath } from '../utils.js';
4
- import { R_OK } from './constants.js';
5
- import * as promises from './promises.js';
6
- import { BigIntStats } from './stats.js';
7
- import { ReadStream, WriteStream } from './streams.js';
8
- import { FSWatcher, StatWatcher } from './watchers.js';
9
- const nop = () => { };
8
+ import { checkAccess } from './config.js';
9
+ import { Dirent, ifToDt } from './dir.js';
10
+ import { Handle } from './file.js';
11
+ import * as flags from './flags.js';
12
+ import { resolveMount } from './shared.js';
13
+ import { emitChange } from './watchers.js';
10
14
  /**
11
- * Helper to collect an async iterator into an array
15
+ * Resolves the mount and real path for a path.
16
+ * Additionally, any stats fetched will be returned for de-duplication
17
+ * @internal @hidden
12
18
  */
13
- async function collectAsyncIterator(it) {
14
- const results = [];
15
- for await (const result of it) {
16
- results.push(result);
19
+ export async function resolve($, path, preserveSymlinks, extra) {
20
+ if (preserveSymlinks) {
21
+ const resolved = resolveMount(path, $, extra);
22
+ const stats = await resolved.fs.stat(resolved.path).catch(() => undefined);
23
+ return { ...resolved, fullPath: path, stats };
17
24
  }
18
- return results;
19
- }
20
- /**
21
- * Asynchronous rename. No arguments other than a possible exception are given to the completion callback.
22
- */
23
- export function rename(oldPath, newPath, cb = nop) {
24
- promises.rename
25
- .call(this, oldPath, newPath)
26
- .then(() => cb(null))
27
- .catch(cb);
28
- }
29
- rename;
30
- /**
31
- * Test whether or not `path` exists by checking with the file system.
32
- * Then call the callback argument with either true or false.
33
- * According to Node.js: deprecated Use {@link stat} or {@link access} instead.
34
- */
35
- export function exists(path, cb = nop) {
36
- promises.exists
37
- .call(this, path)
38
- .then(cb)
39
- .catch(() => cb(false));
40
- }
41
- exists;
42
- export function stat(path, options, callback = nop) {
43
- callback = typeof options == 'function' ? options : callback;
44
- promises.stat
45
- .call(this, path, typeof options != 'function' ? options : {})
46
- .then(stats => callback(null, stats))
47
- .catch(callback);
48
- }
49
- stat;
50
- export function lstat(path, options, callback = nop) {
51
- callback = typeof options == 'function' ? options : callback;
52
- promises.lstat
53
- .call(this, path, typeof options != 'function' ? options : {})
54
- .then(stats => callback(null, stats))
55
- .catch(callback);
56
- }
57
- lstat;
58
- export function truncate(path, cbLen = 0, cb = nop) {
59
- cb = typeof cbLen === 'function' ? cbLen : cb;
60
- const len = typeof cbLen === 'number' ? cbLen : 0;
61
- promises.truncate
62
- .call(this, path, len)
63
- .then(() => cb(null))
64
- .catch(cb);
65
- }
66
- truncate;
67
- export function unlink(path, cb = nop) {
68
- promises.unlink
69
- .call(this, path)
70
- .then(() => cb(null))
71
- .catch(cb);
72
- }
73
- unlink;
74
- export function open(path, flag, cbMode, cb = nop) {
75
- const mode = normalizeMode(cbMode, 0o644);
76
- cb = typeof cbMode === 'function' ? cbMode : cb;
77
- promises.open
78
- .call(this, path, flag, mode)
79
- .then(handle => cb(null, handle.fd))
80
- .catch(cb);
81
- }
82
- open;
83
- export function readFile(filename, options, cb = nop) {
84
- cb = typeof options === 'function' ? options : cb;
85
- promises.readFile
86
- .call(this, filename, typeof options === 'function' ? null : options)
87
- .then(data => cb(null, data))
88
- .catch(cb);
89
- }
90
- readFile;
91
- export function writeFile(filename, data, cbEncOpts, cb = nop) {
92
- cb = typeof cbEncOpts === 'function' ? cbEncOpts : cb;
93
- promises.writeFile
94
- .call(this, filename, data, typeof cbEncOpts != 'function' ? cbEncOpts : null)
95
- .then(() => cb(null))
96
- .catch(cb);
97
- }
98
- writeFile;
99
- export function appendFile(filename, data, cbEncOpts, cb = nop) {
100
- const optionsOrEncoding = typeof cbEncOpts != 'function' ? cbEncOpts : undefined;
101
- cb = typeof cbEncOpts === 'function' ? cbEncOpts : cb;
102
- promises.appendFile
103
- .call(this, filename, data, optionsOrEncoding)
104
- .then(() => cb(null))
105
- .catch(cb);
106
- }
107
- appendFile;
108
- export function fstat(fd, options, cb = nop) {
109
- cb = typeof options == 'function' ? options : cb;
110
- new promises.FileHandle(this, fd)
111
- .stat()
112
- .then(stats => cb(null, typeof options == 'object' && options?.bigint ? new BigIntStats(stats) : stats))
113
- .catch(cb);
114
- }
115
- fstat;
116
- export function close(fd, cb = nop) {
117
- new promises.FileHandle(this, fd)
118
- .close()
119
- .then(() => cb(null))
120
- .catch(cb);
121
- }
122
- close;
123
- export function ftruncate(fd, lenOrCB, cb = nop) {
124
- const length = typeof lenOrCB === 'number' ? lenOrCB : 0;
125
- cb = typeof lenOrCB === 'function' ? lenOrCB : cb;
126
- const file = new promises.FileHandle(this, fd);
127
- if (length < 0)
128
- throw withErrno('EINVAL');
129
- file.truncate(length)
130
- .then(() => cb(null))
131
- .catch(cb);
132
- }
133
- ftruncate;
134
- export function fsync(fd, cb = nop) {
135
- new promises.FileHandle(this, fd)
136
- .sync()
137
- .then(() => cb(null))
138
- .catch(cb);
139
- }
140
- fsync;
141
- export function fdatasync(fd, cb = nop) {
142
- new promises.FileHandle(this, fd)
143
- .datasync()
144
- .then(() => cb(null))
145
- .catch(cb);
146
- }
147
- fdatasync;
148
- export function write(fd, data, cbPosOff, cbLenEnc, cbPosEnc, cb = nop) {
149
- let buffer, offset, length, position, encoding;
150
- const handle = new promises.FileHandle(this, fd);
151
- if (typeof data === 'string') {
152
- // Signature 1: (fd, string, [position?, [encoding?]], cb?)
153
- encoding = 'utf8';
154
- switch (typeof cbPosOff) {
155
- case 'function':
156
- // (fd, string, cb)
157
- cb = cbPosOff;
158
- break;
159
- case 'number':
160
- // (fd, string, position, encoding?, cb?)
161
- position = cbPosOff;
162
- encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
163
- cb = typeof cbPosEnc === 'function' ? cbPosEnc : cb;
164
- break;
165
- default:
166
- // ...try to find the callback and get out of here!
167
- cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb);
168
- cb(withErrno('EINVAL'));
169
- return;
25
+ /* Try to resolve it directly. If this works,
26
+ that means we don't need to perform any resolution for parent directories. */
27
+ try {
28
+ const resolved = resolveMount(path, $);
29
+ // Stat it to make sure it exists
30
+ const stats = await resolved.fs.stat(resolved.path);
31
+ if (!isSymbolicLink(stats)) {
32
+ return { ...resolved, fullPath: path, stats };
170
33
  }
171
- buffer = Buffer.from(data);
172
- offset = 0;
173
- length = buffer.length;
174
- const _cb = cb;
175
- handle
176
- .write(buffer, offset, length, position)
177
- .then(({ bytesWritten }) => _cb(null, bytesWritten, buffer.toString(encoding)))
178
- .catch(_cb);
34
+ const target = resolvePath.call($, dirname(path), await readlink.call($, path));
35
+ return await resolve($, target, preserveSymlinks, extra);
179
36
  }
180
- else {
181
- // Signature 2: (fd, buffer, offset, length, position?, cb?)
182
- buffer = Buffer.from(data.buffer);
183
- offset = cbPosOff;
184
- length = cbLenEnc;
185
- position = typeof cbPosEnc === 'number' ? cbPosEnc : null;
186
- const _cb = (typeof cbPosEnc === 'function' ? cbPosEnc : cb);
187
- void handle
188
- .write(buffer, offset, length, position)
189
- .then(({ bytesWritten }) => _cb(null, bytesWritten, buffer))
190
- .catch(_cb);
37
+ catch {
38
+ // Go the long way
191
39
  }
40
+ const { base, dir } = parse(path);
41
+ const realDir = dir == '/' ? '/' : (await resolve($, dir, false, extra)).fullPath;
42
+ const maybePath = join(realDir, base);
43
+ const resolved = resolveMount(maybePath, $);
44
+ const stats = await resolved.fs.stat(resolved.path).catch((e) => {
45
+ if (e.code == 'ENOENT')
46
+ return;
47
+ throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath }));
48
+ });
49
+ if (!stats)
50
+ return { ...resolved, fullPath: path };
51
+ if (!isSymbolicLink(stats)) {
52
+ return { ...resolved, fullPath: maybePath, stats };
53
+ }
54
+ const target = resolvePath.call($, realDir, await readlink.call($, maybePath));
55
+ return await resolve($, target, false, extra);
192
56
  }
193
- write;
194
- /**
195
- * Read data from the file specified by `fd`.
196
- * @param buffer The buffer that the data will be written to.
197
- * @param offset The offset within the buffer where writing will start.
198
- * @param length An integer specifying the number of bytes to read.
199
- * @param position An integer specifying where to begin reading from in the file.
200
- * If position is null, data will be read from the current file position.
201
- * @param cb The number is the number of bytes read
202
- */
203
- export function read(fd, buffer, offset, length, position, cb = nop) {
204
- new promises.FileHandle(this, fd)
205
- .read(buffer, offset, length, position)
206
- .then(({ bytesRead, buffer }) => cb(null, bytesRead, buffer))
207
- .catch(cb);
208
- }
209
- read;
210
- export function fchown(fd, uid, gid, cb = nop) {
211
- new promises.FileHandle(this, fd)
212
- .chown(uid, gid)
213
- .then(() => cb(null))
214
- .catch(cb);
215
- }
216
- fchown;
217
- export function fchmod(fd, mode, cb) {
218
- new promises.FileHandle(this, fd)
219
- .chmod(mode)
220
- .then(() => cb(null))
221
- .catch(cb);
222
- }
223
- fchmod;
224
- /**
225
- * Change the file timestamps of a file referenced by the supplied file descriptor.
226
- */
227
- export function futimes(fd, atime, mtime, cb = nop) {
228
- new promises.FileHandle(this, fd)
229
- .utimes(atime, mtime)
230
- .then(() => cb(null))
231
- .catch(cb);
232
- }
233
- futimes;
234
- export function rmdir(path, cb = nop) {
235
- promises.rmdir
236
- .call(this, path)
237
- .then(() => cb(null))
238
- .catch(cb);
239
- }
240
- rmdir;
241
- /**
242
- * Asynchronous `mkdir`.
243
- * @param mode defaults to `0777`
244
- */
245
- export function mkdir(path, mode, cb = nop) {
246
- promises.mkdir
247
- .call(this, path, mode)
248
- .then(() => cb(null))
249
- .catch(cb);
250
- }
251
- mkdir;
252
- export function readdir(path, _options, cb = nop) {
253
- cb = typeof _options == 'function' ? _options : cb;
254
- const options = typeof _options != 'function' ? _options : {};
255
- promises.readdir
256
- .call(this, path, options)
257
- .then(entries => cb(null, entries))
258
- .catch(cb);
259
- }
260
- readdir;
261
- export function link(existing, newpath, cb = nop) {
262
- promises.link
263
- .call(this, existing, newpath)
264
- .then(() => cb(null))
265
- .catch(cb);
266
- }
267
- link;
268
- export function symlink(target, path, typeOrCB, cb = nop) {
269
- const type = typeof typeOrCB === 'string' ? typeOrCB : 'file';
270
- cb = typeof typeOrCB === 'function' ? typeOrCB : cb;
271
- promises.symlink
272
- .call(this, target, path, type)
273
- .then(() => cb(null))
274
- .catch(cb);
275
- }
276
- symlink;
277
- export function readlink(path, options, callback = nop) {
278
- callback = typeof options == 'function' ? options : callback;
279
- promises.readlink
280
- .call(this, path)
281
- .then(result => callback(null, result))
282
- .catch(callback);
283
- }
284
- readlink;
285
- export function chown(path, uid, gid, cb = nop) {
286
- promises.chown
287
- .call(this, path, uid, gid)
288
- .then(() => cb(null))
289
- .catch(cb);
290
- }
291
- chown;
292
- export function lchown(path, uid, gid, cb = nop) {
293
- promises.lchown
294
- .call(this, path, uid, gid)
295
- .then(() => cb(null))
296
- .catch(cb);
297
- }
298
- lchown;
299
- export function chmod(path, mode, cb = nop) {
300
- promises.chmod
301
- .call(this, path, mode)
302
- .then(() => cb(null))
303
- .catch(cb);
304
- }
305
- chmod;
306
- export function lchmod(path, mode, cb = nop) {
307
- promises.lchmod
308
- .call(this, path, mode)
309
- .then(() => cb(null))
310
- .catch(cb);
311
- }
312
- lchmod;
313
- /**
314
- * Change file timestamps of the file referenced by the supplied path.
315
- */
316
- export function utimes(path, atime, mtime, cb = nop) {
317
- promises.utimes
318
- .call(this, path, atime, mtime)
319
- .then(() => cb(null))
320
- .catch(cb);
321
- }
322
- utimes;
323
57
  /**
324
- * Change file timestamps of the file referenced by the supplied path.
58
+ * Opens a file. This helper handles the complexity of file flags.
59
+ * @internal
325
60
  */
326
- export function lutimes(path, atime, mtime, cb = nop) {
327
- promises.lutimes
328
- .call(this, path, atime, mtime)
329
- .then(() => cb(null))
330
- .catch(cb);
331
- }
332
- lutimes;
333
- export function realpath(path, arg2, cb = nop) {
334
- cb = typeof arg2 === 'function' ? arg2 : cb;
335
- promises.realpath
336
- .call(this, path, typeof arg2 === 'function' ? null : arg2)
337
- .then(result => cb(null, result))
338
- .catch(cb);
339
- }
340
- realpath;
341
- export function access(path, cbMode, cb = nop) {
342
- const mode = typeof cbMode === 'number' ? cbMode : R_OK;
343
- cb = typeof cbMode === 'function' ? cbMode : cb;
344
- promises.access
345
- .call(this, path, mode)
346
- .then(() => cb(null))
347
- .catch(cb);
348
- }
349
- access;
350
- const statWatchers = new Map();
351
- export function watchFile(path, options, listener) {
352
- const normalizedPath = normalizePath(path);
353
- const opts = typeof options != 'function' ? options : {};
354
- if (typeof options == 'function') {
355
- listener = options;
61
+ export async function open($, path, opt) {
62
+ path = normalizePath(path);
63
+ const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
64
+ const $ex = { syscall: 'open', path };
65
+ const { fs, path: resolved, stats } = await resolve($, path, opt.preserveSymlinks, $ex);
66
+ if (!stats) {
67
+ if (!(flag & constants.O_CREAT))
68
+ throw UV('ENOENT', $ex);
69
+ // Create the file
70
+ const parentStats = await fs.stat(dirname(resolved));
71
+ if (checkAccess && !hasAccess($, parentStats, constants.W_OK))
72
+ throw UV('EACCES', 'open', dirname(path));
73
+ if (!isDirectory(parentStats))
74
+ throw UV('ENOTDIR', 'open', dirname(path));
75
+ if (!opt.allowDirectory && mode & constants.S_IFDIR)
76
+ throw UV('EISDIR', 'open', path);
77
+ const { euid: uid, egid: gid } = $?.credentials ?? defaultContext.credentials;
78
+ const inode = await fs.createFile(resolved, {
79
+ mode,
80
+ uid: parentStats.mode & constants.S_ISUID ? parentStats.uid : uid,
81
+ gid: parentStats.mode & constants.S_ISGID ? parentStats.gid : gid,
82
+ });
83
+ return new Handle($, path, fs, resolved, flag, inode);
356
84
  }
357
- if (!listener)
358
- throw UV('EINVAL', 'watch', path.toString());
359
- if (statWatchers.has(normalizedPath)) {
360
- const entry = statWatchers.get(normalizedPath);
361
- if (entry) {
362
- entry.listeners.add(listener);
363
- }
85
+ if (checkAccess && !hasAccess($, stats, flags.toMode(flag)))
86
+ throw UV('EACCES', $ex);
87
+ if (flag & constants.O_EXCL)
88
+ throw UV('EEXIST', $ex);
89
+ const handle = new Handle($, path, fs, resolved, flag, stats);
90
+ if (!opt.allowDirectory && mode & constants.S_IFDIR)
91
+ throw UV('EISDIR', 'open', path);
92
+ if (flag & constants.O_TRUNC)
93
+ await handle.truncate(0);
94
+ return handle;
95
+ }
96
+ export async function readlink(path) {
97
+ path = normalizePath(path);
98
+ const $ex = { syscall: 'readlink', path };
99
+ const { fs, stats, path: resolved } = await resolve(this, path, true, $ex);
100
+ if (!stats)
101
+ throw UV('ENOENT', $ex);
102
+ if (checkAccess && !hasAccess(this, stats, constants.R_OK))
103
+ throw UV('EACCES', $ex);
104
+ if (!isSymbolicLink(stats))
105
+ throw UV('EINVAL', $ex);
106
+ const size = stats.size;
107
+ const data = new Uint8Array(size);
108
+ await fs.read(resolved, data, 0, size);
109
+ return decodeUTF8(data);
110
+ }
111
+ export async function mkdir(path, options = {}) {
112
+ path = normalizePath(path);
113
+ const { euid: uid, egid: gid } = this?.credentials ?? defaultContext.credentials;
114
+ const { mode = 0o777, recursive } = options;
115
+ const { fs, path: resolved } = resolveMount(path, this, { syscall: 'mkdir' });
116
+ const __create = async (path, resolved, parent) => {
117
+ if (checkAccess && !hasAccess(this, parent, constants.W_OK))
118
+ throw UV('EACCES', 'mkdir', path);
119
+ const inode = await fs.mkdir(resolved, {
120
+ mode,
121
+ uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
122
+ gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
123
+ });
124
+ emitChange(this, 'rename', path);
125
+ return inode;
126
+ };
127
+ if (!recursive) {
128
+ await __create(path, resolved, await fs.stat(dirname(resolved)));
364
129
  return;
365
130
  }
366
- const watcher = new StatWatcher(this, normalizedPath, opts);
367
- watcher.on('change', (curr, prev) => {
368
- const entry = statWatchers.get(normalizedPath);
369
- if (!entry) {
131
+ const dirs = [];
132
+ let origDir = path;
133
+ for (let dir = resolved; !(await fs.exists(dir)); dir = dirname(dir), origDir = dirname(origDir)) {
134
+ dirs.unshift([origDir, dir]);
135
+ }
136
+ if (!dirs.length)
137
+ return;
138
+ const stats = [await fs.stat(dirname(dirs[0][1]))];
139
+ for (const [i, [path, resolved]] of dirs.entries()) {
140
+ stats.push(await __create(path, resolved, stats[i]));
141
+ }
142
+ return dirs[0][0];
143
+ }
144
+ export async function readdir(path, options = {}) {
145
+ path = normalizePath(path);
146
+ const $ex = { syscall: 'readdir', path };
147
+ const { fs, path: resolved, stats } = await resolve(this, path, false, $ex);
148
+ if (!stats)
149
+ throw UV('ENOENT', $ex);
150
+ if (checkAccess && !hasAccess(this, stats, constants.R_OK))
151
+ throw UV('EACCES', $ex);
152
+ if (!isDirectory(stats))
153
+ throw UV('ENOTDIR', $ex);
154
+ const entries = await fs.readdir(resolved);
155
+ const values = [];
156
+ const addEntry = async (entry) => {
157
+ const entryStats = await fs.stat(join(resolved, entry)).catch((e) => {
158
+ if (e.code == 'ENOENT')
159
+ return;
160
+ throw e;
161
+ });
162
+ if (!entryStats)
370
163
  return;
371
- }
372
- for (const listener of entry.listeners) {
373
- listener(curr, prev);
374
- }
164
+ const ent = new Dirent();
165
+ ent.ino = entryStats.ino;
166
+ ent.type = ifToDt(entryStats.mode);
167
+ ent.path = entry;
168
+ ent.name = basename(entry);
169
+ values.push(ent);
170
+ if (!options.recursive || !isDirectory(entryStats))
171
+ return;
172
+ const children = await fs.readdir(join(resolved, entry));
173
+ for (const child of children)
174
+ await addEntry(join(entry, child));
175
+ };
176
+ await Promise.all(entries.map(addEntry));
177
+ return values;
178
+ }
179
+ export async function rename(oldPath, newPath) {
180
+ oldPath = normalizePath(oldPath);
181
+ newPath = normalizePath(newPath);
182
+ const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
183
+ const src = await resolve(this, oldPath, true, $ex);
184
+ const dst = resolveMount(newPath, this, $ex);
185
+ if (src.fs.uuid !== dst.fs.uuid)
186
+ throw UV('EXDEV', $ex);
187
+ if (dst.path.startsWith(src.path + '/'))
188
+ throw UV('EBUSY', $ex);
189
+ if (!src.stats)
190
+ throw UV('ENOENT', $ex);
191
+ const fs = src.fs;
192
+ const oldParent = await fs.stat(dirname(src.path));
193
+ const newParent = await fs.stat(dirname(dst.path));
194
+ const newStats = await fs.stat(dst.path).catch((e) => {
195
+ if (e.code == 'ENOENT')
196
+ return null;
197
+ throw e;
375
198
  });
376
- statWatchers.set(normalizedPath, { watcher, listeners: new Set() });
377
- }
378
- watchFile;
379
- /**
380
- * Stop watching for changes on a file.
381
- *
382
- * If the `listener` is specified, only that particular listener is removed.
383
- * If no `listener` is specified, all listeners are removed, and the file is no longer watched.
384
- *
385
- * @param path The path to the file to stop watching.
386
- * @param listener Optional listener to remove.
387
- */
388
- export function unwatchFile(path, listener = nop) {
389
- const normalizedPath = normalizePath(path);
390
- const entry = statWatchers.get(normalizedPath);
391
- if (entry) {
392
- if (listener && listener !== nop) {
393
- entry.listeners.delete(listener);
394
- }
395
- else {
396
- // If no listener is specified, remove all listeners
397
- entry.listeners.clear();
398
- }
399
- if (entry.listeners.size === 0) {
400
- // No more listeners, stop the watcher
401
- entry.watcher.stop();
402
- statWatchers.delete(normalizedPath);
403
- }
199
+ if (checkAccess && (!hasAccess(this, oldParent, constants.R_OK) || !hasAccess(this, newParent, constants.W_OK)))
200
+ throw UV('EACCES', $ex);
201
+ if (newStats && !isDirectory(src.stats) && isDirectory(newStats))
202
+ throw UV('EISDIR', $ex);
203
+ if (newStats && isDirectory(src.stats) && !isDirectory(newStats))
204
+ throw UV('ENOTDIR', $ex);
205
+ await src.fs.rename(src.path, dst.path);
206
+ emitChange(this, 'rename', oldPath);
207
+ emitChange(this, 'change', newPath);
208
+ }
209
+ export async function link(target, link) {
210
+ target = normalizePath(target);
211
+ link = normalizePath(link);
212
+ const $ex = { syscall: 'link', path: link, dest: target };
213
+ const { fs, path: resolved } = resolveMount(target, this, $ex);
214
+ const dst = resolveMount(link, this, $ex);
215
+ if (fs.uuid != dst.fs.uuid)
216
+ throw UV('EXDEV', $ex);
217
+ const stats = await fs.stat(resolved);
218
+ if (checkAccess) {
219
+ if (!hasAccess(this, stats, constants.R_OK))
220
+ throw UV('EACCES', $ex);
221
+ const dirStats = await fs.stat(dirname(resolved));
222
+ if (!hasAccess(this, dirStats, constants.R_OK))
223
+ throw UV('EACCES', $ex);
224
+ const destStats = await fs.stat(dirname(dst.path));
225
+ if (!hasAccess(this, destStats, constants.W_OK))
226
+ throw UV('EACCES', $ex);
404
227
  }
228
+ return await fs.link(resolved, dst.path);
405
229
  }
406
- unwatchFile;
407
- export function watch(path, options, listener) {
408
- const watcher = new FSWatcher(this, normalizePath(path), typeof options == 'object' ? options : {});
409
- listener = typeof options == 'function' ? options : listener;
410
- watcher.on('change', listener || nop);
411
- return watcher;
412
- }
413
- watch;
414
- /**
415
- * Opens a file in read mode and creates a Node.js-like ReadStream.
416
- *
417
- * @param path The path to the file to be opened.
418
- * @param options Options for the ReadStream and file opening (e.g., `encoding`, `highWaterMark`, `mode`).
419
- * @returns A ReadStream object for interacting with the file's contents.
420
- */
421
- export function createReadStream(path, options) {
422
- options = typeof options == 'object' ? options : { encoding: options };
423
- const _handle = promises.open.call(this, path, 'r', options?.mode);
424
- return new ReadStream({ ...options, autoClose: true }, _handle);
425
- }
426
- createReadStream;
427
- /**
428
- * Opens a file in write mode and creates a Node.js-like WriteStream.
429
- *
430
- * @param path The path to the file to be opened.
431
- * @param options Options for the WriteStream and file opening (e.g., `encoding`, `highWaterMark`, `mode`).
432
- * @returns A WriteStream object for writing to the file.
433
- */
434
- export function createWriteStream(path, options) {
435
- options = typeof options == 'object' ? options : { encoding: options };
436
- const _handle = promises.open.call(this, path, 'w', options?.mode);
437
- return new WriteStream(options, _handle);
438
- }
439
- createWriteStream;
440
- export function rm(path, options, callback = nop) {
441
- callback = typeof options === 'function' ? options : callback;
442
- promises.rm
443
- .call(this, path, typeof options === 'function' ? undefined : options)
444
- .then(() => callback(null))
445
- .catch(callback);
446
- }
447
- rm;
448
- export function mkdtemp(prefix, options, callback = nop) {
449
- callback = typeof options === 'function' ? options : callback;
450
- promises.mkdtemp
451
- .call(this, prefix, typeof options != 'function' ? options : null)
452
- .then(result => callback(null, result))
453
- .catch(callback);
454
- }
455
- mkdtemp;
456
- export function copyFile(src, dest, flags, callback = nop) {
457
- callback = typeof flags === 'function' ? flags : callback;
458
- promises.copyFile
459
- .call(this, src, dest, typeof flags === 'function' ? undefined : flags)
460
- .then(() => callback(null))
461
- .catch(callback);
462
- }
463
- copyFile;
464
- export function readv(fd, buffers, position, cb = nop) {
465
- cb = typeof position === 'function' ? position : cb;
466
- new promises.FileHandle(this, fd)
467
- .readv(buffers, typeof position === 'function' ? undefined : position)
468
- .then(({ buffers, bytesRead }) => cb(null, bytesRead, buffers))
469
- .catch(cb);
470
- }
471
- readv;
472
- export function writev(fd, buffers, position, cb = nop) {
473
- cb = typeof position === 'function' ? position : cb;
474
- new promises.FileHandle(this, fd)
475
- .writev(buffers, typeof position === 'function' ? undefined : position)
476
- .then(({ buffers, bytesWritten }) => cb(null, bytesWritten, buffers))
477
- .catch(cb);
478
- }
479
- writev;
480
- export function opendir(path, options, cb = nop) {
481
- cb = typeof options === 'function' ? options : cb;
482
- promises.opendir
483
- .call(this, path, typeof options === 'function' ? undefined : options)
484
- .then(result => cb(null, result))
485
- .catch(cb);
486
- }
487
- opendir;
488
- export function cp(source, destination, opts, callback = nop) {
489
- callback = typeof opts === 'function' ? opts : callback;
490
- promises.cp
491
- .call(this, source, destination, typeof opts === 'function' ? undefined : opts)
492
- .then(() => callback(null))
493
- .catch(callback);
494
- }
495
- cp;
496
- export function statfs(path, options, callback = nop) {
497
- callback = typeof options === 'function' ? options : callback;
498
- promises.statfs
499
- .call(this, path, typeof options === 'function' ? undefined : options)
500
- .then(result => callback(null, result))
501
- .catch(callback);
502
- }
503
- statfs;
504
- export async function openAsBlob(path, options) {
505
- const handle = await promises.open.call(this, path.toString(), 'r');
506
- const buffer = await handle.readFile();
507
- await handle.close();
508
- return new Blob([buffer], options);
509
- }
510
- openAsBlob;
511
- export function glob(pattern, options, callback = nop) {
512
- callback = typeof options == 'function' ? options : callback;
513
- const it = promises.glob.call(this, pattern, typeof options === 'function' ? undefined : options);
514
- collectAsyncIterator(it)
515
- .then(results => callback(null, results ?? []))
516
- .catch((e) => callback(e));
517
- }
518
- glob;