@zenfs/core 2.4.0 → 2.4.2

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 (100) hide show
  1. package/COPYING.md +18 -0
  2. package/{readme.md → README.md} +16 -74
  3. package/dist/backends/fetch.js +1 -1
  4. package/dist/backends/memory.js +1 -1
  5. package/dist/backends/passthrough.d.ts +1 -2
  6. package/dist/backends/single_buffer.js +1 -1
  7. package/dist/backends/store/fs.js +4 -4
  8. package/dist/config.js +15 -15
  9. package/dist/context.js +3 -2
  10. package/dist/index.d.ts +9 -3
  11. package/dist/index.js +9 -4
  12. package/dist/internal/contexts.d.ts +5 -4
  13. package/dist/internal/devices.js +1 -1
  14. package/dist/internal/error.d.ts +11 -2
  15. package/dist/internal/error.js +38 -2
  16. package/dist/internal/file_index.js +1 -1
  17. package/dist/internal/index.d.ts +1 -0
  18. package/dist/internal/index.js +2 -1
  19. package/dist/internal/index_fs.js +1 -1
  20. package/dist/internal/inode.d.ts +51 -2
  21. package/dist/internal/inode.js +18 -2
  22. package/dist/mixins/shared.js +1 -1
  23. package/dist/node/async.d.ts +278 -0
  24. package/dist/node/async.js +518 -0
  25. package/dist/node/compat.d.ts +4 -0
  26. package/dist/node/compat.js +6 -0
  27. package/dist/node/dir.d.ts +78 -0
  28. package/dist/node/dir.js +150 -0
  29. package/dist/node/index.d.ts +8 -0
  30. package/dist/node/index.js +8 -0
  31. package/dist/{vfs → node}/promises.d.ts +10 -66
  32. package/dist/{vfs → node}/promises.js +141 -478
  33. package/dist/{vfs → node}/stats.d.ts +0 -4
  34. package/dist/{vfs → node}/stats.js +1 -16
  35. package/dist/{vfs → node}/streams.js +2 -2
  36. package/dist/node/sync.d.ts +252 -0
  37. package/dist/node/sync.js +682 -0
  38. package/dist/node/types.d.ts +21 -0
  39. package/dist/path.js +3 -25
  40. package/dist/utils.d.ts +1 -7
  41. package/dist/utils.js +0 -6
  42. package/dist/vfs/acl.js +1 -1
  43. package/dist/vfs/async.d.ts +22 -278
  44. package/dist/vfs/async.js +212 -501
  45. package/dist/vfs/dir.d.ts +5 -82
  46. package/dist/vfs/dir.js +5 -233
  47. package/dist/vfs/file.d.ts +52 -13
  48. package/dist/vfs/file.js +167 -25
  49. package/dist/vfs/flags.js +1 -1
  50. package/dist/vfs/index.d.ts +2 -5
  51. package/dist/vfs/index.js +2 -5
  52. package/dist/vfs/shared.d.ts +25 -1
  53. package/dist/vfs/shared.js +6 -4
  54. package/dist/vfs/sync.d.ts +17 -245
  55. package/dist/vfs/sync.js +129 -773
  56. package/dist/vfs/watchers.d.ts +1 -1
  57. package/dist/vfs/watchers.js +2 -2
  58. package/dist/vfs/xattr.js +1 -1
  59. package/eslint.shared.js +1 -0
  60. package/package.json +7 -5
  61. package/scripts/make-index.js +5 -29
  62. package/scripts/test.js +59 -51
  63. package/tests/backend/fetch.test.ts +2 -2
  64. package/tests/backend/port.test.ts +2 -3
  65. package/tests/backend/single-buffer.test.ts +1 -1
  66. package/tests/common/casefold.test.ts +1 -1
  67. package/tests/common/context.test.ts +11 -4
  68. package/tests/common/devices.test.ts +3 -3
  69. package/tests/common/handle.test.ts +4 -3
  70. package/tests/common/inode.test.ts +2 -2
  71. package/tests/common/mounts.test.ts +1 -3
  72. package/tests/common/mutex.test.ts +1 -3
  73. package/tests/common/path.test.ts +2 -2
  74. package/tests/common/readline.test.ts +1 -1
  75. package/tests/common.ts +5 -4
  76. package/tests/fetch/fetch.ts +1 -1
  77. package/tests/fs/dir.test.ts +3 -43
  78. package/tests/fs/directory.test.ts +4 -4
  79. package/tests/fs/errors.test.ts +2 -2
  80. package/tests/fs/links.test.ts +1 -1
  81. package/tests/fs/permissions.test.ts +3 -3
  82. package/tests/fs/read.test.ts +1 -1
  83. package/tests/fs/scaling.test.ts +1 -1
  84. package/tests/fs/stat.test.ts +1 -2
  85. package/tests/fs/times.test.ts +1 -1
  86. package/tests/fs/watch.test.ts +3 -2
  87. package/tests/setup/context.ts +1 -2
  88. package/tests/setup/cow.ts +1 -1
  89. package/tests/setup/index.ts +2 -2
  90. package/tests/setup/port.ts +1 -1
  91. package/tests/setup/single-buffer.ts +1 -1
  92. package/tests/setup.ts +4 -3
  93. package/dist/vfs/types.d.ts +0 -24
  94. package/tests/assignment.ts +0 -21
  95. /package/dist/{vfs/constants.d.ts → constants.d.ts} +0 -0
  96. /package/dist/{vfs/constants.js → constants.js} +0 -0
  97. /package/dist/{readline.d.ts → node/readline.d.ts} +0 -0
  98. /package/dist/{readline.js → node/readline.js} +0 -0
  99. /package/dist/{vfs → node}/streams.d.ts +0 -0
  100. /package/dist/{vfs → node}/types.js +0 -0
