@zenfs/core 0.9.7 → 0.10.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/AsyncStore.js +29 -29
- package/dist/backends/Fetch.d.ts +84 -0
- package/dist/backends/Fetch.js +171 -0
- package/dist/backends/Index.js +19 -19
- package/dist/backends/Locked.d.ts +11 -11
- package/dist/backends/Locked.js +50 -49
- package/dist/backends/Overlay.js +21 -21
- package/dist/backends/SyncStore.js +27 -27
- package/dist/backends/backend.js +4 -4
- package/dist/backends/port/fs.d.ts +124 -0
- package/dist/backends/port/fs.js +241 -0
- package/dist/backends/port/rpc.d.ts +60 -0
- package/dist/backends/port/rpc.js +71 -0
- package/dist/backends/port/store.d.ts +30 -0
- package/dist/backends/port/store.js +142 -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 +8 -8
- package/dist/emulation/streams.js +3 -3
- package/dist/emulation/sync.js +25 -25
- package/dist/{ApiError.d.ts → error.d.ts} +13 -14
- package/dist/error.js +292 -0
- package/dist/file.d.ts +2 -0
- package/dist/file.js +10 -4
- package/dist/filesystem.js +15 -15
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -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/AsyncStore.ts +29 -29
- package/src/backends/Fetch.ts +230 -0
- package/src/backends/Index.ts +19 -19
- package/src/backends/Locked.ts +50 -49
- package/src/backends/Overlay.ts +23 -23
- package/src/backends/SyncStore.ts +27 -27
- package/src/backends/backend.ts +6 -6
- package/src/backends/port/fs.ts +308 -0
- package/src/backends/port/readme.md +59 -0
- package/src/backends/port/rpc.ts +144 -0
- package/src/backends/port/store.ts +187 -0
- package/src/config.ts +20 -24
- 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 +12 -14
- package/src/emulation/streams.ts +3 -3
- package/src/emulation/sync.ts +28 -28
- package/src/{ApiError.ts → error.ts} +89 -89
- package/src/file.ts +12 -4
- package/src/filesystem.ts +15 -15
- package/src/index.ts +4 -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/src/emulation/shared.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Utilities and shared data
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
4
4
|
import { InMemory } from '../backends/InMemory.js';
|
|
5
5
|
import { Cred, rootCred } from '../cred.js';
|
|
6
6
|
import type { File } from '../file.js';
|
|
7
7
|
import { FileSystem } from '../filesystem.js';
|
|
8
8
|
import { normalizePath } from '../utils.js';
|
|
9
|
-
import { resolve } from './path.js';
|
|
9
|
+
import { resolve, type AbsolutePath } from './path.js';
|
|
10
10
|
|
|
11
11
|
// credentials
|
|
12
12
|
export let cred: Cred = rootCred;
|
|
@@ -24,15 +24,13 @@ export function file2fd(file: File): number {
|
|
|
24
24
|
}
|
|
25
25
|
export function fd2file(fd: number): File {
|
|
26
26
|
if (!fdMap.has(fd)) {
|
|
27
|
-
throw new
|
|
27
|
+
throw new ErrnoError(Errno.EBADF);
|
|
28
28
|
}
|
|
29
29
|
return fdMap.get(fd)!;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// mounting
|
|
33
|
-
export
|
|
34
|
-
[point: string]: FileSystem;
|
|
35
|
-
}
|
|
33
|
+
export type MountObject = Record<AbsolutePath, FileSystem>;
|
|
36
34
|
|
|
37
35
|
/**
|
|
38
36
|
* The map of mount points
|
|
@@ -54,7 +52,7 @@ export function mount(mountPoint: string, fs: FileSystem): void {
|
|
|
54
52
|
}
|
|
55
53
|
mountPoint = resolve(mountPoint);
|
|
56
54
|
if (mounts.has(mountPoint)) {
|
|
57
|
-
throw new
|
|
55
|
+
throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use.');
|
|
58
56
|
}
|
|
59
57
|
mounts.set(mountPoint, fs);
|
|
60
58
|
}
|
|
@@ -68,7 +66,7 @@ export function umount(mountPoint: string): void {
|
|
|
68
66
|
}
|
|
69
67
|
mountPoint = resolve(mountPoint);
|
|
70
68
|
if (!mounts.has(mountPoint)) {
|
|
71
|
-
throw new
|
|
69
|
+
throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already unmounted.');
|
|
72
70
|
}
|
|
73
71
|
mounts.delete(mountPoint);
|
|
74
72
|
}
|
|
@@ -90,20 +88,20 @@ export function resolveMount(path: string): { fs: FileSystem; path: string; moun
|
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
throw new
|
|
91
|
+
throw new ErrnoError(Errno.EIO, 'ZenFS not initialized with a file system');
|
|
94
92
|
}
|
|
95
93
|
|
|
96
94
|
/**
|
|
97
95
|
* Reverse maps the paths in text from the mounted FileSystem to the global path
|
|
98
96
|
*/
|
|
99
|
-
export function fixPaths(text: string, paths:
|
|
97
|
+
export function fixPaths(text: string, paths: Record<string, string>): string {
|
|
100
98
|
for (const [from, to] of Object.entries(paths)) {
|
|
101
99
|
text = text?.replaceAll(from, to);
|
|
102
100
|
}
|
|
103
101
|
return text;
|
|
104
102
|
}
|
|
105
103
|
|
|
106
|
-
export function fixError<E extends Error>(e: E, paths:
|
|
104
|
+
export function fixError<E extends Error>(e: E, paths: Record<string, string>): E {
|
|
107
105
|
if (typeof e.stack == 'string') {
|
|
108
106
|
e.stack = fixPaths(e.stack, paths);
|
|
109
107
|
}
|
|
@@ -111,11 +109,11 @@ export function fixError<E extends Error>(e: E, paths: { [from: string]: string
|
|
|
111
109
|
return e;
|
|
112
110
|
}
|
|
113
111
|
|
|
114
|
-
export function
|
|
115
|
-
if ('/' in
|
|
112
|
+
export function mountObject(mounts: MountObject): void {
|
|
113
|
+
if ('/' in mounts) {
|
|
116
114
|
umount('/');
|
|
117
115
|
}
|
|
118
|
-
for (const [point, fs] of Object.entries(
|
|
116
|
+
for (const [point, fs] of Object.entries(mounts)) {
|
|
119
117
|
mount(point, fs);
|
|
120
118
|
}
|
|
121
119
|
}
|
package/src/emulation/streams.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as Node from 'fs';
|
|
2
2
|
import { Readable, Writable } from 'readable-stream';
|
|
3
3
|
import { Callback } from '../utils.js';
|
|
4
|
-
import {
|
|
4
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
5
5
|
|
|
6
6
|
export class ReadStream extends Readable implements Node.ReadStream {
|
|
7
7
|
close(callback: Callback = () => null): void {
|
|
@@ -10,7 +10,7 @@ export class ReadStream extends Readable implements Node.ReadStream {
|
|
|
10
10
|
super.emit('close');
|
|
11
11
|
callback();
|
|
12
12
|
} catch (err) {
|
|
13
|
-
callback(new
|
|
13
|
+
callback(new ErrnoError(Errno.EIO, (err as Error).toString()));
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
declare bytesRead: number;
|
|
@@ -25,7 +25,7 @@ export class WriteStream extends Writable implements Node.WriteStream {
|
|
|
25
25
|
super.emit('close');
|
|
26
26
|
callback();
|
|
27
27
|
} catch (err) {
|
|
28
|
-
callback(new
|
|
28
|
+
callback(new ErrnoError(Errno.EIO, (err as Error).toString()));
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
declare bytesWritten: number;
|
package/src/emulation/sync.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import type * as fs from 'node:fs';
|
|
3
|
-
import {
|
|
3
|
+
import { ErrnoError, Errno } from '../error.js';
|
|
4
4
|
import { ActionType, File, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
|
|
5
5
|
import { FileContents, FileSystem } from '../filesystem.js';
|
|
6
6
|
import { BigIntStats, FileType, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
|
|
@@ -23,7 +23,7 @@ function doOp<M extends FileSystemMethod, RT extends ReturnType<M>>(...[name, re
|
|
|
23
23
|
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
24
24
|
return fs[name](resolvedPath, ...args) as RT;
|
|
25
25
|
} catch (e) {
|
|
26
|
-
throw fixError(
|
|
26
|
+
throw fixError(e as Error, { [resolvedPath]: path });
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -46,7 +46,7 @@ export function renameSync(oldPath: fs.PathLike, newPath: fs.PathLike): void {
|
|
|
46
46
|
writeFileSync(newPath, readFileSync(oldPath));
|
|
47
47
|
unlinkSync(oldPath);
|
|
48
48
|
} catch (e) {
|
|
49
|
-
throw fixError(
|
|
49
|
+
throw fixError(e as Error, paths);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
renameSync satisfies typeof fs.renameSync;
|
|
@@ -61,7 +61,7 @@ export function existsSync(path: fs.PathLike): boolean {
|
|
|
61
61
|
const { fs, path: resolvedPath } = resolveMount(realpathSync(path));
|
|
62
62
|
return fs.existsSync(resolvedPath, cred);
|
|
63
63
|
} catch (e) {
|
|
64
|
-
if ((e as
|
|
64
|
+
if ((e as ErrnoError).errno == Errno.ENOENT) {
|
|
65
65
|
return false;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -136,23 +136,23 @@ function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | nul
|
|
|
136
136
|
// Ensure parent exists.
|
|
137
137
|
const parentStats: Stats = doOp('statSync', resolveSymlinks, dirname(path), cred);
|
|
138
138
|
if (!parentStats.isDirectory()) {
|
|
139
|
-
throw
|
|
139
|
+
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
|
|
140
140
|
}
|
|
141
141
|
return doOp('createFileSync', resolveSymlinks, path, flag, mode, cred);
|
|
142
142
|
case ActionType.THROW:
|
|
143
|
-
throw
|
|
143
|
+
throw ErrnoError.With('ENOENT', path, '_open');
|
|
144
144
|
default:
|
|
145
|
-
throw new
|
|
145
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
if (!stats.hasAccess(mode, cred)) {
|
|
149
|
-
throw
|
|
149
|
+
throw ErrnoError.With('EACCES', path, '_open');
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// File exists.
|
|
153
153
|
switch (pathExistsAction(flag)) {
|
|
154
154
|
case ActionType.THROW:
|
|
155
|
-
throw
|
|
155
|
+
throw ErrnoError.With('EEXIST', path, '_open');
|
|
156
156
|
case ActionType.TRUNCATE:
|
|
157
157
|
// Delete file.
|
|
158
158
|
doOp('unlinkSync', resolveSymlinks, path, cred);
|
|
@@ -166,7 +166,7 @@ function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | nul
|
|
|
166
166
|
case ActionType.NOP:
|
|
167
167
|
return doOp('openFileSync', resolveSymlinks, path, flag, cred);
|
|
168
168
|
default:
|
|
169
|
-
throw new
|
|
169
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -223,7 +223,7 @@ export function readFileSync(path: fs.PathOrFileDescriptor, _options: fs.WriteFi
|
|
|
223
223
|
const options = normalizeOptions(_options, null, 'r', 0o644);
|
|
224
224
|
const flag = parseFlag(options.flag);
|
|
225
225
|
if (!isReadable(flag)) {
|
|
226
|
-
throw new
|
|
226
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading.');
|
|
227
227
|
}
|
|
228
228
|
const data: Buffer = Buffer.from(_readFileSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), options.flag, true));
|
|
229
229
|
return options.encoding ? data.toString(options.encoding) : data;
|
|
@@ -263,14 +263,14 @@ export function writeFileSync(path: fs.PathOrFileDescriptor, data: FileContents,
|
|
|
263
263
|
const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
|
|
264
264
|
const flag = parseFlag(options.flag);
|
|
265
265
|
if (!isWriteable(flag)) {
|
|
266
|
-
throw new
|
|
266
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to writeFile must allow for writing.');
|
|
267
267
|
}
|
|
268
268
|
if (typeof data != 'string' && !options.encoding) {
|
|
269
|
-
throw new
|
|
269
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
270
270
|
}
|
|
271
271
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
272
272
|
if (!encodedData) {
|
|
273
|
-
throw new
|
|
273
|
+
throw new ErrnoError(Errno.EINVAL, 'Data not specified');
|
|
274
274
|
}
|
|
275
275
|
_writeFileSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), encodedData, options.flag, options.mode, true);
|
|
276
276
|
}
|
|
@@ -304,10 +304,10 @@ export function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileCont
|
|
|
304
304
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
305
305
|
const flag = parseFlag(options.flag);
|
|
306
306
|
if (!isAppendable(flag)) {
|
|
307
|
-
throw new
|
|
307
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending.');
|
|
308
308
|
}
|
|
309
309
|
if (typeof data != 'string' && !options.encoding) {
|
|
310
|
-
throw new
|
|
310
|
+
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
311
311
|
}
|
|
312
312
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
313
313
|
_appendFileSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), encodedData, options.flag, options.mode, true);
|
|
@@ -346,7 +346,7 @@ closeSync satisfies typeof fs.closeSync;
|
|
|
346
346
|
export function ftruncateSync(fd: number, len: number | null = 0): void {
|
|
347
347
|
len ||= 0;
|
|
348
348
|
if (len < 0) {
|
|
349
|
-
throw new
|
|
349
|
+
throw new ErrnoError(Errno.EINVAL);
|
|
350
350
|
}
|
|
351
351
|
fd2file(fd).truncateSync(len);
|
|
352
352
|
}
|
|
@@ -458,7 +458,7 @@ fchownSync satisfies typeof fs.fchownSync;
|
|
|
458
458
|
export function fchmodSync(fd: number, mode: number | string): void {
|
|
459
459
|
const numMode = normalizeMode(mode, -1);
|
|
460
460
|
if (numMode < 0) {
|
|
461
|
-
throw new
|
|
461
|
+
throw new ErrnoError(Errno.EINVAL, `Invalid mode.`);
|
|
462
462
|
}
|
|
463
463
|
fd2file(fd).chmodSync(numMode);
|
|
464
464
|
}
|
|
@@ -561,10 +561,10 @@ linkSync satisfies typeof fs.linkSync;
|
|
|
561
561
|
*/
|
|
562
562
|
export function symlinkSync(target: fs.PathLike, path: fs.PathLike, type: fs.symlink.Type | null = 'file'): void {
|
|
563
563
|
if (!['file', 'dir', 'junction'].includes(type!)) {
|
|
564
|
-
throw new
|
|
564
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid type: ' + type);
|
|
565
565
|
}
|
|
566
566
|
if (existsSync(path)) {
|
|
567
|
-
throw
|
|
567
|
+
throw ErrnoError.With('EEXIST', path.toString(), 'symlink');
|
|
568
568
|
}
|
|
569
569
|
|
|
570
570
|
writeFileSync(path, target.toString());
|
|
@@ -691,7 +691,7 @@ export function realpathSync(path: fs.PathLike, options?: fs.EncodingOption | fs
|
|
|
691
691
|
|
|
692
692
|
return realpathSync(mountPoint + readlinkSync(lpath));
|
|
693
693
|
} catch (e) {
|
|
694
|
-
throw fixError(
|
|
694
|
+
throw fixError(e as Error, { [resolvedPath]: lpath });
|
|
695
695
|
}
|
|
696
696
|
}
|
|
697
697
|
realpathSync satisfies Omit<typeof fs.realpathSync, 'native'>;
|
|
@@ -704,7 +704,7 @@ realpathSync satisfies Omit<typeof fs.realpathSync, 'native'>;
|
|
|
704
704
|
export function accessSync(path: fs.PathLike, mode: number = 0o600): void {
|
|
705
705
|
const stats = statSync(path);
|
|
706
706
|
if (!stats.hasAccess(mode, cred)) {
|
|
707
|
-
throw new
|
|
707
|
+
throw new ErrnoError(Errno.EACCES);
|
|
708
708
|
}
|
|
709
709
|
}
|
|
710
710
|
accessSync satisfies typeof fs.accessSync;
|
|
@@ -737,7 +737,7 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions): void {
|
|
|
737
737
|
case S_IFIFO:
|
|
738
738
|
case S_IFSOCK:
|
|
739
739
|
default:
|
|
740
|
-
throw new
|
|
740
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
|
|
741
741
|
}
|
|
742
742
|
}
|
|
743
743
|
rmSync satisfies typeof fs.rmSync;
|
|
@@ -773,7 +773,7 @@ export function copyFileSync(src: fs.PathLike, dest: fs.PathLike, flags?: number
|
|
|
773
773
|
dest = normalizePath(dest);
|
|
774
774
|
|
|
775
775
|
if (flags && flags & COPYFILE_EXCL && existsSync(dest)) {
|
|
776
|
-
throw new
|
|
776
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');
|
|
777
777
|
}
|
|
778
778
|
|
|
779
779
|
writeFileSync(dest, readFileSync(src));
|
|
@@ -849,13 +849,13 @@ export function cpSync(source: fs.PathLike, destination: fs.PathLike, opts?: fs.
|
|
|
849
849
|
const srcStats = lstatSync(source); // Use lstat to follow symlinks if not dereferencing
|
|
850
850
|
|
|
851
851
|
if (opts?.errorOnExist && existsSync(destination)) {
|
|
852
|
-
throw new
|
|
852
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists.', destination, 'cp');
|
|
853
853
|
}
|
|
854
854
|
|
|
855
855
|
switch (srcStats.mode & S_IFMT) {
|
|
856
856
|
case S_IFDIR:
|
|
857
857
|
if (!opts?.recursive) {
|
|
858
|
-
throw new
|
|
858
|
+
throw new ErrnoError(Errno.EISDIR, source + ' is a directory (not copied)', source, 'cp');
|
|
859
859
|
}
|
|
860
860
|
mkdirSync(destination, { recursive: true }); // Ensure the destination directory exists
|
|
861
861
|
for (const dirent of readdirSync(source, { withFileTypes: true })) {
|
|
@@ -874,7 +874,7 @@ export function cpSync(source: fs.PathLike, destination: fs.PathLike, opts?: fs.
|
|
|
874
874
|
case S_IFIFO:
|
|
875
875
|
case S_IFSOCK:
|
|
876
876
|
default:
|
|
877
|
-
throw new
|
|
877
|
+
throw new ErrnoError(Errno.EPERM, 'File type not supported', source, 'rm');
|
|
878
878
|
}
|
|
879
879
|
|
|
880
880
|
// Optionally preserve timestamps
|
|
@@ -894,5 +894,5 @@ export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions & { big
|
|
|
894
894
|
export function statfsSync(path: fs.PathLike, options: fs.StatFsOptions & { bigint: true }): BigIntStatsFs;
|
|
895
895
|
export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions): StatsFs | BigIntStatsFs;
|
|
896
896
|
export function statfsSync(path: fs.PathLike, options?: fs.StatFsOptions): StatsFs | BigIntStatsFs {
|
|
897
|
-
throw
|
|
897
|
+
throw ErrnoError.With('ENOSYS', path.toString(), 'statfs');
|
|
898
898
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Standard libc error codes. More will be added to this enum and
|
|
2
|
+
* Standard libc error codes. More will be added to this enum and error strings as they are
|
|
3
3
|
* needed.
|
|
4
4
|
* @url https://en.wikipedia.org/wiki/Errno.h
|
|
5
5
|
*/
|
|
6
|
-
export enum
|
|
6
|
+
export enum Errno {
|
|
7
7
|
/** Operation not permitted */
|
|
8
8
|
EPERM = 1,
|
|
9
9
|
/** No such file or directory */
|
|
@@ -157,88 +157,88 @@ export enum ErrorCode {
|
|
|
157
157
|
* Strings associated with each error code.
|
|
158
158
|
* @internal
|
|
159
159
|
*/
|
|
160
|
-
export const errorMessages: { [K in
|
|
161
|
-
[
|
|
162
|
-
[
|
|
163
|
-
[
|
|
164
|
-
[
|
|
165
|
-
[
|
|
166
|
-
[
|
|
167
|
-
[
|
|
168
|
-
[
|
|
169
|
-
[
|
|
170
|
-
[
|
|
171
|
-
[
|
|
172
|
-
[
|
|
173
|
-
[
|
|
174
|
-
[
|
|
175
|
-
[
|
|
176
|
-
[
|
|
177
|
-
[
|
|
178
|
-
[
|
|
179
|
-
[
|
|
180
|
-
[
|
|
181
|
-
[
|
|
182
|
-
[
|
|
183
|
-
[
|
|
184
|
-
[
|
|
185
|
-
[
|
|
186
|
-
[
|
|
187
|
-
[
|
|
188
|
-
[
|
|
189
|
-
[
|
|
190
|
-
[
|
|
191
|
-
[
|
|
192
|
-
[
|
|
193
|
-
[
|
|
194
|
-
[
|
|
195
|
-
[
|
|
196
|
-
[
|
|
197
|
-
[
|
|
198
|
-
[
|
|
199
|
-
[
|
|
200
|
-
[
|
|
201
|
-
[
|
|
202
|
-
[
|
|
203
|
-
[
|
|
204
|
-
[
|
|
205
|
-
[
|
|
206
|
-
[
|
|
207
|
-
[
|
|
208
|
-
[
|
|
209
|
-
[
|
|
210
|
-
[
|
|
211
|
-
[
|
|
212
|
-
[
|
|
213
|
-
[
|
|
214
|
-
[
|
|
215
|
-
[
|
|
216
|
-
[
|
|
217
|
-
[
|
|
218
|
-
[
|
|
219
|
-
[
|
|
220
|
-
[
|
|
221
|
-
[
|
|
222
|
-
[
|
|
223
|
-
[
|
|
224
|
-
[
|
|
225
|
-
[
|
|
226
|
-
[
|
|
227
|
-
[
|
|
228
|
-
[
|
|
229
|
-
[
|
|
230
|
-
[
|
|
231
|
-
[
|
|
232
|
-
[
|
|
233
|
-
[
|
|
234
|
-
[
|
|
160
|
+
export const errorMessages: { [K in Errno]: string } = {
|
|
161
|
+
[Errno.EPERM]: 'Operation not permitted',
|
|
162
|
+
[Errno.ENOENT]: 'No such file or directory',
|
|
163
|
+
[Errno.EINTR]: 'Interrupted system call',
|
|
164
|
+
[Errno.EIO]: 'Input/output error',
|
|
165
|
+
[Errno.ENXIO]: 'No such device or address',
|
|
166
|
+
[Errno.EBADF]: 'Bad file descriptor',
|
|
167
|
+
[Errno.EAGAIN]: 'Resource temporarily unavailable',
|
|
168
|
+
[Errno.ENOMEM]: 'Cannot allocate memory',
|
|
169
|
+
[Errno.EACCES]: 'Permission denied',
|
|
170
|
+
[Errno.EFAULT]: 'Bad address',
|
|
171
|
+
[Errno.ENOTBLK]: 'Block device required',
|
|
172
|
+
[Errno.EBUSY]: 'Resource busy or locked',
|
|
173
|
+
[Errno.EEXIST]: 'File exists',
|
|
174
|
+
[Errno.EXDEV]: 'Invalid cross-device link',
|
|
175
|
+
[Errno.ENODEV]: 'No such device',
|
|
176
|
+
[Errno.ENOTDIR]: 'File is not a directory',
|
|
177
|
+
[Errno.EISDIR]: 'File is a directory',
|
|
178
|
+
[Errno.EINVAL]: 'Invalid argument',
|
|
179
|
+
[Errno.ENFILE]: 'Too many open files in system',
|
|
180
|
+
[Errno.EMFILE]: 'Too many open files',
|
|
181
|
+
[Errno.ETXTBSY]: 'Text file busy',
|
|
182
|
+
[Errno.EFBIG]: 'File is too big',
|
|
183
|
+
[Errno.ENOSPC]: 'No space left on disk',
|
|
184
|
+
[Errno.ESPIPE]: 'Illegal seek',
|
|
185
|
+
[Errno.EROFS]: 'Cannot modify a read-only file system',
|
|
186
|
+
[Errno.EMLINK]: 'Too many links',
|
|
187
|
+
[Errno.EPIPE]: 'Broken pipe',
|
|
188
|
+
[Errno.EDOM]: 'Numerical argument out of domain',
|
|
189
|
+
[Errno.ERANGE]: 'Numerical result out of range',
|
|
190
|
+
[Errno.EDEADLK]: 'Resource deadlock would occur',
|
|
191
|
+
[Errno.ENAMETOOLONG]: 'File name too long',
|
|
192
|
+
[Errno.ENOLCK]: 'No locks available',
|
|
193
|
+
[Errno.ENOSYS]: 'Function not implemented',
|
|
194
|
+
[Errno.ENOTEMPTY]: 'Directory is not empty',
|
|
195
|
+
[Errno.ELOOP]: 'Too many levels of symbolic links',
|
|
196
|
+
[Errno.ENOMSG]: 'No message of desired type',
|
|
197
|
+
[Errno.EBADE]: 'Invalid exchange',
|
|
198
|
+
[Errno.EBADR]: 'Invalid request descriptor',
|
|
199
|
+
[Errno.EXFULL]: 'Exchange full',
|
|
200
|
+
[Errno.ENOANO]: 'No anode',
|
|
201
|
+
[Errno.EBADRQC]: 'Invalid request code',
|
|
202
|
+
[Errno.ENOSTR]: 'Device not a stream',
|
|
203
|
+
[Errno.ENODATA]: 'No data available',
|
|
204
|
+
[Errno.ETIME]: 'Timer expired',
|
|
205
|
+
[Errno.ENOSR]: 'Out of streams resources',
|
|
206
|
+
[Errno.ENONET]: 'Machine is not on the network',
|
|
207
|
+
[Errno.EREMOTE]: 'Object is remote',
|
|
208
|
+
[Errno.ENOLINK]: 'Link has been severed',
|
|
209
|
+
[Errno.ECOMM]: 'Communication error on send',
|
|
210
|
+
[Errno.EPROTO]: 'Protocol error',
|
|
211
|
+
[Errno.EBADMSG]: 'Bad message',
|
|
212
|
+
[Errno.EOVERFLOW]: 'Value too large for defined data type',
|
|
213
|
+
[Errno.EBADFD]: 'File descriptor in bad state',
|
|
214
|
+
[Errno.ESTRPIPE]: 'Streams pipe error',
|
|
215
|
+
[Errno.ENOTSOCK]: 'Socket operation on non-socket',
|
|
216
|
+
[Errno.EDESTADDRREQ]: 'Destination address required',
|
|
217
|
+
[Errno.EMSGSIZE]: 'Message too long',
|
|
218
|
+
[Errno.EPROTOTYPE]: 'Protocol wrong type for socket',
|
|
219
|
+
[Errno.ENOPROTOOPT]: 'Protocol not available',
|
|
220
|
+
[Errno.EPROTONOSUPPORT]: 'Protocol not supported',
|
|
221
|
+
[Errno.ESOCKTNOSUPPORT]: 'Socket type not supported',
|
|
222
|
+
[Errno.ENOTSUP]: 'Operation is not supported',
|
|
223
|
+
[Errno.ENETDOWN]: 'Network is down',
|
|
224
|
+
[Errno.ENETUNREACH]: 'Network is unreachable',
|
|
225
|
+
[Errno.ENETRESET]: 'Network dropped connection on reset',
|
|
226
|
+
[Errno.ETIMEDOUT]: 'Connection timed out',
|
|
227
|
+
[Errno.ECONNREFUSED]: 'Connection refused',
|
|
228
|
+
[Errno.EHOSTDOWN]: 'Host is down',
|
|
229
|
+
[Errno.EHOSTUNREACH]: 'No route to host',
|
|
230
|
+
[Errno.EALREADY]: 'Operation already in progress',
|
|
231
|
+
[Errno.EINPROGRESS]: 'Operation now in progress',
|
|
232
|
+
[Errno.ESTALE]: 'Stale file handle',
|
|
233
|
+
[Errno.EREMOTEIO]: 'Remote I/O error',
|
|
234
|
+
[Errno.EDQUOT]: 'Disk quota exceeded',
|
|
235
235
|
};
|
|
236
236
|
|
|
237
|
-
interface
|
|
238
|
-
errno:
|
|
237
|
+
export interface ErrnoErrorJSON {
|
|
238
|
+
errno: Errno;
|
|
239
239
|
message: string;
|
|
240
240
|
path?: string;
|
|
241
|
-
code: keyof typeof
|
|
241
|
+
code: keyof typeof Errno;
|
|
242
242
|
stack: string;
|
|
243
243
|
syscall: string;
|
|
244
244
|
}
|
|
@@ -247,19 +247,19 @@ interface ApiErrorJSON {
|
|
|
247
247
|
* Represents a ZenFS error. Passed back to applications after a failed
|
|
248
248
|
* call to the ZenFS API.
|
|
249
249
|
*/
|
|
250
|
-
export class
|
|
251
|
-
public static fromJSON(json:
|
|
252
|
-
const err = new
|
|
250
|
+
export class ErrnoError extends Error implements NodeJS.ErrnoException {
|
|
251
|
+
public static fromJSON(json: ErrnoErrorJSON): ErrnoError {
|
|
252
|
+
const err = new ErrnoError(json.errno, json.message, json.path, json.syscall);
|
|
253
253
|
err.code = json.code;
|
|
254
254
|
err.stack = json.stack;
|
|
255
255
|
return err;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
public static With(code: keyof typeof
|
|
259
|
-
return new
|
|
258
|
+
public static With(code: keyof typeof Errno, path?: string, syscall?: string): ErrnoError {
|
|
259
|
+
return new ErrnoError(Errno[code], errorMessages[Errno[code]], path, syscall);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
public code: keyof typeof
|
|
262
|
+
public code: keyof typeof Errno;
|
|
263
263
|
|
|
264
264
|
public declare stack: string;
|
|
265
265
|
|
|
@@ -274,13 +274,13 @@ export class ApiError extends Error implements NodeJS.ErrnoException {
|
|
|
274
274
|
* @param message A descriptive error message.
|
|
275
275
|
*/
|
|
276
276
|
constructor(
|
|
277
|
-
public errno:
|
|
277
|
+
public errno: Errno,
|
|
278
278
|
message: string = errorMessages[errno],
|
|
279
279
|
public path?: string,
|
|
280
280
|
public syscall: string = ''
|
|
281
281
|
) {
|
|
282
282
|
super(message);
|
|
283
|
-
this.code = <keyof typeof
|
|
283
|
+
this.code = <keyof typeof Errno>Errno[errno];
|
|
284
284
|
this.message = `${this.code}: ${message}${this.path ? `, '${this.path}'` : ''}`;
|
|
285
285
|
}
|
|
286
286
|
|
|
@@ -291,7 +291,7 @@ export class ApiError extends Error implements NodeJS.ErrnoException {
|
|
|
291
291
|
return this.message;
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
public toJSON():
|
|
294
|
+
public toJSON(): ErrnoErrorJSON {
|
|
295
295
|
return {
|
|
296
296
|
errno: this.errno,
|
|
297
297
|
code: this.code,
|
package/src/file.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FileReadResult } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
2
|
+
import { ErrnoError, Errno } from './error.js';
|
|
3
3
|
import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT } from './emulation/constants.js';
|
|
4
4
|
import type { FileSystem } from './filesystem.js';
|
|
5
5
|
import { size_max } from './inode.js';
|
|
@@ -208,6 +208,14 @@ export abstract class File {
|
|
|
208
208
|
*/
|
|
209
209
|
public abstract closeSync(): void;
|
|
210
210
|
|
|
211
|
+
public [Symbol.asyncDispose](): Promise<void> {
|
|
212
|
+
return this.close();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public [Symbol.dispose](): void {
|
|
216
|
+
return this.closeSync();
|
|
217
|
+
}
|
|
218
|
+
|
|
211
219
|
/**
|
|
212
220
|
* Asynchronous truncate.
|
|
213
221
|
*/
|
|
@@ -482,7 +490,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
482
490
|
public truncateSync(len: number): void {
|
|
483
491
|
this._dirty = true;
|
|
484
492
|
if (!isWriteable(this.flag)) {
|
|
485
|
-
throw new
|
|
493
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
486
494
|
}
|
|
487
495
|
this.stats.mtimeMs = Date.now();
|
|
488
496
|
if (len > this._buffer.length) {
|
|
@@ -537,7 +545,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
537
545
|
this._dirty = true;
|
|
538
546
|
position ??= this.position;
|
|
539
547
|
if (!isWriteable(this.flag)) {
|
|
540
|
-
throw new
|
|
548
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
541
549
|
}
|
|
542
550
|
const endFp = position + length;
|
|
543
551
|
if (endFp > this.stats.size) {
|
|
@@ -598,7 +606,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
598
606
|
*/
|
|
599
607
|
public readSync(buffer: ArrayBufferView, offset: number = 0, length: number = this.stats.size, position?: number): number {
|
|
600
608
|
if (!isReadable(this.flag)) {
|
|
601
|
-
throw new
|
|
609
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
|
|
602
610
|
}
|
|
603
611
|
position ??= this.position;
|
|
604
612
|
let end = position + length;
|