@zenfs/core 1.1.3 → 1.1.4
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.
- package/dist/backends/overlay.js +8 -8
- package/dist/backends/port/fs.js +2 -1
- package/dist/backends/port/rpc.js +1 -1
- package/dist/devices.js +4 -2
- package/dist/emulation/async.d.ts +1 -1
- package/dist/emulation/promises.d.ts +4 -0
- package/dist/emulation/promises.js +26 -18
- package/dist/emulation/shared.d.ts +2 -1
- package/dist/emulation/sync.d.ts +1 -0
- package/dist/emulation/sync.js +24 -17
- package/dist/error.js +1 -1
- package/eslint.shared.js +1 -0
- package/package.json +1 -1
- package/src/backends/backend.ts +3 -3
- package/src/backends/file_index.ts +1 -1
- package/src/backends/overlay.ts +8 -8
- package/src/backends/port/fs.ts +4 -3
- package/src/backends/port/rpc.ts +3 -2
- package/src/devices.ts +4 -2
- package/src/emulation/async.ts +1 -1
- package/src/emulation/promises.ts +38 -30
- package/src/emulation/shared.ts +1 -1
- package/src/emulation/sync.ts +35 -29
- package/src/error.ts +1 -1
package/dist/backends/overlay.js
CHANGED
|
@@ -141,7 +141,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
141
141
|
try {
|
|
142
142
|
await this.writable.rename(oldPath, newPath);
|
|
143
143
|
}
|
|
144
|
-
catch
|
|
144
|
+
catch {
|
|
145
145
|
if (this._deletedFiles.has(oldPath)) {
|
|
146
146
|
throw ErrnoError.With('ENOENT', oldPath, 'rename');
|
|
147
147
|
}
|
|
@@ -155,7 +155,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
155
155
|
try {
|
|
156
156
|
this.writable.renameSync(oldPath, newPath);
|
|
157
157
|
}
|
|
158
|
-
catch
|
|
158
|
+
catch {
|
|
159
159
|
if (this._deletedFiles.has(oldPath)) {
|
|
160
160
|
throw ErrnoError.With('ENOENT', oldPath, 'rename');
|
|
161
161
|
}
|
|
@@ -166,7 +166,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
166
166
|
try {
|
|
167
167
|
return await this.writable.stat(path);
|
|
168
168
|
}
|
|
169
|
-
catch
|
|
169
|
+
catch {
|
|
170
170
|
if (this._deletedFiles.has(path)) {
|
|
171
171
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
172
172
|
}
|
|
@@ -181,7 +181,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
181
181
|
try {
|
|
182
182
|
return this.writable.statSync(path);
|
|
183
183
|
}
|
|
184
|
-
catch
|
|
184
|
+
catch {
|
|
185
185
|
if (this._deletedFiles.has(path)) {
|
|
186
186
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
187
187
|
}
|
|
@@ -323,13 +323,13 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
323
323
|
try {
|
|
324
324
|
contents.push(...(await this.writable.readdir(path)));
|
|
325
325
|
}
|
|
326
|
-
catch
|
|
326
|
+
catch {
|
|
327
327
|
// NOP.
|
|
328
328
|
}
|
|
329
329
|
try {
|
|
330
330
|
contents.push(...(await this.readable.readdir(path)).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
|
|
331
331
|
}
|
|
332
|
-
catch
|
|
332
|
+
catch {
|
|
333
333
|
// NOP.
|
|
334
334
|
}
|
|
335
335
|
const seenMap = {};
|
|
@@ -350,13 +350,13 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
350
350
|
try {
|
|
351
351
|
contents = contents.concat(this.writable.readdirSync(path));
|
|
352
352
|
}
|
|
353
|
-
catch
|
|
353
|
+
catch {
|
|
354
354
|
// NOP.
|
|
355
355
|
}
|
|
356
356
|
try {
|
|
357
357
|
contents = contents.concat(this.readable.readdirSync(path).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
|
|
358
358
|
}
|
|
359
|
-
catch
|
|
359
|
+
catch {
|
|
360
360
|
// NOP.
|
|
361
361
|
}
|
|
362
362
|
const seenMap = {};
|
package/dist/backends/port/fs.js
CHANGED
|
@@ -180,7 +180,7 @@ export async function handleRequest(port, fs, request) {
|
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
182
|
break;
|
|
183
|
-
case 'file':
|
|
183
|
+
case 'file': {
|
|
184
184
|
const { fd } = request;
|
|
185
185
|
if (!descriptors.has(fd)) {
|
|
186
186
|
throw new ErrnoError(Errno.EBADF);
|
|
@@ -191,6 +191,7 @@ export async function handleRequest(port, fs, request) {
|
|
|
191
191
|
descriptors.delete(fd);
|
|
192
192
|
}
|
|
193
193
|
break;
|
|
194
|
+
}
|
|
194
195
|
default:
|
|
195
196
|
return;
|
|
196
197
|
}
|
|
@@ -79,7 +79,7 @@ export function catchMessages(port) {
|
|
|
79
79
|
return function (fs) {
|
|
80
80
|
detach(port, handler);
|
|
81
81
|
for (const event of events) {
|
|
82
|
-
const request = 'data' in event ? event.data : event;
|
|
82
|
+
const request = ('data' in event ? event.data : event);
|
|
83
83
|
void handleRequest(port, fs, request);
|
|
84
84
|
}
|
|
85
85
|
};
|
package/dist/devices.js
CHANGED
|
@@ -105,14 +105,16 @@ export class DeviceFile extends File {
|
|
|
105
105
|
closeSync() {
|
|
106
106
|
this.driver.close?.(this);
|
|
107
107
|
}
|
|
108
|
-
|
|
108
|
+
close() {
|
|
109
109
|
this.closeSync();
|
|
110
|
+
return Promise.resolve();
|
|
110
111
|
}
|
|
111
112
|
syncSync() {
|
|
112
113
|
this.driver.sync?.(this);
|
|
113
114
|
}
|
|
114
|
-
|
|
115
|
+
sync() {
|
|
115
116
|
this.syncSync();
|
|
117
|
+
return Promise.resolve();
|
|
116
118
|
}
|
|
117
119
|
chown() {
|
|
118
120
|
throw ErrnoError.With('ENOTSUP', this.path, 'chown');
|
|
@@ -181,7 +181,7 @@ export declare function link(existing: fs.PathLike, newpath: fs.PathLike, cb?: C
|
|
|
181
181
|
*/
|
|
182
182
|
export declare function symlink(target: fs.PathLike, path: fs.PathLike, cb?: Callback): void;
|
|
183
183
|
export declare function symlink(target: fs.PathLike, path: fs.PathLike, type?: fs.symlink.Type, cb?: Callback): void;
|
|
184
|
-
export declare function readlink(path: fs.PathLike, callback: Callback<[string]>
|
|
184
|
+
export declare function readlink(path: fs.PathLike, callback: Callback<[string]>): void;
|
|
185
185
|
export declare function readlink(path: fs.PathLike, options: fs.BufferEncodingOption, callback: Callback<[Uint8Array]>): void;
|
|
186
186
|
export declare function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string | Uint8Array]>): void;
|
|
187
187
|
export declare function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string]>): void;
|
|
@@ -92,6 +92,9 @@ export declare class FileHandle implements promises.FileHandle {
|
|
|
92
92
|
* @experimental
|
|
93
93
|
*/
|
|
94
94
|
readableWebStream(options?: promises.ReadableWebStreamOptions): TReadableStream<Uint8Array>;
|
|
95
|
+
/**
|
|
96
|
+
* @todo Implement
|
|
97
|
+
*/
|
|
95
98
|
readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface;
|
|
96
99
|
[Symbol.asyncDispose](): Promise<void>;
|
|
97
100
|
/**
|
|
@@ -296,6 +299,7 @@ export declare function lutimes(path: fs.PathLike, atime: fs.TimeLike, mtime: fs
|
|
|
296
299
|
* Asynchronous realpath(3) - return the canonicalized absolute pathname.
|
|
297
300
|
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
|
|
298
301
|
* @param options The encoding (or an object specifying the encoding), used as the encoding of the result. Defaults to `'utf8'`.
|
|
302
|
+
* @todo handle options
|
|
299
303
|
*/
|
|
300
304
|
export declare function realpath(path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
|
|
301
305
|
export declare function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
|
|
@@ -206,6 +206,9 @@ export class FileHandle {
|
|
|
206
206
|
}
|
|
207
207
|
return new _gt.ReadableStream({ start, type: options.type });
|
|
208
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* @todo Implement
|
|
211
|
+
*/
|
|
209
212
|
readLines(options) {
|
|
210
213
|
throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
|
|
211
214
|
}
|
|
@@ -383,11 +386,11 @@ export async function exists(path) {
|
|
|
383
386
|
}
|
|
384
387
|
export async function stat(path, options) {
|
|
385
388
|
path = normalizePath(path);
|
|
386
|
-
const { fs, path: resolved } = resolveMount(
|
|
389
|
+
const { fs, path: resolved } = resolveMount(await realpath(path));
|
|
387
390
|
try {
|
|
388
391
|
const stats = await fs.stat(resolved);
|
|
389
392
|
if (!stats.hasAccess(constants.R_OK)) {
|
|
390
|
-
throw ErrnoError.With('EACCES',
|
|
393
|
+
throw ErrnoError.With('EACCES', resolved, 'stat');
|
|
391
394
|
}
|
|
392
395
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
393
396
|
}
|
|
@@ -448,7 +451,7 @@ unlink;
|
|
|
448
451
|
async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
|
|
449
452
|
path = normalizePath(path);
|
|
450
453
|
const mode = normalizeMode(_mode, 0o644), flag = parseFlag(_flag);
|
|
451
|
-
path = resolveSymlinks
|
|
454
|
+
path = resolveSymlinks ? await realpath(path) : path;
|
|
452
455
|
const { fs, path: resolved } = resolveMount(path);
|
|
453
456
|
const stats = await fs.stat(resolved).catch(() => null);
|
|
454
457
|
if (!stats) {
|
|
@@ -577,7 +580,7 @@ appendFile;
|
|
|
577
580
|
// DIRECTORY-ONLY METHODS
|
|
578
581
|
export async function rmdir(path) {
|
|
579
582
|
path = normalizePath(path);
|
|
580
|
-
path =
|
|
583
|
+
path = await realpath(path);
|
|
581
584
|
const { fs, path: resolved } = resolveMount(path);
|
|
582
585
|
try {
|
|
583
586
|
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
|
|
@@ -594,8 +597,7 @@ rmdir;
|
|
|
594
597
|
export async function mkdir(path, options) {
|
|
595
598
|
options = typeof options === 'object' ? options : { mode: options };
|
|
596
599
|
const mode = normalizeMode(options?.mode, 0o777);
|
|
597
|
-
path = normalizePath(path);
|
|
598
|
-
path = (await exists(path)) ? await realpath(path) : path;
|
|
600
|
+
path = await realpath(normalizePath(path));
|
|
599
601
|
const { fs, path: resolved } = resolveMount(path);
|
|
600
602
|
const errorPaths = { [resolved]: path };
|
|
601
603
|
try {
|
|
@@ -628,12 +630,11 @@ export async function mkdir(path, options) {
|
|
|
628
630
|
mkdir;
|
|
629
631
|
export async function readdir(path, options) {
|
|
630
632
|
options = typeof options === 'object' ? options : { encoding: options };
|
|
631
|
-
path = normalizePath(path);
|
|
632
|
-
|
|
633
|
+
path = await realpath(normalizePath(path));
|
|
634
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
635
|
+
if (!(await fs.stat(resolved)).hasAccess(constants.R_OK)) {
|
|
633
636
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
634
637
|
}
|
|
635
|
-
path = (await exists(path)) ? await realpath(path) : path;
|
|
636
|
-
const { fs, path: resolved } = resolveMount(path);
|
|
637
638
|
const entries = await fs.readdir(resolved).catch((e) => {
|
|
638
639
|
throw fixError(e, { [resolved]: path });
|
|
639
640
|
});
|
|
@@ -649,8 +650,12 @@ export async function readdir(path, options) {
|
|
|
649
650
|
}
|
|
650
651
|
const values = [];
|
|
651
652
|
for (const entry of entries) {
|
|
652
|
-
|
|
653
|
-
|
|
653
|
+
let stats;
|
|
654
|
+
if (options?.recursive || options?.withFileTypes) {
|
|
655
|
+
stats = await fs.stat(join(resolved, entry)).catch((error) => {
|
|
656
|
+
throw fixError(error, { [resolved]: path });
|
|
657
|
+
});
|
|
658
|
+
}
|
|
654
659
|
if (options?.withFileTypes) {
|
|
655
660
|
values.push(new Dirent(entry, stats));
|
|
656
661
|
}
|
|
@@ -663,7 +668,7 @@ export async function readdir(path, options) {
|
|
|
663
668
|
if (!options?.recursive || !stats?.isDirectory()) {
|
|
664
669
|
continue;
|
|
665
670
|
}
|
|
666
|
-
for (const subEntry of await readdir(
|
|
671
|
+
for (const subEntry of await readdir(join(path, entry), options)) {
|
|
667
672
|
if (subEntry instanceof Dirent) {
|
|
668
673
|
subEntry.path = join(entry, subEntry.path);
|
|
669
674
|
values.push(subEntry);
|
|
@@ -861,9 +866,12 @@ export async function realpath(path, options) {
|
|
|
861
866
|
if (!stats.isSymbolicLink()) {
|
|
862
867
|
return lpath;
|
|
863
868
|
}
|
|
864
|
-
return realpath(mountPoint + (await readlink(lpath)));
|
|
869
|
+
return await realpath(mountPoint + (await readlink(lpath)));
|
|
865
870
|
}
|
|
866
871
|
catch (e) {
|
|
872
|
+
if (e.code == 'ENOENT') {
|
|
873
|
+
return path;
|
|
874
|
+
}
|
|
867
875
|
throw fixError(e, { [resolvedPath]: lpath });
|
|
868
876
|
}
|
|
869
877
|
}
|
|
@@ -972,9 +980,9 @@ copyFile;
|
|
|
972
980
|
* @returns A `Dir` object representing the opened directory.
|
|
973
981
|
* @todo Use options
|
|
974
982
|
*/
|
|
975
|
-
export
|
|
983
|
+
export function opendir(path, options) {
|
|
976
984
|
path = normalizePath(path);
|
|
977
|
-
return new Dir(path);
|
|
985
|
+
return Promise.resolve(new Dir(path));
|
|
978
986
|
}
|
|
979
987
|
opendir;
|
|
980
988
|
/**
|
|
@@ -1026,8 +1034,8 @@ export async function cp(source, destination, opts) {
|
|
|
1026
1034
|
}
|
|
1027
1035
|
}
|
|
1028
1036
|
cp;
|
|
1029
|
-
export
|
|
1037
|
+
export function statfs(path, opts) {
|
|
1030
1038
|
path = normalizePath(path);
|
|
1031
1039
|
const { fs } = resolveMount(path);
|
|
1032
|
-
return _statfs(fs, opts?.bigint);
|
|
1040
|
+
return Promise.resolve(_statfs(fs, opts?.bigint));
|
|
1033
1041
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BigIntStatsFs, StatsFs } from 'node:fs';
|
|
2
|
+
import { ErrnoError } from '../error.js';
|
|
2
3
|
import type { File } from '../file.js';
|
|
3
4
|
import type { FileSystem } from '../filesystem.js';
|
|
4
5
|
import { type AbsolutePath } from './path.js';
|
|
@@ -36,7 +37,7 @@ export declare function fixPaths(text: string, paths: Record<string, string>): s
|
|
|
36
37
|
* Fix paths in error stacks
|
|
37
38
|
* @hidden
|
|
38
39
|
*/
|
|
39
|
-
export declare function fixError<E extends
|
|
40
|
+
export declare function fixError<E extends ErrnoError>(e: E, paths: Record<string, string>): E;
|
|
40
41
|
export declare function mountObject(mounts: MountObject): void;
|
|
41
42
|
/**
|
|
42
43
|
* @hidden
|
package/dist/emulation/sync.d.ts
CHANGED
|
@@ -199,6 +199,7 @@ export declare function writevSync(fd: number, buffers: readonly ArrayBufferView
|
|
|
199
199
|
* @param path The path to the directory.
|
|
200
200
|
* @param options Options for opening the directory.
|
|
201
201
|
* @returns A `Dir` object representing the opened directory.
|
|
202
|
+
* @todo Handle options
|
|
202
203
|
*/
|
|
203
204
|
export declare function opendirSync(path: fs.PathLike, options?: fs.OpenDirOptions): Dir;
|
|
204
205
|
/**
|
package/dist/emulation/sync.js
CHANGED
|
@@ -97,11 +97,11 @@ export function existsSync(path) {
|
|
|
97
97
|
existsSync;
|
|
98
98
|
export function statSync(path, options) {
|
|
99
99
|
path = normalizePath(path);
|
|
100
|
-
const { fs, path: resolved } = resolveMount(
|
|
100
|
+
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
101
101
|
try {
|
|
102
102
|
const stats = fs.statSync(resolved);
|
|
103
103
|
if (!stats.hasAccess(constants.R_OK)) {
|
|
104
|
-
throw ErrnoError.With('EACCES',
|
|
104
|
+
throw ErrnoError.With('EACCES', resolved, 'stat');
|
|
105
105
|
}
|
|
106
106
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
107
107
|
}
|
|
@@ -159,9 +159,16 @@ unlinkSync;
|
|
|
159
159
|
function _openSync(path, _flag, _mode, resolveSymlinks = true) {
|
|
160
160
|
path = normalizePath(path);
|
|
161
161
|
const mode = normalizeMode(_mode, 0o644), flag = parseFlag(_flag);
|
|
162
|
-
path = resolveSymlinks
|
|
162
|
+
path = resolveSymlinks ? realpathSync(path) : path;
|
|
163
163
|
const { fs, path: resolved } = resolveMount(path);
|
|
164
|
-
|
|
164
|
+
let stats;
|
|
165
|
+
try {
|
|
166
|
+
stats = fs.statSync(resolved);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// nothing
|
|
170
|
+
}
|
|
171
|
+
if (!stats) {
|
|
165
172
|
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
|
|
166
173
|
throw ErrnoError.With('ENOENT', path, '_open');
|
|
167
174
|
}
|
|
@@ -175,7 +182,6 @@ function _openSync(path, _flag, _mode, resolveSymlinks = true) {
|
|
|
175
182
|
}
|
|
176
183
|
return fs.createFileSync(resolved, flag, mode);
|
|
177
184
|
}
|
|
178
|
-
const stats = fs.statSync(resolved);
|
|
179
185
|
if (!stats.hasAccess(mode) || !stats.hasAccess(flagToMode(flag))) {
|
|
180
186
|
throw ErrnoError.With('EACCES', path, '_open');
|
|
181
187
|
}
|
|
@@ -384,7 +390,7 @@ export function futimesSync(fd, atime, mtime) {
|
|
|
384
390
|
futimesSync;
|
|
385
391
|
export function rmdirSync(path) {
|
|
386
392
|
path = normalizePath(path);
|
|
387
|
-
const { fs, path: resolved } = resolveMount(
|
|
393
|
+
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
388
394
|
try {
|
|
389
395
|
if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
|
|
390
396
|
throw ErrnoError.With('EACCES', resolved, 'rmdir');
|
|
@@ -400,8 +406,7 @@ rmdirSync;
|
|
|
400
406
|
export function mkdirSync(path, options) {
|
|
401
407
|
options = typeof options === 'object' ? options : { mode: options };
|
|
402
408
|
const mode = normalizeMode(options?.mode, 0o777);
|
|
403
|
-
path = normalizePath(path);
|
|
404
|
-
path = existsSync(path) ? realpathSync(path) : path;
|
|
409
|
+
path = realpathSync(normalizePath(path));
|
|
405
410
|
const { fs, path: resolved } = resolveMount(path);
|
|
406
411
|
const errorPaths = { [resolved]: path };
|
|
407
412
|
try {
|
|
@@ -433,12 +438,12 @@ mkdirSync;
|
|
|
433
438
|
export function readdirSync(path, options) {
|
|
434
439
|
options = typeof options === 'object' ? options : { encoding: options };
|
|
435
440
|
path = normalizePath(path);
|
|
436
|
-
const { fs, path: resolved } = resolveMount(
|
|
441
|
+
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
437
442
|
let entries;
|
|
438
|
-
if (!statSync(path).hasAccess(constants.R_OK)) {
|
|
439
|
-
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
440
|
-
}
|
|
441
443
|
try {
|
|
444
|
+
if (!fs.statSync(resolved).hasAccess(constants.R_OK)) {
|
|
445
|
+
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
446
|
+
}
|
|
442
447
|
entries = fs.readdirSync(resolved);
|
|
443
448
|
}
|
|
444
449
|
catch (e) {
|
|
@@ -458,8 +463,7 @@ export function readdirSync(path, options) {
|
|
|
458
463
|
// Iterate over entries and handle recursive case if needed
|
|
459
464
|
const values = [];
|
|
460
465
|
for (const entry of entries) {
|
|
461
|
-
const
|
|
462
|
-
const entryStat = statSync(fullPath);
|
|
466
|
+
const entryStat = fs.statSync(join(resolved, entry));
|
|
463
467
|
if (options?.withFileTypes) {
|
|
464
468
|
values.push(new Dirent(entry, entryStat));
|
|
465
469
|
}
|
|
@@ -471,7 +475,7 @@ export function readdirSync(path, options) {
|
|
|
471
475
|
}
|
|
472
476
|
if (!entryStat.isDirectory() || !options?.recursive)
|
|
473
477
|
continue;
|
|
474
|
-
for (const subEntry of readdirSync(
|
|
478
|
+
for (const subEntry of readdirSync(join(path, entry), options)) {
|
|
475
479
|
if (subEntry instanceof Dirent) {
|
|
476
480
|
subEntry.path = join(entry, subEntry.path);
|
|
477
481
|
values.push(subEntry);
|
|
@@ -596,13 +600,15 @@ export function realpathSync(path, options) {
|
|
|
596
600
|
return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
|
|
597
601
|
}
|
|
598
602
|
catch (e) {
|
|
603
|
+
if (e.code == 'ENOENT') {
|
|
604
|
+
return path;
|
|
605
|
+
}
|
|
599
606
|
throw fixError(e, { [resolvedPath]: lpath });
|
|
600
607
|
}
|
|
601
608
|
}
|
|
602
609
|
realpathSync;
|
|
603
610
|
export function accessSync(path, mode = 0o600) {
|
|
604
|
-
|
|
605
|
-
if (!stats.hasAccess(mode)) {
|
|
611
|
+
if (!statSync(path).hasAccess(mode)) {
|
|
606
612
|
throw new ErrnoError(Errno.EACCES);
|
|
607
613
|
}
|
|
608
614
|
}
|
|
@@ -706,6 +712,7 @@ writevSync;
|
|
|
706
712
|
* @param path The path to the directory.
|
|
707
713
|
* @param options Options for opening the directory.
|
|
708
714
|
* @returns A `Dir` object representing the opened directory.
|
|
715
|
+
* @todo Handle options
|
|
709
716
|
*/
|
|
710
717
|
export function opendirSync(path, options) {
|
|
711
718
|
path = normalizePath(path);
|
package/dist/error.js
CHANGED
|
@@ -261,7 +261,7 @@ export class ErrnoError extends Error {
|
|
|
261
261
|
this.path = path;
|
|
262
262
|
this.syscall = syscall;
|
|
263
263
|
this.code = Errno[errno];
|
|
264
|
-
this.message =
|
|
264
|
+
this.message = this.code + ': ' + message + (this.path ? `, '${this.path}'` : '');
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
267
267
|
* @returns A friendly error message.
|
package/eslint.shared.js
CHANGED
package/package.json
CHANGED
package/src/backends/backend.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RequiredKeys } from 'utilium';
|
|
1
|
+
import type { Entries, RequiredKeys } from 'utilium';
|
|
2
2
|
import { ErrnoError, Errno } from '../error.js';
|
|
3
3
|
import type { FileSystem } from '../filesystem.js';
|
|
4
4
|
import { levenshtein } from '../utils.js';
|
|
@@ -102,7 +102,7 @@ export async function checkOptions<T extends Backend>(backend: T, options: Recor
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// Check for required options.
|
|
105
|
-
for (const [optName, opt] of Object.entries(backend.options)) {
|
|
105
|
+
for (const [optName, opt] of Object.entries(backend.options) as Entries<OptionsConfig<Record<string, any>>>) {
|
|
106
106
|
const providedValue = options?.[optName];
|
|
107
107
|
|
|
108
108
|
if (providedValue === undefined || providedValue === null) {
|
|
@@ -133,7 +133,7 @@ export async function checkOptions<T extends Backend>(backend: T, options: Recor
|
|
|
133
133
|
throw new ErrnoError(
|
|
134
134
|
Errno.EINVAL,
|
|
135
135
|
`${backend.name}: Value provided for option ${optName} is not the proper type. Expected ${
|
|
136
|
-
Array.isArray(opt.type) ? `one of {${opt.type.join(', ')}}` : opt.type
|
|
136
|
+
Array.isArray(opt.type) ? `one of {${opt.type.join(', ')}}` : (opt.type as string)
|
|
137
137
|
}, but received ${typeof providedValue}`
|
|
138
138
|
);
|
|
139
139
|
}
|
|
@@ -202,7 +202,7 @@ export abstract class IndexFS extends Readonly(FileSystem) {
|
|
|
202
202
|
if (!content.every(item => typeof item == 'string')) {
|
|
203
203
|
throw ErrnoError.With('ENODATA', path, 'readdir');
|
|
204
204
|
}
|
|
205
|
-
return content
|
|
205
|
+
return content;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
|
package/src/backends/overlay.ts
CHANGED
|
@@ -128,7 +128,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
128
128
|
|
|
129
129
|
try {
|
|
130
130
|
await this.writable.rename(oldPath, newPath);
|
|
131
|
-
} catch
|
|
131
|
+
} catch {
|
|
132
132
|
if (this._deletedFiles.has(oldPath)) {
|
|
133
133
|
throw ErrnoError.With('ENOENT', oldPath, 'rename');
|
|
134
134
|
}
|
|
@@ -144,7 +144,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
144
144
|
|
|
145
145
|
try {
|
|
146
146
|
this.writable.renameSync(oldPath, newPath);
|
|
147
|
-
} catch
|
|
147
|
+
} catch {
|
|
148
148
|
if (this._deletedFiles.has(oldPath)) {
|
|
149
149
|
throw ErrnoError.With('ENOENT', oldPath, 'rename');
|
|
150
150
|
}
|
|
@@ -155,7 +155,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
155
155
|
this.checkInitialized();
|
|
156
156
|
try {
|
|
157
157
|
return await this.writable.stat(path);
|
|
158
|
-
} catch
|
|
158
|
+
} catch {
|
|
159
159
|
if (this._deletedFiles.has(path)) {
|
|
160
160
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
161
161
|
}
|
|
@@ -170,7 +170,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
170
170
|
this.checkInitialized();
|
|
171
171
|
try {
|
|
172
172
|
return this.writable.statSync(path);
|
|
173
|
-
} catch
|
|
173
|
+
} catch {
|
|
174
174
|
if (this._deletedFiles.has(path)) {
|
|
175
175
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
176
176
|
}
|
|
@@ -329,12 +329,12 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
329
329
|
const contents: string[] = [];
|
|
330
330
|
try {
|
|
331
331
|
contents.push(...(await this.writable.readdir(path)));
|
|
332
|
-
} catch
|
|
332
|
+
} catch {
|
|
333
333
|
// NOP.
|
|
334
334
|
}
|
|
335
335
|
try {
|
|
336
336
|
contents.push(...(await this.readable.readdir(path)).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
|
|
337
|
-
} catch
|
|
337
|
+
} catch {
|
|
338
338
|
// NOP.
|
|
339
339
|
}
|
|
340
340
|
const seenMap: { [name: string]: boolean } = {};
|
|
@@ -356,12 +356,12 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
356
356
|
let contents: string[] = [];
|
|
357
357
|
try {
|
|
358
358
|
contents = contents.concat(this.writable.readdirSync(path));
|
|
359
|
-
} catch
|
|
359
|
+
} catch {
|
|
360
360
|
// NOP.
|
|
361
361
|
}
|
|
362
362
|
try {
|
|
363
363
|
contents = contents.concat(this.readable.readdirSync(path).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
|
|
364
|
-
} catch
|
|
364
|
+
} catch {
|
|
365
365
|
// NOP.
|
|
366
366
|
}
|
|
367
367
|
const seenMap: { [name: string]: boolean } = {};
|
package/src/backends/port/fs.ts
CHANGED
|
@@ -260,7 +260,7 @@ export async function handleRequest(port: RPC.Port, fs: FileSystem, request: Fil
|
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
262
|
break;
|
|
263
|
-
case 'file':
|
|
263
|
+
case 'file': {
|
|
264
264
|
const { fd } = request;
|
|
265
265
|
if (!descriptors.has(fd)) {
|
|
266
266
|
throw new ErrnoError(Errno.EBADF);
|
|
@@ -271,11 +271,12 @@ export async function handleRequest(port: RPC.Port, fs: FileSystem, request: Fil
|
|
|
271
271
|
descriptors.delete(fd);
|
|
272
272
|
}
|
|
273
273
|
break;
|
|
274
|
+
}
|
|
274
275
|
default:
|
|
275
276
|
return;
|
|
276
277
|
}
|
|
277
|
-
} catch (e
|
|
278
|
-
value = e instanceof ErrnoError ? e.toJSON() : e.toString();
|
|
278
|
+
} catch (e) {
|
|
279
|
+
value = e instanceof ErrnoError ? e.toJSON() : (e as object).toString();
|
|
279
280
|
error = true;
|
|
280
281
|
}
|
|
281
282
|
|
package/src/backends/port/rpc.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { Errno, ErrnoError, type ErrnoErrorJSON } from '../../error.js';
|
|
3
|
+
import type { FileSystem } from '../../filesystem.js';
|
|
3
4
|
import type { Backend, FilesystemOf } from '../backend.js';
|
|
4
5
|
import { handleRequest, PortFile, type PortFS } from './fs.js';
|
|
5
6
|
import type { FileOrFSRequest } from './fs.js';
|
|
@@ -156,10 +157,10 @@ export function catchMessages<T extends Backend>(port: Port): (fs: FilesystemOf<
|
|
|
156
157
|
const events: _MessageEvent[] = [];
|
|
157
158
|
const handler = events.push.bind(events);
|
|
158
159
|
attach(port, handler);
|
|
159
|
-
return function (fs:
|
|
160
|
+
return function (fs: FileSystem) {
|
|
160
161
|
detach(port, handler);
|
|
161
162
|
for (const event of events) {
|
|
162
|
-
const request
|
|
163
|
+
const request = ('data' in event ? event.data : event) as FileOrFSRequest;
|
|
163
164
|
void handleRequest(port, fs, request);
|
|
164
165
|
}
|
|
165
166
|
};
|
package/src/devices.ts
CHANGED
|
@@ -134,16 +134,18 @@ export class DeviceFile extends File {
|
|
|
134
134
|
this.driver.close?.(this);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
public
|
|
137
|
+
public close(): Promise<void> {
|
|
138
138
|
this.closeSync();
|
|
139
|
+
return Promise.resolve();
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
public syncSync(): void {
|
|
142
143
|
this.driver.sync?.(this);
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
public
|
|
146
|
+
public sync(): Promise<void> {
|
|
146
147
|
this.syncSync();
|
|
148
|
+
return Promise.resolve();
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
public chown(): Promise<void> {
|
package/src/emulation/async.ts
CHANGED
|
@@ -425,7 +425,7 @@ export function symlink(target: fs.PathLike, path: fs.PathLike, typeOrCB?: fs.sy
|
|
|
425
425
|
}
|
|
426
426
|
symlink satisfies Omit<typeof fs.symlink, '__promisify__'>;
|
|
427
427
|
|
|
428
|
-
export function readlink(path: fs.PathLike, callback: Callback<[string]>
|
|
428
|
+
export function readlink(path: fs.PathLike, callback: Callback<[string]>): void;
|
|
429
429
|
export function readlink(path: fs.PathLike, options: fs.BufferEncodingOption, callback: Callback<[Uint8Array]>): void;
|
|
430
430
|
export function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string | Uint8Array]>): void;
|
|
431
431
|
export function readlink(path: fs.PathLike, options: fs.EncodingOption, callback: Callback<[string]>): void;
|
|
@@ -202,6 +202,9 @@ export class FileHandle implements promises.FileHandle {
|
|
|
202
202
|
return new (_gt as { ReadableStream: new (...args: unknown[]) => TReadableStream<Uint8Array> }).ReadableStream({ start, type: options.type });
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
/**
|
|
206
|
+
* @todo Implement
|
|
207
|
+
*/
|
|
205
208
|
public readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface {
|
|
206
209
|
throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
|
|
207
210
|
}
|
|
@@ -396,7 +399,7 @@ export async function rename(oldPath: fs.PathLike, newPath: fs.PathLike): Promis
|
|
|
396
399
|
await unlink(oldPath);
|
|
397
400
|
emitChange('rename', oldPath.toString());
|
|
398
401
|
} catch (e) {
|
|
399
|
-
throw fixError(e as
|
|
402
|
+
throw fixError(e as ErrnoError, { [src.path]: oldPath, [dst.path]: newPath });
|
|
400
403
|
}
|
|
401
404
|
}
|
|
402
405
|
rename satisfies typeof promises.rename;
|
|
@@ -422,15 +425,15 @@ export async function stat(path: fs.PathLike, options?: { bigint?: false }): Pro
|
|
|
422
425
|
export async function stat(path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats>;
|
|
423
426
|
export async function stat(path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats> {
|
|
424
427
|
path = normalizePath(path);
|
|
425
|
-
const { fs, path: resolved } = resolveMount(
|
|
428
|
+
const { fs, path: resolved } = resolveMount(await realpath(path));
|
|
426
429
|
try {
|
|
427
430
|
const stats = await fs.stat(resolved);
|
|
428
431
|
if (!stats.hasAccess(constants.R_OK)) {
|
|
429
|
-
throw ErrnoError.With('EACCES',
|
|
432
|
+
throw ErrnoError.With('EACCES', resolved, 'stat');
|
|
430
433
|
}
|
|
431
434
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
432
435
|
} catch (e) {
|
|
433
|
-
throw fixError(e as
|
|
436
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
434
437
|
}
|
|
435
438
|
}
|
|
436
439
|
stat satisfies typeof promises.stat;
|
|
@@ -449,7 +452,7 @@ export async function lstat(path: fs.PathLike, options?: fs.StatOptions): Promis
|
|
|
449
452
|
const stats = await fs.stat(resolved);
|
|
450
453
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
451
454
|
} catch (e) {
|
|
452
|
-
throw fixError(e as
|
|
455
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
453
456
|
}
|
|
454
457
|
}
|
|
455
458
|
lstat satisfies typeof promises.lstat;
|
|
@@ -472,7 +475,7 @@ export async function unlink(path: fs.PathLike): Promise<void> {
|
|
|
472
475
|
await fs.unlink(resolved);
|
|
473
476
|
emitChange('rename', path.toString());
|
|
474
477
|
} catch (e) {
|
|
475
|
-
throw fixError(e as
|
|
478
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
476
479
|
}
|
|
477
480
|
}
|
|
478
481
|
unlink satisfies typeof promises.unlink;
|
|
@@ -486,7 +489,7 @@ async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o6
|
|
|
486
489
|
const mode = normalizeMode(_mode, 0o644),
|
|
487
490
|
flag = parseFlag(_flag);
|
|
488
491
|
|
|
489
|
-
path = resolveSymlinks
|
|
492
|
+
path = resolveSymlinks ? await realpath(path) : path;
|
|
490
493
|
const { fs, path: resolved } = resolveMount(path);
|
|
491
494
|
|
|
492
495
|
const stats = await fs.stat(resolved).catch(() => null);
|
|
@@ -617,7 +620,7 @@ appendFile satisfies typeof promises.appendFile;
|
|
|
617
620
|
|
|
618
621
|
export async function rmdir(path: fs.PathLike): Promise<void> {
|
|
619
622
|
path = normalizePath(path);
|
|
620
|
-
path =
|
|
623
|
+
path = await realpath(path);
|
|
621
624
|
const { fs, path: resolved } = resolveMount(path);
|
|
622
625
|
try {
|
|
623
626
|
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
|
|
@@ -626,7 +629,7 @@ export async function rmdir(path: fs.PathLike): Promise<void> {
|
|
|
626
629
|
await fs.rmdir(resolved);
|
|
627
630
|
emitChange('rename', path.toString());
|
|
628
631
|
} catch (e) {
|
|
629
|
-
throw fixError(e as
|
|
632
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
630
633
|
}
|
|
631
634
|
}
|
|
632
635
|
rmdir satisfies typeof promises.rmdir;
|
|
@@ -644,8 +647,7 @@ export async function mkdir(path: fs.PathLike, options?: fs.Mode | fs.MakeDirect
|
|
|
644
647
|
options = typeof options === 'object' ? options : { mode: options };
|
|
645
648
|
const mode = normalizeMode(options?.mode, 0o777);
|
|
646
649
|
|
|
647
|
-
path = normalizePath(path);
|
|
648
|
-
path = (await exists(path)) ? await realpath(path) : path;
|
|
650
|
+
path = await realpath(normalizePath(path));
|
|
649
651
|
const { fs, path: resolved } = resolveMount(path);
|
|
650
652
|
const errorPaths: Record<string, string> = { [resolved]: path };
|
|
651
653
|
|
|
@@ -673,7 +675,7 @@ export async function mkdir(path: fs.PathLike, options?: fs.Mode | fs.MakeDirect
|
|
|
673
675
|
}
|
|
674
676
|
return dirs[0];
|
|
675
677
|
} catch (e) {
|
|
676
|
-
throw fixError(e as
|
|
678
|
+
throw fixError(e as ErrnoError, errorPaths);
|
|
677
679
|
}
|
|
678
680
|
}
|
|
679
681
|
mkdir satisfies typeof promises.mkdir;
|
|
@@ -699,17 +701,16 @@ export async function readdir(
|
|
|
699
701
|
options?: { withFileTypes?: boolean; recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null } | BufferEncoding | 'buffer' | null
|
|
700
702
|
): Promise<string[] | Dirent[] | Buffer[]> {
|
|
701
703
|
options = typeof options === 'object' ? options : { encoding: options };
|
|
702
|
-
path = normalizePath(path);
|
|
704
|
+
path = await realpath(normalizePath(path));
|
|
705
|
+
|
|
706
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
703
707
|
|
|
704
|
-
if (!(await stat(
|
|
708
|
+
if (!(await fs.stat(resolved)).hasAccess(constants.R_OK)) {
|
|
705
709
|
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
706
710
|
}
|
|
707
711
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const entries = await fs.readdir(resolved).catch((e: Error) => {
|
|
712
|
-
throw fixError(e as Error, { [resolved]: path });
|
|
712
|
+
const entries = await fs.readdir(resolved).catch((e: ErrnoError) => {
|
|
713
|
+
throw fixError(e, { [resolved]: path });
|
|
713
714
|
});
|
|
714
715
|
|
|
715
716
|
for (const point of mounts.keys()) {
|
|
@@ -725,9 +726,12 @@ export async function readdir(
|
|
|
725
726
|
|
|
726
727
|
const values: (string | Dirent | Buffer)[] = [];
|
|
727
728
|
for (const entry of entries) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
729
|
+
let stats: Stats | undefined | void;
|
|
730
|
+
if (options?.recursive || options?.withFileTypes) {
|
|
731
|
+
stats = await fs.stat(join(resolved, entry)).catch((error: ErrnoError) => {
|
|
732
|
+
throw fixError(error, { [resolved]: path });
|
|
733
|
+
});
|
|
734
|
+
}
|
|
731
735
|
if (options?.withFileTypes) {
|
|
732
736
|
values.push(new Dirent(entry, stats!));
|
|
733
737
|
} else if (options?.encoding === 'buffer') {
|
|
@@ -740,7 +744,7 @@ export async function readdir(
|
|
|
740
744
|
continue;
|
|
741
745
|
}
|
|
742
746
|
|
|
743
|
-
for (const subEntry of await readdir(
|
|
747
|
+
for (const subEntry of await readdir(join(path, entry), options)) {
|
|
744
748
|
if (subEntry instanceof Dirent) {
|
|
745
749
|
subEntry.path = join(entry, subEntry.path);
|
|
746
750
|
values.push(subEntry);
|
|
@@ -778,7 +782,7 @@ export async function link(targetPath: fs.PathLike, linkPath: fs.PathLike): Prom
|
|
|
778
782
|
}
|
|
779
783
|
return await fs.link(path, link.path);
|
|
780
784
|
} catch (e) {
|
|
781
|
-
throw fixError(e as
|
|
785
|
+
throw fixError(e as ErrnoError, { [link.path]: linkPath, [path]: targetPath });
|
|
782
786
|
}
|
|
783
787
|
}
|
|
784
788
|
link satisfies typeof promises.link;
|
|
@@ -863,6 +867,7 @@ lutimes satisfies typeof promises.lutimes;
|
|
|
863
867
|
* Asynchronous realpath(3) - return the canonicalized absolute pathname.
|
|
864
868
|
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
|
|
865
869
|
* @param options The encoding (or an object specifying the encoding), used as the encoding of the result. Defaults to `'utf8'`.
|
|
870
|
+
* @todo handle options
|
|
866
871
|
*/
|
|
867
872
|
export async function realpath(path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
|
|
868
873
|
export async function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
|
|
@@ -878,9 +883,12 @@ export async function realpath(path: fs.PathLike, options?: fs.EncodingOption |
|
|
|
878
883
|
return lpath;
|
|
879
884
|
}
|
|
880
885
|
|
|
881
|
-
return realpath(mountPoint + (await readlink(lpath)));
|
|
886
|
+
return await realpath(mountPoint + (await readlink(lpath)));
|
|
882
887
|
} catch (e) {
|
|
883
|
-
|
|
888
|
+
if ((e as ErrnoError).code == 'ENOENT') {
|
|
889
|
+
return path;
|
|
890
|
+
}
|
|
891
|
+
throw fixError(e as ErrnoError, { [resolvedPath]: lpath });
|
|
884
892
|
}
|
|
885
893
|
}
|
|
886
894
|
realpath satisfies typeof promises.realpath;
|
|
@@ -1016,9 +1024,9 @@ copyFile satisfies typeof promises.copyFile;
|
|
|
1016
1024
|
* @returns A `Dir` object representing the opened directory.
|
|
1017
1025
|
* @todo Use options
|
|
1018
1026
|
*/
|
|
1019
|
-
export
|
|
1027
|
+
export function opendir(path: fs.PathLike, options?: fs.OpenDirOptions): Promise<Dir> {
|
|
1020
1028
|
path = normalizePath(path);
|
|
1021
|
-
return new Dir(path);
|
|
1029
|
+
return Promise.resolve(new Dir(path));
|
|
1022
1030
|
}
|
|
1023
1031
|
opendir satisfies typeof promises.opendir;
|
|
1024
1032
|
|
|
@@ -1083,8 +1091,8 @@ cp satisfies typeof promises.cp;
|
|
|
1083
1091
|
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions & { bigint?: false }): Promise<fs.StatsFs>;
|
|
1084
1092
|
export async function statfs(path: fs.PathLike, opts: fs.StatFsOptions & { bigint: true }): Promise<fs.BigIntStatsFs>;
|
|
1085
1093
|
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs>;
|
|
1086
|
-
export
|
|
1094
|
+
export function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<fs.StatsFs | fs.BigIntStatsFs> {
|
|
1087
1095
|
path = normalizePath(path);
|
|
1088
1096
|
const { fs } = resolveMount(path);
|
|
1089
|
-
return _statfs(fs, opts?.bigint);
|
|
1097
|
+
return Promise.resolve(_statfs(fs, opts?.bigint));
|
|
1090
1098
|
}
|
package/src/emulation/shared.ts
CHANGED
|
@@ -99,7 +99,7 @@ export function fixPaths(text: string, paths: Record<string, string>): string {
|
|
|
99
99
|
* Fix paths in error stacks
|
|
100
100
|
* @hidden
|
|
101
101
|
*/
|
|
102
|
-
export function fixError<E extends
|
|
102
|
+
export function fixError<E extends ErrnoError>(e: E, paths: Record<string, string>): E {
|
|
103
103
|
if (typeof e.stack == 'string') {
|
|
104
104
|
e.stack = fixPaths(e.stack, paths);
|
|
105
105
|
}
|
package/src/emulation/sync.ts
CHANGED
|
@@ -31,7 +31,7 @@ export function renameSync(oldPath: fs.PathLike, newPath: fs.PathLike): void {
|
|
|
31
31
|
unlinkSync(oldPath);
|
|
32
32
|
emitChange('rename', oldPath.toString());
|
|
33
33
|
} catch (e) {
|
|
34
|
-
throw fixError(e as
|
|
34
|
+
throw fixError(e as ErrnoError, { [oldMount.path]: oldPath, [newMount.path]: newPath });
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
renameSync satisfies typeof fs.renameSync;
|
|
@@ -58,15 +58,15 @@ export function statSync(path: fs.PathLike, options?: { bigint?: boolean }): Sta
|
|
|
58
58
|
export function statSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
|
|
59
59
|
export function statSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
|
|
60
60
|
path = normalizePath(path);
|
|
61
|
-
const { fs, path: resolved } = resolveMount(
|
|
61
|
+
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
62
62
|
try {
|
|
63
63
|
const stats = fs.statSync(resolved);
|
|
64
64
|
if (!stats.hasAccess(constants.R_OK)) {
|
|
65
|
-
throw ErrnoError.With('EACCES',
|
|
65
|
+
throw ErrnoError.With('EACCES', resolved, 'stat');
|
|
66
66
|
}
|
|
67
67
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
68
68
|
} catch (e) {
|
|
69
|
-
throw fixError(e as
|
|
69
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
statSync satisfies typeof fs.statSync;
|
|
@@ -85,7 +85,7 @@ export function lstatSync(path: fs.PathLike, options?: fs.StatOptions): Stats |
|
|
|
85
85
|
const stats = fs.statSync(resolved);
|
|
86
86
|
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
87
87
|
} catch (e) {
|
|
88
|
-
throw fixError(e as
|
|
88
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
lstatSync satisfies typeof fs.lstatSync;
|
|
@@ -110,7 +110,7 @@ export function unlinkSync(path: fs.PathLike): void {
|
|
|
110
110
|
fs.unlinkSync(resolved);
|
|
111
111
|
emitChange('rename', path.toString());
|
|
112
112
|
} catch (e) {
|
|
113
|
-
throw fixError(e as
|
|
113
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
unlinkSync satisfies typeof fs.unlinkSync;
|
|
@@ -120,10 +120,17 @@ function _openSync(path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null
|
|
|
120
120
|
const mode = normalizeMode(_mode, 0o644),
|
|
121
121
|
flag = parseFlag(_flag);
|
|
122
122
|
|
|
123
|
-
path = resolveSymlinks
|
|
123
|
+
path = resolveSymlinks ? realpathSync(path) : path;
|
|
124
124
|
const { fs, path: resolved } = resolveMount(path);
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
let stats: Stats | undefined;
|
|
127
|
+
try {
|
|
128
|
+
stats = fs.statSync(resolved);
|
|
129
|
+
} catch {
|
|
130
|
+
// nothing
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!stats) {
|
|
127
134
|
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {
|
|
128
135
|
throw ErrnoError.With('ENOENT', path, '_open');
|
|
129
136
|
}
|
|
@@ -138,8 +145,6 @@ function _openSync(path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | null
|
|
|
138
145
|
return fs.createFileSync(resolved, flag, mode);
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
const stats: Stats = fs.statSync(resolved);
|
|
142
|
-
|
|
143
148
|
if (!stats.hasAccess(mode) || !stats.hasAccess(flagToMode(flag))) {
|
|
144
149
|
throw ErrnoError.With('EACCES', path, '_open');
|
|
145
150
|
}
|
|
@@ -378,7 +383,7 @@ futimesSync satisfies typeof fs.futimesSync;
|
|
|
378
383
|
|
|
379
384
|
export function rmdirSync(path: fs.PathLike): void {
|
|
380
385
|
path = normalizePath(path);
|
|
381
|
-
const { fs, path: resolved } = resolveMount(
|
|
386
|
+
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
382
387
|
try {
|
|
383
388
|
if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
|
|
384
389
|
throw ErrnoError.With('EACCES', resolved, 'rmdir');
|
|
@@ -386,7 +391,7 @@ export function rmdirSync(path: fs.PathLike): void {
|
|
|
386
391
|
fs.rmdirSync(resolved);
|
|
387
392
|
emitChange('rename', path.toString());
|
|
388
393
|
} catch (e) {
|
|
389
|
-
throw fixError(e as
|
|
394
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
390
395
|
}
|
|
391
396
|
}
|
|
392
397
|
rmdirSync satisfies typeof fs.rmdirSync;
|
|
@@ -401,8 +406,7 @@ export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirector
|
|
|
401
406
|
options = typeof options === 'object' ? options : { mode: options };
|
|
402
407
|
const mode = normalizeMode(options?.mode, 0o777);
|
|
403
408
|
|
|
404
|
-
path = normalizePath(path);
|
|
405
|
-
path = existsSync(path) ? realpathSync(path) : path;
|
|
409
|
+
path = realpathSync(normalizePath(path));
|
|
406
410
|
const { fs, path: resolved } = resolveMount(path);
|
|
407
411
|
const errorPaths: Record<string, string> = { [resolved]: path };
|
|
408
412
|
|
|
@@ -428,7 +432,7 @@ export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirector
|
|
|
428
432
|
}
|
|
429
433
|
return dirs[0];
|
|
430
434
|
} catch (e) {
|
|
431
|
-
throw fixError(e as
|
|
435
|
+
throw fixError(e as ErrnoError, errorPaths);
|
|
432
436
|
}
|
|
433
437
|
}
|
|
434
438
|
mkdirSync satisfies typeof fs.mkdirSync;
|
|
@@ -447,15 +451,15 @@ export function readdirSync(
|
|
|
447
451
|
): string[] | Dirent[] | Buffer[] {
|
|
448
452
|
options = typeof options === 'object' ? options : { encoding: options };
|
|
449
453
|
path = normalizePath(path);
|
|
450
|
-
const { fs, path: resolved } = resolveMount(
|
|
454
|
+
const { fs, path: resolved } = resolveMount(realpathSync(path));
|
|
451
455
|
let entries: string[];
|
|
452
|
-
if (!statSync(path).hasAccess(constants.R_OK)) {
|
|
453
|
-
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
454
|
-
}
|
|
455
456
|
try {
|
|
457
|
+
if (!fs.statSync(resolved).hasAccess(constants.R_OK)) {
|
|
458
|
+
throw ErrnoError.With('EACCES', path, 'readdir');
|
|
459
|
+
}
|
|
456
460
|
entries = fs.readdirSync(resolved);
|
|
457
461
|
} catch (e) {
|
|
458
|
-
throw fixError(e as
|
|
462
|
+
throw fixError(e as ErrnoError, { [resolved]: path });
|
|
459
463
|
}
|
|
460
464
|
|
|
461
465
|
for (const mount of mounts.keys()) {
|
|
@@ -473,8 +477,7 @@ export function readdirSync(
|
|
|
473
477
|
// Iterate over entries and handle recursive case if needed
|
|
474
478
|
const values: (string | Dirent | Buffer)[] = [];
|
|
475
479
|
for (const entry of entries) {
|
|
476
|
-
const
|
|
477
|
-
const entryStat = statSync(fullPath);
|
|
480
|
+
const entryStat = fs.statSync(join(resolved, entry));
|
|
478
481
|
|
|
479
482
|
if (options?.withFileTypes) {
|
|
480
483
|
values.push(new Dirent(entry, entryStat));
|
|
@@ -485,7 +488,7 @@ export function readdirSync(
|
|
|
485
488
|
}
|
|
486
489
|
if (!entryStat.isDirectory() || !options?.recursive) continue;
|
|
487
490
|
|
|
488
|
-
for (const subEntry of readdirSync(
|
|
491
|
+
for (const subEntry of readdirSync(join(path, entry), options)) {
|
|
489
492
|
if (subEntry instanceof Dirent) {
|
|
490
493
|
subEntry.path = join(entry, subEntry.path);
|
|
491
494
|
values.push(subEntry);
|
|
@@ -524,7 +527,7 @@ export function linkSync(targetPath: fs.PathLike, linkPath: fs.PathLike): void {
|
|
|
524
527
|
}
|
|
525
528
|
return fs.linkSync(path, linkPath);
|
|
526
529
|
} catch (e) {
|
|
527
|
-
throw fixError(e as
|
|
530
|
+
throw fixError(e as ErrnoError, { [path]: targetPath, [link.path]: linkPath });
|
|
528
531
|
}
|
|
529
532
|
}
|
|
530
533
|
linkSync satisfies typeof fs.linkSync;
|
|
@@ -628,14 +631,16 @@ export function realpathSync(path: fs.PathLike, options?: fs.EncodingOption | fs
|
|
|
628
631
|
|
|
629
632
|
return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
|
|
630
633
|
} catch (e) {
|
|
631
|
-
|
|
634
|
+
if ((e as ErrnoError).code == 'ENOENT') {
|
|
635
|
+
return path;
|
|
636
|
+
}
|
|
637
|
+
throw fixError(e as ErrnoError, { [resolvedPath]: lpath });
|
|
632
638
|
}
|
|
633
639
|
}
|
|
634
640
|
realpathSync satisfies Omit<typeof fs.realpathSync, 'native'>;
|
|
635
641
|
|
|
636
642
|
export function accessSync(path: fs.PathLike, mode: number = 0o600): void {
|
|
637
|
-
|
|
638
|
-
if (!stats.hasAccess(mode)) {
|
|
643
|
+
if (!statSync(path).hasAccess(mode)) {
|
|
639
644
|
throw new ErrnoError(Errno.EACCES);
|
|
640
645
|
}
|
|
641
646
|
}
|
|
@@ -651,8 +656,8 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions): void {
|
|
|
651
656
|
let stats: Stats | undefined;
|
|
652
657
|
try {
|
|
653
658
|
stats = statSync(path);
|
|
654
|
-
} catch (error
|
|
655
|
-
if (error.code != 'ENOENT' || !options?.force) throw error;
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if ((error as ErrnoError).code != 'ENOENT' || !options?.force) throw error;
|
|
656
661
|
}
|
|
657
662
|
|
|
658
663
|
if (!stats) {
|
|
@@ -763,6 +768,7 @@ writevSync satisfies typeof fs.writevSync;
|
|
|
763
768
|
* @param path The path to the directory.
|
|
764
769
|
* @param options Options for opening the directory.
|
|
765
770
|
* @returns A `Dir` object representing the opened directory.
|
|
771
|
+
* @todo Handle options
|
|
766
772
|
*/
|
|
767
773
|
export function opendirSync(path: fs.PathLike, options?: fs.OpenDirOptions): Dir {
|
|
768
774
|
path = normalizePath(path);
|
package/src/error.ts
CHANGED
|
@@ -276,7 +276,7 @@ export class ErrnoError extends Error implements NodeJS.ErrnoException {
|
|
|
276
276
|
) {
|
|
277
277
|
super(message);
|
|
278
278
|
this.code = Errno[errno] as keyof typeof Errno;
|
|
279
|
-
this.message =
|
|
279
|
+
this.message = this.code + ': ' + message + (this.path ? `, '${this.path}'` : '');
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
/**
|