package/dist/vfs/sync.js CHANGED
@@ -1,189 +1,67 @@
1
- var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
- if (value !== null && value !== void 0) {
3
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
- var dispose, inner;
5
- if (async) {
6
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
- dispose = value[Symbol.asyncDispose];
8
- }
9
- if (dispose === void 0) {
10
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
- dispose = value[Symbol.dispose];
12
- if (async) inner = dispose;
13
- }
14
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
- env.stack.push({ value: value, dispose: dispose, async: async });
17
- }
18
- else if (async) {
19
- env.stack.push({ async: true });
20
- }
21
- return value;
22
- };
23
- var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
- return function (env) {
25
- function fail(e) {
26
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
- env.hasError = true;
28
- }
29
- var r, s = 0;
30
- function next() {
31
- while (r = env.stack.pop()) {
32
- try {
33
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
- if (r.dispose) {
35
- var result = r.dispose.call(r.value);
36
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
- }
38
- else s |= 1;
39
- }
40
- catch (e) {
41
- fail(e);
42
- }
43
- }
44
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
- if (env.hasError) throw env.error;
46
- }
47
- return next();
48
- };
49
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
- var e = new Error(message);
51
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
- });
53
- import { Buffer } from 'buffer';
54
- import { Errno, Exception, setUVMessage, UV } from 'kerium';
55
- import { encodeUTF8 } from 'utilium';
1
+ import { setUVMessage, UV } from 'kerium';
2
+ import { decodeUTF8 } from 'utilium';
3
+ import * as constants from '../constants.js';
56
4
  import { defaultContext } from '../internal/contexts.js';
57
- import { wrap } from '../internal/error.js';
58
5
  import { hasAccess, isDirectory, isSymbolicLink } from '../internal/inode.js';
59
- import { basename, dirname, join, matchesGlob, parse, resolve } from '../path.js';
60
- import { __assertType, _tempDirName, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
6
+ import { basename, dirname, join, parse, resolve as resolvePath } from '../path.js';
7
+ import { normalizeMode, normalizePath } from '../utils.js';
61
8
  import { checkAccess } from './config.js';
62
- import * as constants from './constants.js';
63
- import { Dir, Dirent } from './dir.js';
64
- import { deleteFD, fromFD, SyncHandle, toFD } from './file.js';
9
+ import { Dirent, ifToDt } from './dir.js';
10
+ import { Handle } from './file.js';
65
11
  import * as flags from './flags.js';
66
- import { _statfs, resolveMount } from './shared.js';
67
- import { BigIntStats, Stats } from './stats.js';
12
+ import { resolveMount } from './shared.js';
68
13
  import { emitChange } from './watchers.js';
69
- export function renameSync(oldPath, newPath) {
70
- oldPath = normalizePath(oldPath);
71
- __assertType(oldPath);
72
- newPath = normalizePath(newPath);
73
- __assertType(newPath);
74
- const src = resolveMount(oldPath, this);
75
- const dst = resolveMount(newPath, this);
76
- const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
77
- if (src.fs !== dst.fs)
78
- throw UV('EXDEV', $ex);
79
- if (dst.path.startsWith(src.path + '/'))
80
- throw UV('EBUSY', $ex);
81
- const oldStats = statSync.call(this, oldPath);
82
- const oldParent = statSync.call(this, dirname(oldPath));
83
- const newParent = statSync.call(this, dirname(newPath));
84
- let newStats;
85
- try {
86
- newStats = statSync.call(this, newPath);
87
- }
88
- catch (e) {
89
- setUVMessage(Object.assign(e, $ex));
90
- if (e.code != 'ENOENT')
91
- throw e;
92
- }
93
- if (checkAccess && (!oldParent.hasAccess(constants.R_OK, this) || !newParent.hasAccess(constants.W_OK, this)))
94
- throw UV('EACCES', $ex);
95
- if (newStats && !isDirectory(oldStats) && isDirectory(newStats))
96
- throw UV('EISDIR', $ex);
97
- if (newStats && isDirectory(oldStats) && !isDirectory(newStats))
98
- throw UV('ENOTDIR', $ex);
99
- try {
100
- src.fs.renameSync(src.path, dst.path);
101
- }
102
- catch (e) {
103
- throw setUVMessage(Object.assign(e, $ex));
104
- }
105
- emitChange(this, 'rename', oldPath);
106
- emitChange(this, 'change', newPath);
107
- }
108
- renameSync;
109
14
  /**
110
- * Test whether or not `path` exists by checking with the file system.
15
+ * Resolves the mount and real path for a path.
16
+ * Additionally, any stats fetched will be returned for de-duplication
17
+ * @category VFS
18
+ * @internal @hidden
111
19
  */
112
- export function existsSync(path) {
113
- path = normalizePath(path);
20
+ export function resolve($, path, preserveSymlinks, extra) {
21
+ /* Try to resolve it directly. If this works,
22
+ that means we don't need to perform any resolution for parent directories. */
114
23
  try {
115
- const { fs, path: resolvedPath } = resolveMount(realpathSync.call(this, path), this);
116
- return fs.existsSync(resolvedPath);
24
+ const resolved = resolveMount(path, $);
25
+ // Stat it to make sure it exists
26
+ const stats = resolved.fs.statSync(resolved.path);
27
+ if (!isSymbolicLink(stats) || preserveSymlinks) {
28
+ return { ...resolved, fullPath: path, stats };
29
+ }
30
+ const target = resolvePath.call($, dirname(path), readlink.call($, path));
31
+ return resolve($, target, preserveSymlinks, extra);
117
32
  }
118
33
  catch (e) {
119
- if (e.errno == Errno.ENOENT)
120
- return false;
121
- throw e;
34
+ setUVMessage(Object.assign(e, { syscall: 'stat', path }));
35
+ if (preserveSymlinks)
36
+ throw e;
122
37
  }
123
- }
124
- existsSync;
125
- export function statSync(path, options) {
126
- path = normalizePath(path);
127
- const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
38
+ const { base, dir } = parse(path);
39
+ const realDir = dir == '/' ? '/' : resolve($, dir, false, extra).fullPath;
40
+ const maybePath = join(realDir, base);
41
+ const resolved = resolveMount(maybePath, $);
128
42
  let stats;
129
43
  try {
130
- stats = fs.statSync(resolved);
44
+ stats = resolved.fs.statSync(resolved.path);
131
45
  }
132
46
  catch (e) {
133
- throw setUVMessage(Object.assign(e, { path }));
134
- }
135
- if (checkAccess && !hasAccess(this, stats, constants.R_OK))
136
- throw UV('EACCES', { syscall: 'stat', path });
137
- return options?.bigint ? new BigIntStats(stats) : new Stats(stats);
138
- }
139
- statSync;
140
- export function lstatSync(path, options) {
141
- path = normalizePath(path);
142
- const real = join(realpathSync.call(this, dirname(path)), basename(path));
143
- const { fs, path: resolved } = resolveMount(real, this);
144
- const stats = wrap(fs, 'statSync', path)(resolved);
145
- if (checkAccess && !hasAccess(this, stats, constants.R_OK))
146
- throw UV('EACCES', { syscall: 'lstat', path });
147
- return options?.bigint ? new BigIntStats(stats) : new Stats(stats);
148
- }
149
- lstatSync;
150
- export function truncateSync(path, len = 0) {
151
- const env_1 = { stack: [], error: void 0, hasError: false };
152
- try {
153
- const file = __addDisposableResource(env_1, _openSync.call(this, path, { flag: 'r+' }), false);
154
- len ||= 0;
155
- if (len < 0)
156
- throw UV('EINVAL', 'truncate', path.toString());
157
- file.truncate(len);
158
- }
159
- catch (e_1) {
160
- env_1.error = e_1;
161
- env_1.hasError = true;
162
- }
163
- finally {
164
- __disposeResources(env_1);
165
- }
166
- }
167
- truncateSync;
168
- export function unlinkSync(path) {
169
- path = normalizePath(path);
170
- const { fs, path: resolved } = resolveMount(path, this);
171
- try {
172
- if (checkAccess && !hasAccess(this, fs.statSync(resolved), constants.W_OK)) {
173
- throw UV('EACCES', 'unlink');
174
- }
175
- fs.unlinkSync(resolved);
47
+ if (e.code === 'ENOENT')
48
+ return { ...resolved, fullPath: path };
49
+ throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath }));
176
50
  }
