@zenfs/core 0.9.7 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/Index.d.ts +3 -3
- package/dist/backends/Index.js +23 -23
- package/dist/backends/backend.js +6 -5
- package/dist/backends/fetch.d.ts +84 -0
- package/dist/backends/fetch.js +170 -0
- package/dist/backends/{Locked.d.ts → locked.d.ts} +13 -13
- package/dist/backends/{Locked.js → locked.js} +54 -54
- package/dist/backends/{InMemory.d.ts → memory.d.ts} +7 -9
- package/dist/backends/memory.js +38 -0
- package/dist/backends/{Overlay.d.ts → overlay.d.ts} +12 -13
- package/dist/backends/{Overlay.js → overlay.js} +105 -110
- package/dist/backends/port/fs.d.ts +123 -0
- package/dist/backends/port/fs.js +239 -0
- package/dist/backends/port/rpc.d.ts +60 -0
- package/dist/backends/port/rpc.js +71 -0
- package/dist/backends/store/fs.d.ts +169 -0
- package/dist/backends/store/fs.js +743 -0
- package/dist/backends/store/simple.d.ts +64 -0
- package/dist/backends/store/simple.js +111 -0
- package/dist/backends/store/store.d.ts +111 -0
- package/dist/backends/store/store.js +62 -0
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +8 -10
- package/dist/config.js +11 -11
- package/dist/emulation/async.js +6 -6
- package/dist/emulation/dir.js +2 -2
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/path.d.ts +3 -2
- package/dist/emulation/path.js +19 -45
- package/dist/emulation/promises.d.ts +7 -12
- package/dist/emulation/promises.js +144 -146
- package/dist/emulation/shared.d.ts +5 -10
- package/dist/emulation/shared.js +9 -9
- package/dist/emulation/streams.js +3 -3
- package/dist/emulation/sync.js +25 -25
- package/dist/{ApiError.d.ts → error.d.ts} +13 -15
- package/dist/error.js +291 -0
- package/dist/file.d.ts +2 -0
- package/dist/file.js +11 -5
- package/dist/filesystem.d.ts +3 -3
- package/dist/filesystem.js +41 -44
- package/dist/index.d.ts +7 -6
- package/dist/index.js +7 -6
- package/dist/inode.d.ts +1 -1
- package/dist/mutex.js +2 -1
- package/dist/utils.d.ts +8 -7
- package/dist/utils.js +11 -12
- package/package.json +3 -3
- package/readme.md +17 -9
- package/src/backends/Index.ts +23 -23
- package/src/backends/backend.ts +8 -7
- package/src/backends/fetch.ts +229 -0
- package/src/backends/{Locked.ts → locked.ts} +55 -55
- package/src/backends/memory.ts +44 -0
- package/src/backends/{Overlay.ts → overlay.ts} +108 -114
- package/src/backends/port/fs.ts +306 -0
- package/src/backends/port/readme.md +59 -0
- package/src/backends/port/rpc.ts +144 -0
- package/src/backends/store/fs.ts +881 -0
- package/src/backends/store/readme.md +9 -0
- package/src/backends/store/simple.ts +144 -0
- package/src/backends/store/store.ts +164 -0
- package/src/config.ts +21 -25
- package/src/emulation/async.ts +6 -6
- package/src/emulation/dir.ts +2 -2
- package/src/emulation/index.ts +1 -1
- package/src/emulation/path.ts +25 -49
- package/src/emulation/promises.ts +150 -159
- package/src/emulation/shared.ts +13 -15
- package/src/emulation/streams.ts +3 -3
- package/src/emulation/sync.ts +28 -28
- package/src/{ApiError.ts → error.ts} +89 -90
- package/src/file.ts +13 -5
- package/src/filesystem.ts +44 -47
- package/src/index.ts +7 -6
- package/src/inode.ts +1 -1
- package/src/mutex.ts +3 -1
- package/src/utils.ts +16 -18
- package/tsconfig.json +2 -2
- package/dist/ApiError.js +0 -292
- package/dist/backends/AsyncStore.d.ts +0 -204
- package/dist/backends/AsyncStore.js +0 -509
- package/dist/backends/InMemory.js +0 -49
- package/dist/backends/SyncStore.d.ts +0 -213
- package/dist/backends/SyncStore.js +0 -445
- package/src/backends/AsyncStore.ts +0 -655
- package/src/backends/InMemory.ts +0 -56
- package/src/backends/SyncStore.ts +0 -589
|
@@ -6,9 +6,9 @@ import type { Stream } from 'node:stream';
|
|
|
6
6
|
import type { ReadableStream as TReadableStream } from 'node:stream/web';
|
|
7
7
|
import type { Interface as ReadlineInterface } from 'readline';
|
|
8
8
|
import type { ReadableStreamController } from 'stream/web';
|
|
9
|
-
import {
|
|
9
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
10
10
|
import { ActionType, File, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
|
|
11
|
-
import { FileContents
|
|
11
|
+
import type { FileContents } from '../filesystem.js';
|
|
12
12
|
import { BigIntStats, FileType, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
|
|
13
13
|
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
14
14
|
import * as constants from './constants.js';
|
|
@@ -19,18 +19,21 @@ import { ReadStream, WriteStream } from './streams.js';
|
|
|
19
19
|
export * as constants from './constants.js';
|
|
20
20
|
|
|
21
21
|
export class FileHandle implements promises.FileHandle {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
public readonly fd: number
|
|
27
|
-
) {}
|
|
22
|
+
/**
|
|
23
|
+
* The file descriptor for this file handle.
|
|
24
|
+
*/
|
|
25
|
+
public readonly fd: number;
|
|
28
26
|
|
|
29
27
|
/**
|
|
30
28
|
* @internal
|
|
29
|
+
* The file for this file handle
|
|
31
30
|
*/
|
|
32
|
-
public
|
|
33
|
-
|
|
31
|
+
public readonly file: File;
|
|
32
|
+
|
|
33
|
+
public constructor(fdOrFile: number | File) {
|
|
34
|
+
const isFile = typeof fdOrFile != 'number';
|
|
35
|
+
this.fd = isFile ? file2fd(fdOrFile) : fdOrFile;
|
|
36
|
+
this.file = isFile ? fdOrFile : fd2file(fdOrFile);
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
@@ -47,7 +50,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
47
50
|
public chmod(mode: fs.Mode): Promise<void> {
|
|
48
51
|
const numMode = normalizeMode(mode, -1);
|
|
49
52
|
if (numMode < 0) {
|
|
50
|
-
throw new
|
|
53
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid mode.');
|
|
51
54
|
}
|
|
52
55
|
return this.file.chmod(numMode);
|
|
53
56
|
}
|
|
@@ -73,7 +76,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
73
76
|
public truncate(len?: number | null): Promise<void> {
|
|
74
77
|
len ||= 0;
|
|
75
78
|
if (len < 0) {
|
|
76
|
-
throw new
|
|
79
|
+
throw new ErrnoError(Errno.EINVAL);
|
|
77
80
|
}
|
|
78
81
|
return this.file.truncate(len);
|
|
79
82
|
}
|
|
@@ -101,10 +104,10 @@ export class FileHandle implements promises.FileHandle {
|
|
|
101
104
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
102
105
|
const flag = parseFlag(options.flag);
|
|
103
106
|
if (!isAppendable(flag)) {
|
|
104
|
-
throw new
|
|
107
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
105
108
|
}
|
|
106
109
|
if (typeof data != 'string' && !options.encoding) {
|
|
107
|
-
throw new
|
|
110
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
108
111
|
}
|
|
109
112
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : data;
|
|
110
113
|
await this.file.write(encodedData, 0, encodedData.length, null);
|
|
@@ -133,11 +136,11 @@ export class FileHandle implements promises.FileHandle {
|
|
|
133
136
|
*/
|
|
134
137
|
public async readFile(_options?: { flag?: fs.OpenMode }): Promise<Buffer>;
|
|
135
138
|
public async readFile(_options: (fs.ObjectEncodingOptions & FlagAndOpenMode) | BufferEncoding): Promise<string>;
|
|
136
|
-
public async readFile(_options
|
|
139
|
+
public async readFile(_options?: (fs.ObjectEncodingOptions & FlagAndOpenMode) | BufferEncoding): Promise<string | Buffer> {
|
|
137
140
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
138
141
|
const flag = parseFlag(options.flag);
|
|
139
142
|
if (!isReadable(flag)) {
|
|
140
|
-
throw new
|
|
143
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading.');
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
const { size } = await this.stat();
|
|
@@ -177,7 +180,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
177
180
|
enqueue(result.buffer.slice(0, result.bytesRead));
|
|
178
181
|
position += result.bytesRead;
|
|
179
182
|
if (++i >= maxChunks) {
|
|
180
|
-
throw new
|
|
183
|
+
throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
|
|
181
184
|
}
|
|
182
185
|
bytesRead = result.bytesRead;
|
|
183
186
|
}
|
|
@@ -190,7 +193,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
public readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface {
|
|
193
|
-
throw
|
|
196
|
+
throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
|
|
194
197
|
}
|
|
195
198
|
|
|
196
199
|
public [Symbol.asyncDispose](): Promise<void> {
|
|
@@ -262,10 +265,10 @@ export class FileHandle implements promises.FileHandle {
|
|
|
262
265
|
const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
|
|
263
266
|
const flag = parseFlag(options.flag);
|
|
264
267
|
if (!isWriteable(flag)) {
|
|
265
|
-
throw new
|
|
268
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing.');
|
|
266
269
|
}
|
|
267
270
|
if (typeof data != 'string' && !options.encoding) {
|
|
268
|
-
throw new
|
|
271
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
269
272
|
}
|
|
270
273
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : data;
|
|
271
274
|
await this.file.write(encodedData, 0, encodedData.length, 0);
|
|
@@ -328,7 +331,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
328
331
|
stream.push(!result.bytesRead ? null : result.buffer.slice(0, result.bytesRead)); // Push data or null for EOF
|
|
329
332
|
this.file.position += result.bytesRead;
|
|
330
333
|
} catch (error) {
|
|
331
|
-
stream.destroy(
|
|
334
|
+
stream.destroy(error as Error);
|
|
332
335
|
}
|
|
333
336
|
},
|
|
334
337
|
});
|
|
@@ -353,7 +356,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
353
356
|
const { bytesWritten } = await this.write(chunk, null, encoding);
|
|
354
357
|
callback(bytesWritten == chunk.length ? null : new Error('Failed to write full chunk'));
|
|
355
358
|
} catch (error) {
|
|
356
|
-
callback(
|
|
359
|
+
callback(error as Error);
|
|
357
360
|
}
|
|
358
361
|
},
|
|
359
362
|
};
|
|
@@ -364,38 +367,6 @@ export class FileHandle implements promises.FileHandle {
|
|
|
364
367
|
}
|
|
365
368
|
}
|
|
366
369
|
|
|
367
|
-
type FileSystemMethod = {
|
|
368
|
-
[K in keyof FileSystem]: FileSystem[K] extends (...args: any[]) => unknown
|
|
369
|
-
? (name: K, resolveSymlinks: boolean, ...args: Parameters<FileSystem[K]>) => ReturnType<FileSystem[K]>
|
|
370
|
-
: never;
|
|
371
|
-
}[keyof FileSystem]; // https://stackoverflow.com/a/76335220/17637456
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Utility for FS ops. It handles
|
|
375
|
-
* - path normalization (for the first parameter to the FS op)
|
|
376
|
-
* - path translation for errors
|
|
377
|
-
* - FS/mount point resolution
|
|
378
|
-
*
|
|
379
|
-
* It can't be used for functions which may operate on multiple mounted FSs or paths (e.g. `rename`)
|
|
380
|
-
* @param name the function name
|
|
381
|
-
* @param resolveSymlinks whether to resolve symlinks
|
|
382
|
-
* @param args the rest of the parameters are passed to the FS function. Note that the first parameter is required to be a path
|
|
383
|
-
* @returns
|
|
384
|
-
*/
|
|
385
|
-
async function doOp<M extends FileSystemMethod, RT extends ReturnType<M> = ReturnType<M>>(...[name, resolveSymlinks, rawPath, ...args]: Parameters<M>): Promise<RT> {
|
|
386
|
-
rawPath = normalizePath(rawPath!);
|
|
387
|
-
const _path = resolveSymlinks && (await exists(rawPath)) ? await realpath(rawPath) : rawPath;
|
|
388
|
-
const { fs, path } = resolveMount(_path);
|
|
389
|
-
try {
|
|
390
|
-
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
391
|
-
return fs[name](path, ...args) as Promise<RT>;
|
|
392
|
-
} catch (e) {
|
|
393
|
-
throw fixError(<Error>e, { [path]: rawPath });
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// fs.promises
|
|
398
|
-
|
|
399
370
|
/**
|
|
400
371
|
* Renames a file
|
|
401
372
|
* @param oldPath
|
|
@@ -414,21 +385,21 @@ export async function rename(oldPath: fs.PathLike, newPath: fs.PathLike): Promis
|
|
|
414
385
|
await writeFile(newPath, await readFile(oldPath));
|
|
415
386
|
await unlink(oldPath);
|
|
416
387
|
} catch (e) {
|
|
417
|
-
throw fixError(
|
|
388
|
+
throw fixError(e as Error, { [src.path]: oldPath, [dst.path]: newPath });
|
|
418
389
|
}
|
|
419
390
|
}
|
|
420
391
|
rename satisfies typeof promises.rename;
|
|
421
392
|
|
|
422
393
|
/**
|
|
423
394
|
* Test whether or not the given path exists by checking with the file system.
|
|
424
|
-
* @param
|
|
395
|
+
* @param path
|
|
425
396
|
*/
|
|
426
|
-
export async function exists(
|
|
397
|
+
export async function exists(path: fs.PathLike): Promise<boolean> {
|
|
427
398
|
try {
|
|
428
|
-
const { fs, path } = resolveMount(await realpath(
|
|
429
|
-
return await fs.exists(
|
|
399
|
+
const { fs, path: resolved } = resolveMount(await realpath(path));
|
|
400
|
+
return await fs.exists(resolved, cred);
|
|
430
401
|
} catch (e) {
|
|
431
|
-
if (
|
|
402
|
+
if (e instanceof ErrnoError && e.code == 'ENOENT') {
|
|
432
403
|
return false;
|
|
433
404
|
}
|
|
434
405
|
|
|
@@ -445,8 +416,14 @@ export async function stat(path: fs.PathLike, options: fs.BigIntOptions): Promis
|
|
|
445
416
|
export async function stat(path: fs.PathLike, options?: { bigint?: false }): Promise<Stats>;
|
|
446
417
|
export async function stat(path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats>;
|
|
447
418
|
export async function stat(path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats> {
|
|
448
|
-
|
|
449
|
-
|
|
419
|
+
path = normalizePath(path);
|
|
420
|
+
const { fs, path: resolved } = resolveMount((await exists(path)) ? await realpath(path) : path);
|
|
421
|
+
try {
|
|
422
|
+
const stats = await fs.stat(resolved, cred);
|
|
423
|
+
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
424
|
+
} catch (e) {
|
|
425
|
+
throw fixError(e as Error, { [resolved]: path });
|
|
426
|
+
}
|
|
450
427
|
}
|
|
451
428
|
stat satisfies typeof promises.stat;
|
|
452
429
|
|
|
@@ -460,8 +437,14 @@ stat satisfies typeof promises.stat;
|
|
|
460
437
|
export async function lstat(path: fs.PathLike, options?: { bigint?: boolean }): Promise<Stats>;
|
|
461
438
|
export async function lstat(path: fs.PathLike, options: { bigint: true }): Promise<BigIntStats>;
|
|
462
439
|
export async function lstat(path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats> {
|
|
463
|
-
|
|
464
|
-
|
|
440
|
+
path = normalizePath(path);
|
|
441
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
442
|
+
try {
|
|
443
|
+
const stats = await fs.stat(resolved, cred);
|
|
444
|
+
return options?.bigint ? new BigIntStats(stats) : stats;
|
|
445
|
+
} catch (e) {
|
|
446
|
+
throw fixError(e as Error, { [resolved]: path });
|
|
447
|
+
}
|
|
465
448
|
}
|
|
466
449
|
lstat satisfies typeof promises.lstat;
|
|
467
450
|
|
|
@@ -487,7 +470,13 @@ truncate satisfies typeof promises.truncate;
|
|
|
487
470
|
* @param path
|
|
488
471
|
*/
|
|
489
472
|
export async function unlink(path: fs.PathLike): Promise<void> {
|
|
490
|
-
|
|
473
|
+
path = normalizePath(path);
|
|
474
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
475
|
+
try {
|
|
476
|
+
await fs.unlink(resolved, cred);
|
|
477
|
+
} catch (e) {
|
|
478
|
+
throw fixError(e as Error, { [resolved]: path });
|
|
479
|
+
}
|
|
491
480
|
}
|
|
492
481
|
unlink satisfies typeof promises.unlink;
|
|
493
482
|
|
|
@@ -495,15 +484,18 @@ unlink satisfies typeof promises.unlink;
|
|
|
495
484
|
* Opens a file. This helper handles the complexity of file flags.
|
|
496
485
|
* @internal
|
|
497
486
|
*/
|
|
498
|
-
async function _open(
|
|
499
|
-
|
|
500
|
-
|
|
487
|
+
async function _open(path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o644, resolveSymlinks: boolean): Promise<FileHandle> {
|
|
488
|
+
path = normalizePath(path);
|
|
489
|
+
const mode = normalizeMode(_mode, 0o644),
|
|
501
490
|
flag = parseFlag(_flag);
|
|
502
491
|
|
|
492
|
+
path = resolveSymlinks && (await exists(path)) ? await realpath(path) : path;
|
|
493
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
494
|
+
|
|
503
495
|
try {
|
|
504
496
|
switch (pathExistsAction(flag)) {
|
|
505
497
|
case ActionType.THROW:
|
|
506
|
-
throw
|
|
498
|
+
throw ErrnoError.With('EEXIST', path, '_open');
|
|
507
499
|
case ActionType.TRUNCATE:
|
|
508
500
|
/*
|
|
509
501
|
In a previous implementation, we deleted the file and
|
|
@@ -511,32 +503,29 @@ async function _open(_path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o
|
|
|
511
503
|
asynchronous request was trying to read the file, as the file
|
|
512
504
|
would not exist for a small period of time.
|
|
513
505
|
*/
|
|
514
|
-
const file: File = await
|
|
515
|
-
if (!file) {
|
|
516
|
-
throw new ApiError(ErrorCode.EIO, 'Impossible code path reached');
|
|
517
|
-
}
|
|
506
|
+
const file: File = await fs.openFile(resolved, flag, cred);
|
|
518
507
|
await file.truncate(0);
|
|
519
508
|
await file.sync();
|
|
520
|
-
return file;
|
|
509
|
+
return new FileHandle(file);
|
|
521
510
|
case ActionType.NOP:
|
|
522
511
|
// Must await so thrown errors are caught by the catch below
|
|
523
|
-
return await
|
|
512
|
+
return new FileHandle(await fs.openFile(resolved, flag, cred));
|
|
524
513
|
default:
|
|
525
|
-
throw new
|
|
514
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
526
515
|
}
|
|
527
516
|
} catch (e) {
|
|
528
517
|
switch (pathNotExistsAction(flag)) {
|
|
529
518
|
case ActionType.CREATE:
|
|
530
519
|
// Ensure parent exists.
|
|
531
|
-
const parentStats: Stats = await
|
|
520
|
+
const parentStats: Stats = await fs.stat(dirname(resolved), cred);
|
|
532
521
|
if (parentStats && !parentStats.isDirectory()) {
|
|
533
|
-
throw
|
|
522
|
+
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
|
|
534
523
|
}
|
|
535
|
-
return await
|
|
524
|
+
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
|
|
536
525
|
case ActionType.THROW:
|
|
537
|
-
throw
|
|
526
|
+
throw ErrnoError.With('ENOENT', path, '_open');
|
|
538
527
|
default:
|
|
539
|
-
throw new
|
|
528
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
|
|
540
529
|
}
|
|
541
530
|
}
|
|
542
531
|
}
|
|
@@ -548,29 +537,10 @@ async function _open(_path: fs.PathLike, _flag: fs.OpenMode, _mode: fs.Mode = 0o
|
|
|
548
537
|
* @param mode Mode to use to open the file. Can be ignored if the filesystem doesn't support permissions.
|
|
549
538
|
*/
|
|
550
539
|
export async function open(path: fs.PathLike, flag: fs.OpenMode = 'r', mode: fs.Mode = 0o644): Promise<FileHandle> {
|
|
551
|
-
|
|
552
|
-
return new FileHandle(file2fd(file));
|
|
540
|
+
return await _open(path, flag, mode, true);
|
|
553
541
|
}
|
|
554
542
|
open satisfies typeof promises.open;
|
|
555
543
|
|
|
556
|
-
/**
|
|
557
|
-
* Asynchronously reads the entire contents of a file.
|
|
558
|
-
*/
|
|
559
|
-
async function _readFile(fname: string, flag: string, resolveSymlinks: boolean): Promise<Uint8Array> {
|
|
560
|
-
const file = await _open(normalizePath(fname), flag, 0o644, resolveSymlinks);
|
|
561
|
-
|
|
562
|
-
try {
|
|
563
|
-
const stat = await file.stat();
|
|
564
|
-
const data = new Uint8Array(stat.size);
|
|
565
|
-
await file.read(data, 0, stat.size, 0);
|
|
566
|
-
await file.close();
|
|
567
|
-
return data;
|
|
568
|
-
} catch (e) {
|
|
569
|
-
await file.close();
|
|
570
|
-
throw e;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
544
|
/**
|
|
575
545
|
* Asynchronously reads the entire contents of a file.
|
|
576
546
|
* @param filename
|
|
@@ -589,14 +559,14 @@ export async function readFile(
|
|
|
589
559
|
path: fs.PathLike | promises.FileHandle,
|
|
590
560
|
_options?: (fs.ObjectEncodingOptions & { flag?: fs.OpenMode }) | BufferEncoding | null
|
|
591
561
|
): Promise<Buffer | string> {
|
|
592
|
-
const options = normalizeOptions(_options, null, 'r',
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
562
|
+
const options = normalizeOptions(_options, null, 'r', 0o644);
|
|
563
|
+
const handle: FileHandle | promises.FileHandle = typeof path == 'object' && 'fd' in path ? path : await open(path as string, options.flag, options.mode);
|
|
564
|
+
|
|
565
|
+
try {
|
|
566
|
+
return await handle.readFile(options);
|
|
567
|
+
} finally {
|
|
568
|
+
await handle.close();
|
|
596
569
|
}
|
|
597
|
-
path = path instanceof FileHandle ? path.file.path : path.toString();
|
|
598
|
-
const data: Buffer = Buffer.from(await _readFile(path, options.flag, true));
|
|
599
|
-
return options.encoding ? data.toString(options.encoding) : data;
|
|
600
570
|
}
|
|
601
571
|
readFile satisfies typeof promises.readFile;
|
|
602
572
|
|
|
@@ -621,7 +591,7 @@ export async function writeFile(
|
|
|
621
591
|
try {
|
|
622
592
|
const _data = typeof data == 'string' ? data : data;
|
|
623
593
|
if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
|
|
624
|
-
throw new
|
|
594
|
+
throw new ErrnoError(Errno.EINVAL, 'Iterables and streams not supported', handle.file.path, 'writeFile');
|
|
625
595
|
}
|
|
626
596
|
await handle.writeFile(_data, options);
|
|
627
597
|
} finally {
|
|
@@ -630,19 +600,6 @@ export async function writeFile(
|
|
|
630
600
|
}
|
|
631
601
|
writeFile satisfies typeof promises.writeFile;
|
|
632
602
|
|
|
633
|
-
/**
|
|
634
|
-
* Asynchronously append data to a file, creating the file if
|
|
635
|
-
* it not yet exists.
|
|
636
|
-
*/
|
|
637
|
-
async function _appendFile(path: fs.PathLike, data: Uint8Array, flag: string, mode: number, resolveSymlinks: boolean): Promise<void> {
|
|
638
|
-
const file = await _open(path, flag, mode, resolveSymlinks);
|
|
639
|
-
try {
|
|
640
|
-
await file.write(data, 0, data.length, null);
|
|
641
|
-
} finally {
|
|
642
|
-
await file.close();
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
603
|
/**
|
|
647
604
|
* Asynchronously append data to a file, creating the file if it not yet
|
|
648
605
|
* exists.
|
|
@@ -661,13 +618,19 @@ export async function appendFile(
|
|
|
661
618
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
662
619
|
const flag = parseFlag(options.flag);
|
|
663
620
|
if (!isAppendable(flag)) {
|
|
664
|
-
throw new
|
|
621
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
665
622
|
}
|
|
666
623
|
if (typeof data != 'string' && !options.encoding) {
|
|
667
|
-
throw new
|
|
624
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
668
625
|
}
|
|
669
626
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
670
|
-
|
|
627
|
+
const handle: FileHandle | promises.FileHandle = typeof path == 'object' && 'fd' in path ? path : await open(path as string, options.flag, options.mode);
|
|
628
|
+
|
|
629
|
+
try {
|
|
630
|
+
await handle.appendFile(encodedData, options);
|
|
631
|
+
} finally {
|
|
632
|
+
await handle.close();
|
|
633
|
+
}
|
|
671
634
|
}
|
|
672
635
|
appendFile satisfies typeof promises.appendFile;
|
|
673
636
|
|
|
@@ -678,7 +641,14 @@ appendFile satisfies typeof promises.appendFile;
|
|
|
678
641
|
* @param path
|
|
679
642
|
*/
|
|
680
643
|
export async function rmdir(path: fs.PathLike): Promise<void> {
|
|
681
|
-
|
|
644
|
+
path = normalizePath(path);
|
|
645
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
646
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
647
|
+
try {
|
|
648
|
+
await fs.rmdir(resolved, cred);
|
|
649
|
+
} catch (e) {
|
|
650
|
+
throw fixError(e as Error, { [resolved]: path });
|
|
651
|
+
}
|
|
682
652
|
}
|
|
683
653
|
rmdir satisfies typeof promises.rmdir;
|
|
684
654
|
|
|
@@ -692,8 +662,14 @@ export async function mkdir(path: fs.PathLike, options: fs.MakeDirectoryOptions
|
|
|
692
662
|
export async function mkdir(path: fs.PathLike, options?: fs.Mode | (fs.MakeDirectoryOptions & { recursive?: false | undefined }) | null): Promise<void>;
|
|
693
663
|
export async function mkdir(path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions | null): Promise<string | undefined>;
|
|
694
664
|
export async function mkdir(path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions | null): Promise<string | undefined | void> {
|
|
695
|
-
|
|
696
|
-
|
|
665
|
+
path = normalizePath(path);
|
|
666
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
667
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
668
|
+
try {
|
|
669
|
+
await fs.mkdir(resolved, normalizeMode(typeof options == 'object' ? options?.mode : options, 0o777), cred);
|
|
670
|
+
} catch (e) {
|
|
671
|
+
throw fixError(e as Error, { [resolved]: path });
|
|
672
|
+
}
|
|
697
673
|
}
|
|
698
674
|
mkdir satisfies typeof promises.mkdir;
|
|
699
675
|
|
|
@@ -714,9 +690,15 @@ export async function readdir(
|
|
|
714
690
|
options?: { withFileTypes?: boolean; recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null } | BufferEncoding | 'buffer' | null
|
|
715
691
|
): Promise<string[] | Dirent[] | Buffer[]> {
|
|
716
692
|
path = normalizePath(path);
|
|
717
|
-
|
|
718
|
-
const
|
|
719
|
-
|
|
693
|
+
path = (await exists(path)) ? await realpath(path) : path;
|
|
694
|
+
const { fs, path: resolved } = resolveMount(path);
|
|
695
|
+
let entries: string[];
|
|
696
|
+
try {
|
|
697
|
+
entries = await fs.readdir(resolved, cred);
|
|
698
|
+
} catch (e) {
|
|
699
|
+
throw fixError(e as Error, { [resolved]: path });
|
|
700
|
+
}
|
|
701
|
+
for (const point of mounts.keys()) {
|
|
720
702
|
if (point.startsWith(path)) {
|
|
721
703
|
const entry = point.slice(path.length);
|
|
722
704
|
if (entry.includes('/') || entry.length == 0) {
|
|
@@ -742,8 +724,14 @@ readdir satisfies typeof promises.readdir;
|
|
|
742
724
|
* @param newpath
|
|
743
725
|
*/
|
|
744
726
|
export async function link(existing: fs.PathLike, newpath: fs.PathLike): Promise<void> {
|
|
727
|
+
existing = normalizePath(existing);
|
|
745
728
|
newpath = normalizePath(newpath);
|
|
746
|
-
|
|
729
|
+
const { fs, path: resolved } = resolveMount(newpath);
|
|
730
|
+
try {
|
|
731
|
+
return await fs.link(existing, newpath, cred);
|
|
732
|
+
} catch (e) {
|
|
733
|
+
throw fixError(e as Error, { [resolved]: newpath });
|
|
734
|
+
}
|
|
747
735
|
}
|
|
748
736
|
link satisfies typeof promises.link;
|
|
749
737
|
|
|
@@ -755,16 +743,16 @@ link satisfies typeof promises.link;
|
|
|
755
743
|
*/
|
|
756
744
|
export async function symlink(target: fs.PathLike, path: fs.PathLike, type: fs.symlink.Type | string | null = 'file'): Promise<void> {
|
|
757
745
|
if (!['file', 'dir', 'junction'].includes(type!)) {
|
|
758
|
-
throw new
|
|
746
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid symlink type: ' + type);
|
|
759
747
|
}
|
|
760
748
|
|
|
761
749
|
if (await exists(path)) {
|
|
762
|
-
throw
|
|
750
|
+
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
763
751
|
}
|
|
764
752
|
|
|
765
753
|
await writeFile(path, target.toString());
|
|
766
|
-
const
|
|
767
|
-
await file._setType(FileType.SYMLINK);
|
|
754
|
+
const handle = await _open(path, 'r+', 0o644, false);
|
|
755
|
+
await handle.file._setType(FileType.SYMLINK);
|
|
768
756
|
}
|
|
769
757
|
symlink satisfies typeof promises.symlink;
|
|
770
758
|
|
|
@@ -776,9 +764,14 @@ export async function readlink(path: fs.PathLike, options: fs.BufferEncodingOpti
|
|
|
776
764
|
export async function readlink(path: fs.PathLike, options?: fs.EncodingOption | null): Promise<string>;
|
|
777
765
|
export async function readlink(path: fs.PathLike, options?: fs.BufferEncodingOption | fs.EncodingOption | string | null): Promise<string | Buffer>;
|
|
778
766
|
export async function readlink(path: fs.PathLike, options?: fs.BufferEncodingOption | fs.EncodingOption | string | null): Promise<string | Buffer> {
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
767
|
+
const handle = await _open(normalizePath(path), 'r', 0o644, false);
|
|
768
|
+
try {
|
|
769
|
+
const value = await handle.readFile();
|
|
770
|
+
const encoding = typeof options == 'object' ? options?.encoding : options;
|
|
771
|
+
return encoding == 'buffer' ? value : value.toString(encoding! as BufferEncoding);
|
|
772
|
+
} finally {
|
|
773
|
+
await handle.close();
|
|
774
|
+
}
|
|
782
775
|
}
|
|
783
776
|
readlink satisfies typeof promises.readlink;
|
|
784
777
|
|
|
@@ -807,11 +800,11 @@ chown satisfies typeof promises.chown;
|
|
|
807
800
|
* @param gid
|
|
808
801
|
*/
|
|
809
802
|
export async function lchown(path: fs.PathLike, uid: number, gid: number): Promise<void> {
|
|
810
|
-
const
|
|
803
|
+
const handle: FileHandle = await _open(path, 'r+', 0o644, false);
|
|
811
804
|
try {
|
|
812
|
-
await
|
|
805
|
+
await handle.chown(uid, gid);
|
|
813
806
|
} finally {
|
|
814
|
-
await
|
|
807
|
+
await handle.close();
|
|
815
808
|
}
|
|
816
809
|
}
|
|
817
810
|
lchown satisfies typeof promises.lchown;
|
|
@@ -837,11 +830,11 @@ chmod satisfies typeof promises.chmod;
|
|
|
837
830
|
* @param mode
|
|
838
831
|
*/
|
|
839
832
|
export async function lchmod(path: fs.PathLike, mode: fs.Mode): Promise<void> {
|
|
840
|
-
const
|
|
833
|
+
const handle: FileHandle = await _open(path, 'r+', 0o644, false);
|
|
841
834
|
try {
|
|
842
|
-
await
|
|
835
|
+
await handle.chmod(mode);
|
|
843
836
|
} finally {
|
|
844
|
-
await
|
|
837
|
+
await handle.close();
|
|
845
838
|
}
|
|
846
839
|
}
|
|
847
840
|
lchmod satisfies typeof promises.lchmod;
|
|
@@ -869,11 +862,11 @@ utimes satisfies typeof promises.utimes;
|
|
|
869
862
|
* @param mtime
|
|
870
863
|
*/
|
|
871
864
|
export async function lutimes(path: fs.PathLike, atime: fs.TimeLike, mtime: fs.TimeLike): Promise<void> {
|
|
872
|
-
const
|
|
865
|
+
const handle: FileHandle = await _open(path, 'r+', 0o644, false);
|
|
873
866
|
try {
|
|
874
|
-
await
|
|
867
|
+
await handle.utimes(new Date(atime), new Date(mtime));
|
|
875
868
|
} finally {
|
|
876
|
-
await
|
|
869
|
+
await handle.close();
|
|
877
870
|
}
|
|
878
871
|
}
|
|
879
872
|
lutimes satisfies typeof promises.lutimes;
|
|
@@ -882,8 +875,6 @@ lutimes satisfies typeof promises.lutimes;
|
|
|
882
875
|
* Asynchronous realpath(3) - return the canonicalized absolute pathname.
|
|
883
876
|
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
|
|
884
877
|
* @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used.
|
|
885
|
-
*
|
|
886
|
-
* Note: This *Can not* use doOp since doOp depends on it
|
|
887
878
|
*/
|
|
888
879
|
export async function realpath(path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
|
|
889
880
|
export async function realpath(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
|
|
@@ -901,7 +892,7 @@ export async function realpath(path: fs.PathLike, options?: fs.EncodingOption |
|
|
|
901
892
|
|
|
902
893
|
return realpath(mountPoint + (await readlink(lpath)));
|
|
903
894
|
} catch (e) {
|
|
904
|
-
throw fixError(
|
|
895
|
+
throw fixError(e as Error, { [resolvedPath]: lpath });
|
|
905
896
|
}
|
|
906
897
|
}
|
|
907
898
|
realpath satisfies typeof promises.realpath;
|
|
@@ -913,7 +904,7 @@ export function watch(filename: fs.PathLike, options?: fs.WatchOptions | BufferE
|
|
|
913
904
|
export function watch(filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<FileChangeInfo<Buffer>>;
|
|
914
905
|
export function watch(filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterable<FileChangeInfo<string>> | AsyncIterable<FileChangeInfo<Buffer>>;
|
|
915
906
|
export function watch(filename: fs.PathLike, options: fs.WatchOptions | string = {}): AsyncIterable<FileChangeInfo<string>> | AsyncIterable<FileChangeInfo<Buffer>> {
|
|
916
|
-
throw
|
|
907
|
+
throw ErrnoError.With('ENOSYS', filename.toString(), 'watch');
|
|
917
908
|
}
|
|
918
909
|
watch satisfies typeof promises.watch;
|
|
919
910
|
|
|
@@ -925,7 +916,7 @@ watch satisfies typeof promises.watch;
|
|
|
925
916
|
export async function access(path: fs.PathLike, mode: number = constants.F_OK): Promise<void> {
|
|
926
917
|
const stats = await stat(path);
|
|
927
918
|
if (!stats.hasAccess(mode, cred)) {
|
|
928
|
-
throw new
|
|
919
|
+
throw new ErrnoError(Errno.EACCES);
|
|
929
920
|
}
|
|
930
921
|
}
|
|
931
922
|
access satisfies typeof promises.access;
|
|
@@ -958,7 +949,7 @@ export async function rm(path: fs.PathLike, options?: fs.RmOptions) {
|
|
|
958
949
|
case constants.S_IFIFO:
|
|
959
950
|
case constants.S_IFSOCK:
|
|
960
951
|
default:
|
|
961
|
-
throw new
|
|
952
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
962
953
|
}
|
|
963
954
|
}
|
|
964
955
|
rm satisfies typeof promises.rm;
|
|
@@ -994,7 +985,7 @@ export async function copyFile(src: fs.PathLike, dest: fs.PathLike, mode?: numbe
|
|
|
994
985
|
dest = normalizePath(dest);
|
|
995
986
|
|
|
996
987
|
if (mode && mode & constants.COPYFILE_EXCL && (await exists(dest))) {
|
|
997
|
-
throw new
|
|
988
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');
|
|
998
989
|
}
|
|
999
990
|
|
|
1000
991
|
await writeFile(dest, await readFile(src));
|
|
@@ -1032,13 +1023,13 @@ export async function cp(source: fs.PathLike, destination: fs.PathLike, opts?: f
|
|
|
1032
1023
|
const srcStats = await lstat(source); // Use lstat to follow symlinks if not dereferencing
|
|
1033
1024
|
|
|
1034
1025
|
if (opts?.errorOnExist && (await exists(destination))) {
|
|
1035
|
-
throw new
|
|
1026
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists.', destination, 'cp');
|
|
1036
1027
|
}
|
|
1037
1028
|
|
|
1038
1029
|
switch (srcStats.mode & constants.S_IFMT) {
|
|
1039
1030
|
case constants.S_IFDIR:
|
|
1040
1031
|
if (!opts?.recursive) {
|
|
1041
|
-
throw new
|
|
1032
|
+
throw new ErrnoError(Errno.EISDIR, source + ' is a directory (not copied)', source, 'cp');
|
|
1042
1033
|
}
|
|
1043
1034
|
await mkdir(destination, { recursive: true }); // Ensure the destination directory exists
|
|
1044
1035
|
for (const dirent of await readdir(source, { withFileTypes: true })) {
|
|
@@ -1057,7 +1048,7 @@ export async function cp(source: fs.PathLike, destination: fs.PathLike, opts?: f
|
|
|
1057
1048
|
case constants.S_IFIFO:
|
|
1058
1049
|
case constants.S_IFSOCK:
|
|
1059
1050
|
default:
|
|
1060
|
-
throw new
|
|
1051
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', source, 'rm');
|
|
1061
1052
|
}
|
|
1062
1053
|
|
|
1063
1054
|
// Optionally preserve timestamps
|
|
@@ -1075,5 +1066,5 @@ export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions & { bigi
|
|
|
1075
1066
|
export async function statfs(path: fs.PathLike, opts: fs.StatFsOptions & { bigint: true }): Promise<BigIntStatsFs>;
|
|
1076
1067
|
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<StatsFs | BigIntStatsFs>;
|
|
1077
1068
|
export async function statfs(path: fs.PathLike, opts?: fs.StatFsOptions): Promise<StatsFs | BigIntStatsFs> {
|
|
1078
|
-
throw
|
|
1069
|
+
throw ErrnoError.With('ENOSYS', path.toString(), 'statfs');
|
|
1079
1070
|
}
|