177
- catch (e) {
178
- throw setUVMessage(Object.assign(e, { path }));
51
+ if (!isSymbolicLink(stats)) {
52
+ return { ...resolved, fullPath: maybePath, stats };
179
53
  }
180
- emitChange(this, 'rename', path.toString());
54
+ const target = resolvePath.call($, realDir, readlink.call($, maybePath));
55
+ return resolve($, target, false, extra);
181
56
  }
182
- unlinkSync;
183
- function _openSync(path, opt) {
57
+ /**
58
+ * @category VFS
59
+ * @internal
60
+ */
61
+ export function open(path, opt) {
184
62
  path = normalizePath(path);
185
63
  const mode = normalizeMode(opt.mode, 0o644), flag = flags.parse(opt.flag);
186
- path = opt.preserveSymlinks ? path : realpathSync.call(this, path);
64
+ path = opt.preserveSymlinks ? path : resolve(this, path).fullPath;
187
65
  const { fs, path: resolved } = resolveMount(path, this);
188
66
  let stats;
189
67
  try {
@@ -215,241 +93,43 @@ function _openSync(path, opt) {
215
93
  uid: parentStats.mode & constants.S_ISUID ? parentStats.uid : uid,
216
94
  gid: parentStats.mode & constants.S_ISGID ? parentStats.gid : gid,
217
95
  });
218
- return new SyncHandle(this, path, fs, resolved, flag, inode);
96
+ return new Handle(this, path, fs, resolved, flag, inode);
219
97
  }
220
98
  if (checkAccess && (!hasAccess(this, stats, mode) || !hasAccess(this, stats, flags.toMode(flag)))) {
221
99
  throw UV('EACCES', 'open', path);
222
100
  }
223
101
  if (flag & constants.O_EXCL)
224
102
  throw UV('EEXIST', 'open', path);
225
- const file = new SyncHandle(this, path, fs, resolved, flag, stats);
103
+ const file = new Handle(this, path, fs, resolved, flag, stats);
226
104
  if (!opt.allowDirectory && stats.mode & constants.S_IFDIR)
227
105
  throw UV('EISDIR', 'open', path);
228
106
  if (flag & constants.O_TRUNC)
229
- file.truncate(0);
107
+ file.truncateSync(0);
230
108
  return file;
231
109
  }
232
- /**
233
- * Synchronous file open.
234
- * @see https://nodejs.org/api/fs.html#fsopensyncpath-flags-mode
235
- * @param flag {@link https://nodejs.org/api/fs.html#file-system-flags}
236
- */
237
- export function openSync(path, flag, mode = constants.F_OK) {
238
- return toFD(_openSync.call(this, path, { flag, mode }));
239
- }
240
- openSync;
241
- /**
242
- * Opens a file or symlink
243
- * @internal
244
- */
245
- export function lopenSync(path, flag, mode) {
246
- return toFD(_openSync.call(this, path, { flag, mode, preserveSymlinks: true }));
247
- }
248
- export function readFileSync(path, _options = {}) {
249
- const env_2 = { stack: [], error: void 0, hasError: false };
250
- try {
251
- const options = normalizeOptions(_options, null, 'r', 0o644);
252
- const flag = flags.parse(options.flag);
253
- if (flag & constants.O_WRONLY)
254
- throw UV('EBADF', 'read', path.toString());
255
- const file = __addDisposableResource(env_2, typeof path == 'number'
256
- ? fromFD(this, path)
257
- : _openSync.call(this, path.toString(), { flag: options.flag, mode: 0o644, preserveSymlinks: false }), false);
258
- const { size } = file.stat();
259
- const data = Buffer.alloc(size);
260
- file.read(data, 0, size, 0);
261
- return options.encoding ? data.toString(options.encoding) : data;
262
- }
263
- catch (e_2) {
264
- env_2.error = e_2;
265
- env_2.hasError = true;
266
- }
267
- finally {
268
- __disposeResources(env_2);
269
- }
270
- }
271
- readFileSync;
272
- export function writeFileSync(path, data, _options = {}) {
273
- const env_3 = { stack: [], error: void 0, hasError: false };
274
- try {
275
- const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
276
- const flag = flags.parse(options.flag);
277
- if (!(flag & constants.O_WRONLY || flag & constants.O_RDWR)) {
278
- throw new Exception(Errno.EINVAL, 'Flag passed to writeFile must allow for writing');
279
- }
280
- if (typeof data != 'string' && !options.encoding) {
281
- throw new Exception(Errno.EINVAL, 'Encoding not specified');
282
- }
283
- const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
284
- if (!encodedData) {
285
- throw new Exception(Errno.EINVAL, 'Data not specified');
286
- }
287
- const file = __addDisposableResource(env_3, typeof path == 'number'
288
- ? fromFD(this, path)
289
- : _openSync.call(this, path.toString(), {
290
- flag,
291
- mode: options.mode,
292
- preserveSymlinks: true,
293
- }), false);
294
- file.write(encodedData, 0, encodedData.byteLength, 0);
295
- emitChange(this, 'change', path.toString());
296
- }
297
- catch (e_3) {
298
- env_3.error = e_3;
299
- env_3.hasError = true;
300
- }
301
- finally {
302
- __disposeResources(env_3);
303
- }
304
- }
305
- writeFileSync;
306
- /**
307
- * Asynchronously append data to a file, creating the file if it not yet exists.
308
- * @option encoding Defaults to `'utf8'`.
309
- * @option mode Defaults to `0644`.
310
- * @option flag Defaults to `'a+'`.
311
- */
312
- export function appendFileSync(filename, data, _options = {}) {
313
- const env_4 = { stack: [], error: void 0, hasError: false };
314
- try {
315
- const options = normalizeOptions(_options, 'utf8', 'a+', 0o644);
316
- const flag = flags.parse(options.flag);
317
- if (!(flag & constants.O_APPEND)) {
318
- throw new Exception(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
319
- }
320
- if (typeof data != 'string' && !options.encoding) {
321
- throw new Exception(Errno.EINVAL, 'Encoding not specified');
322
- }
323
- const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
324
- const file = __addDisposableResource(env_4, _openSync.call(this, typeof filename == 'number' ? fromFD(this, filename).path : filename.toString(), {
325
- flag,
326
- mode: options.mode,
327
- preserveSymlinks: true,
328
- }), false);
329
- file.write(encodedData, 0, encodedData.byteLength);
330
- }
331
- catch (e_4) {
332
- env_4.error = e_4;
333
- env_4.hasError = true;
334
- }
335
- finally {
336
- __disposeResources(env_4);
337
- }
338
- }
339
- appendFileSync;
340
- export function fstatSync(fd, options) {
341
- const stats = fromFD(this, fd).stat();
342
- return options?.bigint ? new BigIntStats(stats) : new Stats(stats);
343
- }
344
- fstatSync;
345
- export function closeSync(fd) {
346
- fromFD(this, fd).close();
347
- deleteFD(this, fd);
348
- }
349
- closeSync;
350
- export function ftruncateSync(fd, len = 0) {
351
- len ||= 0;
352
- if (len < 0) {
353
- throw new Exception(Errno.EINVAL);
354
- }
355
- fromFD(this, fd).truncate(len);
356
- }
357
- ftruncateSync;
358
- export function fsyncSync(fd) {
359
- fromFD(this, fd).sync();
360
- }
361
- fsyncSync;
362
- export function fdatasyncSync(fd) {
363
- fromFD(this, fd).datasync();
364
- }
365
- fdatasyncSync;
366
- export function writeSync(fd, data, posOrOff, lenOrEnc, pos) {
367
- let buffer, offset, length, position;
368
- if (typeof data === 'string') {
369
- // Signature 1: (fd, string, [position?, [encoding?]])
370
- position = typeof posOrOff === 'number' ? posOrOff : null;
371
- const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8';
372
- offset = 0;
373
- buffer = Buffer.from(data, encoding);
374
- length = buffer.byteLength;
375
- }
376
- else {
377
- // Signature 2: (fd, buffer, offset, length, position?)
378
- buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
379
- offset = posOrOff;
380
- length = lenOrEnc;
381
- position = typeof pos === 'number' ? pos : null;
382
- }
383
- const file = fromFD(this, fd);
384
- position ??= file.position;
385
- const bytesWritten = file.write(buffer, offset, length, position);
386
- emitChange(this, 'change', file.path);
387
- return bytesWritten;
388
- }
389
- writeSync;
390
- /**
391
- * Read data from the file specified by `fd`.
392
- * @param buffer The buffer that the data will be written to.
393
- * @param offset The offset within the buffer where writing will start.
394
- * @param length An integer specifying the number of bytes to read.
395
- * @param position An integer specifying where to begin reading from in the file.
396
- * If position is null, data will be read from the current file position.
397
- */
398
- export function readSync(fd, buffer, options, length, position) {
399
- const file = fromFD(this, fd);
400
- const offset = typeof options == 'object' ? options.offset : options;
401
- if (typeof options == 'object') {
402
- length = options.length;
403
- position = options.position;
404
- }
405
- position = Number(position);
406
- if (isNaN(position)) {
407
- position = file.position;
408
- }
409
- return file.read(buffer, offset, length, position);
410
- }
411
- readSync;
412
- export function fchownSync(fd, uid, gid) {
413
- fromFD(this, fd).chown(uid, gid);
414
- }
415
- fchownSync;
416
- export function fchmodSync(fd, mode) {
417
- const numMode = normalizeMode(mode, -1);
418
- if (numMode < 0) {
419
- throw new Exception(Errno.EINVAL, `Invalid mode.`);
420
- }
421
- fromFD(this, fd).chmod(numMode);
422
- }
423
- fchmodSync;
424
- /**
425
- * Change the file timestamps of a file referenced by the supplied file descriptor.
426
- */
427
- export function futimesSync(fd, atime, mtime) {
428
- fromFD(this, fd).utimes(normalizeTime(atime), normalizeTime(mtime));
429
- }
430
- futimesSync;
431
- export function rmdirSync(path) {
110
+ export function readlink(path) {
432
111
  path = normalizePath(path);
433
- const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
434
- const stats = wrap(fs, 'statSync', path)(resolved);
435
- if (!isDirectory(stats))
436
- throw UV('ENOTDIR', 'rmdir', path);
437
- if (checkAccess && !hasAccess(this, stats, constants.W_OK))
438
- throw UV('EACCES', 'rmdir', path);
439
- wrap(fs, 'rmdirSync', path)(resolved);
440
- emitChange(this, 'rename', path.toString());
441
- }
442
- rmdirSync;
443
- export function mkdirSync(path, options) {
112
+ const { fs, stats, path: resolved } = resolve(this, path, true);
113
+ if (!stats)
114
+ throw UV('ENOENT', 'readlink', path);
115
+ if (checkAccess && !hasAccess(this, stats, constants.R_OK))
116
+ throw UV('EACCES', 'readlink', path);
117
+ if (!isSymbolicLink(stats))
118
+ throw UV('EINVAL', 'readlink', path);
119
+ const size = stats.size;
120
+ const data = new Uint8Array(size);
121
+ fs.readSync(resolved, data, 0, size);
122
+ return decodeUTF8(data);
123
+ }
124
+ export function mkdir(path, options = {}) {
125
+ path = normalizePath(path);
126
+ const { fs, path: resolved } = resolve(this, path);
444
127
  const { euid: uid, egid: gid } = this?.credentials ?? defaultContext.credentials;
445
- options = typeof options === 'object' ? options : { mode: options };
446
- const mode = normalizeMode(options?.mode, 0o777);
447
- path = realpathSync.call(this, path);
448
- const { fs, path: resolved } = resolveMount(path, this);
128
+ const { mode = 0o777, recursive } = options;
449
129
  const __create = (path, resolved, parent) => {
450
130
  if (checkAccess && !hasAccess(this, parent, constants.W_OK))
451
131
  throw UV('EACCES', 'mkdir', dirname(path));
452
- const inode = wrap(fs, 'mkdirSync', path)(resolved, {
132
+ const inode = fs.mkdirSync(resolved, {
453
133
  mode,
454
134
  uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
455
135
  gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
@@ -457,33 +137,31 @@ export function mkdirSync(path, options) {
457
137
  emitChange(this, 'rename', path);
458
138
  return inode;
459
139
  };
460
- if (!options?.recursive) {
461
- __create(path, resolved, wrap(fs, 'statSync', dirname(path))(dirname(resolved)));
140
+ if (!recursive) {
141
+ __create(path, resolved, fs.statSync(dirname(resolved)));
462
142
  return;
463
143
  }
464
144
  const dirs = [];
465
- for (let dir = resolved, original = path; !wrap(fs, 'existsSync', original)(dir); dir = dirname(dir), original = dirname(original)) {
145
+ for (let dir = resolved, original = path; !fs.existsSync(dir); dir = dirname(dir), original = dirname(original)) {
466
146
  dirs.unshift({ resolved: dir, original });
467
147
  }
468
148
  if (!dirs.length)
469
149
  return;
470
- const stats = [wrap(fs, 'statSync', dirname(dirs[0].original))(dirname(dirs[0].resolved))];
150
+ const stats = [fs.statSync(dirname(dirs[0].resolved))];
471
151
  for (const [i, dir] of dirs.entries()) {
472
152
  stats.push(__create(dir.original, dir.resolved, stats[i]));
473
153
  }
474
154
  return dirs[0].original;
475
155
  }
476
- mkdirSync;
477
- export function readdirSync(path, options) {
478
- options = typeof options === 'object' ? options : { encoding: options };
156
+ export function readdir(path, options = {}) {
479
157
  path = normalizePath(path);
480
- const { fs, path: resolved } = resolveMount(realpathSync.call(this, path), this);
481
- const stats = wrap(fs, 'statSync', path)(resolved);
158
+ const { fs, path: resolved } = resolve(this, path);
159
+ const stats = fs.statSync(resolved);
482
160
  if (checkAccess && !hasAccess(this, stats, constants.R_OK))
483
161
  throw UV('EACCES', 'readdir', path);
484
162
  if (!isDirectory(stats))
485
163
  throw UV('ENOTDIR', 'readdir', path);
486
- const entries = wrap(fs, 'readdirSync', path)(resolved);
164
+ const entries = fs.readdirSync(resolved);
487
165
  // Iterate over entries and handle recursive case if needed
488
166
  const values = [];
489
167
  const addEntry = (entry) => {
@@ -494,20 +172,17 @@ export function readdirSync(path, options) {
494
172
  catch (e) {
495
173
  if (e.code == 'ENOENT')
496
174
  return;
497
- throw setUVMessage(Object.assign(e, { syscall: 'stat', path: join(path, entry) }));
498
- }
499
- if (options?.withFileTypes) {
500
- values.push(Dirent.from(entry, entryStat, options.encoding));
501
- }
502
- else if (options?.encoding == 'buffer') {
503
- values.push(Buffer.from(entry));
504
- }
505
- else {
506
- values.push(entry);
175
+ throw e;
507
176
  }
177
+ const ent = new Dirent();
178
+ ent.ino = entryStat.ino;
179
+ ent.type = ifToDt(entryStat.mode);
180
+ ent.path = entry;
181
+ ent.name = basename(entry);
182
+ values.push(ent);
508
183
  if (!isDirectory(entryStat) || !options?.recursive)
509
184
  return;
510
- const children = wrap(fs, 'readdirSync', join(path, entry))(join(resolved, entry));
185
+ const children = fs.readdirSync(join(resolved, entry));
511
186
  for (const child of children)
512
187
  addEntry(join(entry, child));
513
188
  };
@@ -515,376 +190,57 @@ export function readdirSync(path, options) {
515
190
  addEntry(entry);
516
191
  return values;
517
192
  }
518
- readdirSync;
519
- export function linkSync(targetPath, linkPath) {
520
- targetPath = normalizePath(targetPath);
521
- if (checkAccess && !statSync(dirname(targetPath)).hasAccess(constants.R_OK, this)) {
522
- throw UV('EACCES', 'link', dirname(targetPath));
523
- }
524
- linkPath = normalizePath(linkPath);
525
- if (checkAccess && !statSync(dirname(linkPath)).hasAccess(constants.W_OK, this)) {
526
- throw UV('EACCES', 'link', dirname(linkPath));
527
- }
528
- const { fs, path } = resolveMount(targetPath, this);
529
- const link = resolveMount(linkPath, this);
530
- if (fs != link.fs) {
531
- throw UV('EXDEV', 'link', linkPath);
532
- }
533
- const stats = wrap(fs, 'statSync', targetPath)(path);
534
- if (checkAccess && !hasAccess(this, stats, constants.R_OK))
535
- throw UV('EACCES', 'link', path);
536
- return wrap(fs, 'linkSync', targetPath, linkPath)(path, link.path);
537
- }
538
- linkSync;
539
- /**
540
- * Synchronous `symlink`.
541
- * @param target target path
542
- * @param path link path
543
- * @param type can be either `'dir'` or `'file'` (default is `'file'`)
544
- */
545
- export function symlinkSync(target, path, type = 'file') {
546
- const env_5 = { stack: [], error: void 0, hasError: false };
547
- try {
548
- if (!['file', 'dir', 'junction'].includes(type))
549
- throw new TypeError('Invalid symlink type: ' + type);
550
- path = normalizePath(path);
551
- const file = __addDisposableResource(env_5, _openSync.call(this, path, { flag: 'wx', mode: 0o644 }), false);
552
- file.write(encodeUTF8(normalizePath(target, true)));
553
- file.chmod(constants.S_IFLNK);
554
- }
555
- catch (e_5) {
556
- env_5.error = e_5;
557
- env_5.hasError = true;
558
- }
559
- finally {
560
- __disposeResources(env_5);
561
- }
562
- }
563
- symlinkSync;
564
- export function readlinkSync(path, options) {
565
- const env_6 = { stack: [], error: void 0, hasError: false };
566
- try {
567
- const handle = __addDisposableResource(env_6, _openSync.call(this, normalizePath(path), { flag: 'r', mode: 0o644, preserveSymlinks: true }), false);
568
- if (!isSymbolicLink(handle.inode))
569
- throw new Exception(Errno.EINVAL, 'Not a symbolic link: ' + path);
570
- const size = handle.inode.size;
571
- const data = Buffer.alloc(size);
572
- handle.read(data, 0, size, 0);
573
- const encoding = typeof options == 'object' ? options?.encoding : options;
574
- if (encoding == 'buffer') {
575
- return data;
576
- }
577
- // always defaults to utf-8 to avoid wrangler (cloudflare) worker "unknown encoding" exception
578
- return data.toString(encoding ?? 'utf-8');
579
- }
580
- catch (e_6) {
581
- env_6.error = e_6;
582
- env_6.hasError = true;
583
- }
584
- finally {
585
- __disposeResources(env_6);
586
- }
587
- }
588
- readlinkSync;
589
- export function chownSync(path, uid, gid) {
590
- const fd = openSync.call(this, path, 'r+');
591
- fchownSync.call(this, fd, uid, gid);
592
- closeSync.call(this, fd);
593
- }
594
- chownSync;
595
- export function lchownSync(path, uid, gid) {
596
- const fd = lopenSync.call(this, path, 'r+');
597
- fchownSync.call(this, fd, uid, gid);
598
- closeSync.call(this, fd);
599
- }
600
- lchownSync;
601
- export function chmodSync(path, mode) {
602
- const fd = openSync.call(this, path, 'r+');
603
- fchmodSync.call(this, fd, mode);
604
- closeSync.call(this, fd);
605
- }
606
- chmodSync;
607
- export function lchmodSync(path, mode) {
608
- const fd = lopenSync.call(this, path, 'r+');
609
- fchmodSync.call(this, fd, mode);
610
- closeSync.call(this, fd);
611
- }
612
- lchmodSync;
613
- /**
614
- * Change file timestamps of the file referenced by the supplied path.
615
- */
616
- export function utimesSync(path, atime, mtime) {
617
- const fd = openSync.call(this, path, 'r+');
618
- futimesSync.call(this, fd, atime, mtime);
619
- closeSync.call(this, fd);
620
- }
621
- utimesSync;
622
- /**
623
- * Change file timestamps of the file referenced by the supplied path.
624
- */
625
- export function lutimesSync(path, atime, mtime) {
626
- const fd = lopenSync.call(this, path, 'r+');
627
- futimesSync.call(this, fd, atime, mtime);
628
- closeSync.call(this, fd);
629
- }
630
- lutimesSync;
631
- /**
632
- * Resolves the mount and real path for a path.
633
- * Additionally, any stats fetched will be returned for de-duplication
634
- * @internal @hidden
635
- */
636
- function _resolveSync($, path, preserveSymlinks) {
637
- if (preserveSymlinks) {
638
- const resolved = resolveMount(path, $);
639
- const stats = resolved.fs.statSync(resolved.path);
640
- return { ...resolved, fullPath: path, stats };
641
- }
642
- /* Try to resolve it directly. If this works,
643
- that means we don't need to perform any resolution for parent directories. */
644
- try {
645
- const resolved = resolveMount(path, $);
646
- // Stat it to make sure it exists
647
- const stats = resolved.fs.statSync(resolved.path);
648
- if (!isSymbolicLink(stats)) {
649
- return { ...resolved, fullPath: path, stats };
650
- }
651
- const target = resolve.call($, dirname(path), readlinkSync.call($, path).toString());
652
- return _resolveSync($, target);
653
- }
654
- catch {
655
- // Go the long way
656
- }
657
- const { base, dir } = parse(path);
658
- const realDir = dir == '/' ? '/' : realpathSync.call($, dir);
659
- const maybePath = join(realDir, base);
660
- const resolved = resolveMount(maybePath, $);
661
- let stats;
193
+ export function rename(oldPath, newPath) {
194
+ oldPath = normalizePath(oldPath);
195
+ newPath = normalizePath(newPath);
196
+ const $ex = { syscall: 'rename', path: oldPath, dest: newPath };
197
+ const src = resolve(this, oldPath, true, $ex);
198
+ const dst = resolveMount(newPath, this, $ex);
199
+ if (src.fs.uuid !== dst.fs.uuid)
200
+ throw UV('EXDEV', $ex);
201
+ if (dst.path.startsWith(src.path + '/'))
202
+ throw UV('EBUSY', $ex);
203
+ if (!src.stats)
204
+ throw UV('ENOENT', $ex);
205
+ const fs = src.fs;
206
+ const oldParent = fs.statSync(dirname(src.path));
207
+ const newParent = fs.statSync(dirname(dst.path));
208
+ let newStats;
662
209
  try {
663
- stats = resolved.fs.statSync(resolved.path);
210
+ newStats = fs.statSync(dst.path);
664
211
  }
665
212
  catch (e) {
666
- if (e.code === 'ENOENT')
667
- return { ...resolved, fullPath: path };
668
- throw setUVMessage(Object.assign(e, { syscall: 'stat', path: maybePath }));
669
- }
670
- if (!isSymbolicLink(stats)) {
671
- return { ...resolved, fullPath: maybePath, stats };
672
- }
673
- const target = resolve.call($, realDir, readlinkSync.call($, maybePath).toString());
674
- return _resolveSync($, target);
675
- }
676
- export function realpathSync(path, options) {
677
- const encoding = typeof options == 'string' ? options : (options?.encoding ?? 'utf8');
678
- path = normalizePath(path);
679
- const { fullPath } = _resolveSync(this, path);
680
- if (encoding == 'utf8' || encoding == 'utf-8')
681
- return fullPath;
682
- const buf = Buffer.from(fullPath, 'utf-8');
683
- if (encoding == 'buffer')
684
- return buf;
685
- return buf.toString(encoding);
686
- }
687
- realpathSync;
688
- export function accessSync(path, mode = 0o600) {
689
- if (!checkAccess)
690
- return;
691
- if (!hasAccess(this, statSync.call(this, path), mode)) {
692
- throw new Exception(Errno.EACCES);
693
- }
694
- }
695
- accessSync;
696
- /**
697
- * Synchronous `rm`. Removes files or directories (recursively).
698
- * @param path The path to the file or directory to remove.
699
- */
700
- export function rmSync(path, options) {
701
- path = normalizePath(path);
702
- let stats;
703
- try {
704
- stats = lstatSync.bind(this)(path);
705
- }
706
- catch (error) {
707
- if (error.code != 'ENOENT' || !options?.force)
708
- throw error;
709
- }
710
- if (!stats)
711
- return;
712
- switch (stats.mode & constants.S_IFMT) {
713
- case constants.S_IFDIR:
714
- if (options?.recursive) {
715
- for (const entry of readdirSync.call(this, path)) {
716
- rmSync.call(this, join(path, entry), options);
717
- }
718
- }
719
- rmdirSync.call(this, path);
720
- break;
721
- case constants.S_IFREG:
722
- case constants.S_IFLNK:
723
- case constants.S_IFBLK:
724
- case constants.S_IFCHR:
725
- unlinkSync.call(this, path);
726
- break;
727
- case constants.S_IFIFO:
728
- case constants.S_IFSOCK:
729
- default:
730
- throw UV('ENOSYS', 'rm', path);
731
- }
732
- }
733
- rmSync;
734
- export function mkdtempSync(prefix, options) {
735
- const encoding = typeof options === 'object' ? options?.encoding : options || 'utf8';
736
- const path = _tempDirName(prefix);
737
- mkdirSync.call(this, path);
738
- return encoding == 'buffer' ? Buffer.from(path) : path;
739
- }
740
- mkdtempSync;
741
- /**
742
- * Returns a disposable object whose `path` property holds the created directory path.
743
- * When the object is disposed, the directory and its contents will be removed if it still exists.
744
- * If the directory cannot be deleted, disposal will throw an error.
745
- * The object has a `remove()` method which will perform the same task.
746
- * @todo Add `satisfies` and maybe change return type once @types/node adds this.
747
- */
748
- export function mkdtempDisposableSync(prefix, options) {
749
- const path = _tempDirName(prefix);
750
- mkdirSync.call(this, path);
751
- const remove = () => rmSync(path, { recursive: true, force: true });
752
- return { path, remove, [Symbol.dispose]: remove };
753
- }
754
- /**
755
- * Synchronous `copyFile`. Copies a file.
756
- * @param flags Optional flags for the copy operation. Currently supports these flags:
757
- * - `fs.constants.COPYFILE_EXCL`: If the destination file already exists, the operation fails.
758
- */
759
- export function copyFileSync(source, destination, flags) {
760
- source = normalizePath(source);
761
- destination = normalizePath(destination);
762
- if (flags && flags & constants.COPYFILE_EXCL && existsSync(destination))
763
- throw UV('EEXIST', 'copyFile', destination);
764
- writeFileSync.call(this, destination, readFileSync(source));
765
- emitChange(this, 'rename', destination.toString());
766
- }
767
- copyFileSync;
768
- /**
769
- * Synchronous `readv`. Reads from a file descriptor into multiple buffers.
770
- * @param fd The file descriptor.
771
- * @param buffers An array of Uint8Array buffers.
772
- * @param position The position in the file where to begin reading.
773
- * @returns The number of bytes read.
774
- */
775
- export function readvSync(fd, buffers, position) {
776
- const file = fromFD(this, fd);
777
- let bytesRead = 0;
778
- for (const buffer of buffers) {
779
- bytesRead += file.read(buffer, 0, buffer.byteLength, position + bytesRead);
780
- }
781
- return bytesRead;
782
- }
783
- readvSync;
784
- /**
785
- * Synchronous `writev`. Writes from multiple buffers into a file descriptor.
786
- * @param fd The file descriptor.
787
- * @param buffers An array of Uint8Array buffers.
788
- * @param position The position in the file where to begin writing.
789
- * @returns The number of bytes written.
790
- */
791
- export function writevSync(fd, buffers, position) {
792
- const file = fromFD(this, fd);
793
- let bytesWritten = 0;
794
- for (const buffer of buffers) {
795
- bytesWritten += file.write(new Uint8Array(buffer.buffer), 0, buffer.byteLength, position + bytesWritten);
796
- }
797
- return bytesWritten;
798
- }
799
- writevSync;
800
- /**
801
- * Synchronous `opendir`. Opens a directory.
802
- * @param path The path to the directory.
803
- * @param options Options for opening the directory.
804
- * @returns A `Dir` object representing the opened directory.
805
- * @todo Handle options
806
- */
807
- export function opendirSync(path, options) {
808
- path = normalizePath(path);
809
- return new Dir(path, this);
810
- }
811
- opendirSync;
812
- /**
813
- * Synchronous `cp`. Recursively copies a file or directory.
814
- * @param source The source file or directory.
815
- * @param destination The destination file or directory.
816
- * @param opts Options for the copy operation. Currently supports these options from Node.js 'fs.cpSync':
817
- * - `dereference`: Dereference symbolic links. *(unconfirmed)*
818
- * - `errorOnExist`: Throw an error if the destination file or directory already exists.
819
- * - `filter`: A function that takes a source and destination path and returns a boolean, indicating whether to copy `source` element.
820
- * - `force`: Overwrite the destination if it exists, and overwrite existing readonly destination files. *(unconfirmed)*
821
- * - `preserveTimestamps`: Preserve file timestamps.
822
- * - `recursive`: If `true`, copies directories recursively.
823
- */
824
- export function cpSync(source, destination, opts) {
825
- source = normalizePath(source);
826
- destination = normalizePath(destination);
827
- const srcStats = lstatSync.call(this, source); // Use lstat to follow symlinks if not dereferencing
828
- if (opts?.errorOnExist && existsSync.call(this, destination))
829
- throw UV('EEXIST', 'cp', destination);
830
- switch (srcStats.mode & constants.S_IFMT) {
831
- case constants.S_IFDIR:
832
- if (!opts?.recursive)
833
- throw UV('EISDIR', 'cp', source);
834
- mkdirSync.call(this, destination, { recursive: true }); // Ensure the destination directory exists
835
- for (const dirent of readdirSync.call(this, source, { withFileTypes: true })) {
836
- if (opts.filter && !opts.filter(join(source, dirent.name), join(destination, dirent.name))) {
837
- continue; // Skip if the filter returns false
838
- }
839
- cpSync.call(this, join(source, dirent.name), join(destination, dirent.name), opts);
840
- }
841
- break;
842
- case constants.S_IFREG:
843
- case constants.S_IFLNK:
844
- copyFileSync.call(this, source, destination);
845
- break;
846
- case constants.S_IFBLK:
847
- case constants.S_IFCHR:
848
- case constants.S_IFIFO:
849
- case constants.S_IFSOCK:
850
- default:
851
- throw UV('ENOSYS', 'cp', source);
852
- }
853
- // Optionally preserve timestamps
854
- if (opts?.preserveTimestamps) {
855
- utimesSync.call(this, destination, srcStats.atime, srcStats.mtime);
213
+ if (e.code != 'ENOENT')
214
+ throw e;
856
215
  }
216
+ if (checkAccess && (!hasAccess(this, oldParent, constants.R_OK) || !hasAccess(this, newParent, constants.W_OK)))
217
+ throw UV('EACCES', $ex);
218
+ if (newStats && !isDirectory(src.stats) && isDirectory(newStats))
219
+ throw UV('EISDIR', $ex);
220
+ if (newStats && isDirectory(src.stats) && !isDirectory(newStats))
221
+ throw UV('ENOTDIR', $ex);
222
+ src.fs.renameSync(src.path, dst.path);
223
+ emitChange(this, 'rename', oldPath);
224
+ emitChange(this, 'change', newPath);
857
225
  }
858
- cpSync;
859
- export function statfsSync(path, options) {
860
- path = normalizePath(path);
861
- const { fs } = resolveMount(path, this);
862
- return _statfs(fs, options?.bigint);
863
- }
864
- export function globSync(pattern, options = {}) {
865
- pattern = Array.isArray(pattern) ? pattern : [pattern];
866
- const { cwd = '/', withFileTypes = false, exclude = () => false } = options;
867
- // Escape special characters in pattern
868
- const regexPatterns = pattern.map(globToRegex);
869
- const results = [];
870
- function recursiveList(dir) {
871
- const entries = readdirSync(dir, { withFileTypes, encoding: 'utf8' });
872
- for (const entry of entries) {
873
- const fullPath = withFileTypes ? join(entry.parentPath, entry.name) : dir + '/' + entry;
874
- if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, fullPath)) : exclude((withFileTypes ? entry : fullPath)))
875
- continue;
876
- /**
877
- * @todo is the pattern.source check correct?
878
- */
879
- if (statSync(fullPath).isDirectory() && regexPatterns.some(pattern => pattern.source.includes('.*'))) {
880
- recursiveList(fullPath);
881
- }
882
- if (regexPatterns.some(pattern => pattern.test(fullPath.replace(/^\/+/g, '')))) {
883
- results.push(withFileTypes ? entry : fullPath.replace(/^\/+/g, ''));
884
- }
885
- }
886
- }
887
- recursiveList(cwd);
888
- return results;
226
+ export function link(target, link) {
227
+ target = normalizePath(target);
228
+ link = normalizePath(link);
229
+ const $ex = { syscall: 'link', path: link, dest: target };
230
+ const { fs, path: resolved } = resolveMount(target, this, $ex);
231
+ const dst = resolveMount(link, this, $ex);
232
+ if (fs.uuid !== dst.fs.uuid)
233
+ throw UV('EXDEV', $ex);
234
+ const stats = fs.statSync(resolved);
235
+ if (checkAccess) {
236
+ if (!hasAccess(this, stats, constants.R_OK))
237
+ throw UV('EACCES', $ex);
238
+ const dirStats = fs.statSync(dirname(resolved));
239
+ if (!hasAccess(this, dirStats, constants.R_OK))
240
+ throw UV('EACCES', $ex);
241
+ const destStats = fs.statSync(dirname(dst.path));
242
+ if (!hasAccess(this, destStats, constants.W_OK))
243
+ throw UV('EACCES', $ex);
244
+ }
245
+ return fs.linkSync(resolved, dst.path);
889
246
  }
890
- globSync;