@zenfs/core 1.1.3 → 1.1.5
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/fetch.js +5 -5
- 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/config.js +1 -0
- 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/license.md +1 -1
- package/package.json +5 -3
- package/readme.md +3 -8
- package/scripts/make-index.js +3 -3
- package/scripts/test.js +31 -0
- package/tests/assignment.ts +20 -0
- package/tests/common.ts +21 -0
- package/tests/data/49chars.txt +1 -0
- package/tests/data/a.js +46 -0
- package/tests/data/a1.js +46 -0
- package/tests/data/elipses.txt +1 -0
- package/tests/data/empty.txt +0 -0
- package/tests/data/exit.js +22 -0
- package/tests/data/x.txt +1 -0
- package/tests/devices.test.ts +29 -0
- package/tests/fs/appendFile.test.ts +33 -0
- package/tests/fs/chmod.test.ts +48 -0
- package/tests/fs/dir.test.ts +156 -0
- package/tests/fs/directory.test.ts +129 -0
- package/tests/fs/errors.test.ts +53 -0
- package/tests/fs/exists.test.ts +22 -0
- package/tests/fs/links.test.ts +46 -0
- package/tests/fs/open.test.ts +39 -0
- package/tests/fs/permissions.test.ts +52 -0
- package/tests/fs/read.test.ts +67 -0
- package/tests/fs/readFile.test.ts +73 -0
- package/tests/fs/readdir.test.ts +87 -0
- package/tests/fs/rename.test.ts +107 -0
- package/tests/fs/stat.test.ts +48 -0
- package/tests/fs/streams.test.ts +177 -0
- package/tests/fs/times.test.ts +84 -0
- package/tests/fs/truncate.test.ts +94 -0
- package/tests/fs/watch.test.ts +124 -0
- package/tests/fs/write.test.ts +58 -0
- package/tests/fs/writeFile.test.ts +69 -0
- package/tests/handle.test.ts +60 -0
- package/tests/mutex.test.ts +62 -0
- package/tests/path.test.ts +34 -0
- package/tests/port/channel.test.ts +39 -0
- package/tests/port/config.test.ts +31 -0
- package/tests/port/config.worker.ts +5 -0
- package/tests/port/remote.test.ts +33 -0
- package/tests/port/remote.worker.ts +5 -0
- package/tests/port/timeout.test.ts +48 -0
- package/tests/readme.md +5 -0
- package/tests/setup/common.ts +28 -0
- package/tests/setup/cow+fetch.ts +43 -0
- package/tests/setup/memory.ts +3 -0
- package/tests/tsconfig.json +14 -0
- package/src/backends/backend.ts +0 -160
- package/src/backends/fetch.ts +0 -179
- package/src/backends/file_index.ts +0 -210
- package/src/backends/memory.ts +0 -50
- package/src/backends/overlay.ts +0 -568
- package/src/backends/port/fs.ts +0 -334
- package/src/backends/port/readme.md +0 -54
- package/src/backends/port/rpc.ts +0 -166
- package/src/backends/readme.md +0 -3
- package/src/backends/store/fs.ts +0 -715
- package/src/backends/store/readme.md +0 -9
- package/src/backends/store/simple.ts +0 -146
- package/src/backends/store/store.ts +0 -173
- package/src/config.ts +0 -152
- package/src/credentials.ts +0 -31
- package/src/devices.ts +0 -469
- package/src/emulation/async.ts +0 -834
- package/src/emulation/constants.ts +0 -182
- package/src/emulation/dir.ts +0 -138
- package/src/emulation/index.ts +0 -8
- package/src/emulation/path.ts +0 -440
- package/src/emulation/promises.ts +0 -1090
- package/src/emulation/shared.ts +0 -135
- package/src/emulation/streams.ts +0 -34
- package/src/emulation/sync.ts +0 -839
- package/src/emulation/watchers.ts +0 -193
- package/src/error.ts +0 -307
- package/src/file.ts +0 -661
- package/src/filesystem.ts +0 -174
- package/src/index.ts +0 -25
- package/src/inode.ts +0 -132
- package/src/mixins/async.ts +0 -208
- package/src/mixins/index.ts +0 -5
- package/src/mixins/mutexed.ts +0 -257
- package/src/mixins/readonly.ts +0 -96
- package/src/mixins/shared.ts +0 -25
- package/src/mixins/sync.ts +0 -58
- package/src/polyfills.ts +0 -21
- package/src/stats.ts +0 -363
- package/src/utils.ts +0 -288
package/dist/backends/fetch.js
CHANGED
|
@@ -2,21 +2,21 @@ import { Errno, ErrnoError } from '../error.js';
|
|
|
2
2
|
import { IndexFS } from './file_index.js';
|
|
3
3
|
async function fetchFile(path, type) {
|
|
4
4
|
const response = await fetch(path).catch((e) => {
|
|
5
|
-
throw new ErrnoError(Errno.EIO, e.message);
|
|
5
|
+
throw new ErrnoError(Errno.EIO, e.message, path);
|
|
6
6
|
});
|
|
7
7
|
if (!response.ok) {
|
|
8
|
-
throw new ErrnoError(Errno.EIO, 'fetch failed: response returned code ' + response.status);
|
|
8
|
+
throw new ErrnoError(Errno.EIO, 'fetch failed: response returned code ' + response.status, path);
|
|
9
9
|
}
|
|
10
10
|
switch (type) {
|
|
11
11
|
case 'buffer': {
|
|
12
12
|
const arrayBuffer = await response.arrayBuffer().catch((e) => {
|
|
13
|
-
throw new ErrnoError(Errno.EIO, e.message);
|
|
13
|
+
throw new ErrnoError(Errno.EIO, e.message, path);
|
|
14
14
|
});
|
|
15
15
|
return new Uint8Array(arrayBuffer);
|
|
16
16
|
}
|
|
17
17
|
case 'json':
|
|
18
18
|
return response.json().catch((e) => {
|
|
19
|
-
throw new ErrnoError(Errno.EIO, e.message);
|
|
19
|
+
throw new ErrnoError(Errno.EIO, e.message, path);
|
|
20
20
|
});
|
|
21
21
|
default:
|
|
22
22
|
throw new ErrnoError(Errno.EINVAL, 'Invalid download type: ' + type);
|
|
@@ -58,11 +58,11 @@ export class FetchFS extends IndexFS {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
constructor({ index = 'index.json', baseUrl = '' }) {
|
|
61
|
-
super(typeof index != 'string' ? index : fetchFile(index, 'json'));
|
|
62
61
|
// prefix url must end in a directory separator.
|
|
63
62
|
if (baseUrl.at(-1) != '/') {
|
|
64
63
|
baseUrl += '/';
|
|
65
64
|
}
|
|
65
|
+
super(typeof index != 'string' ? index : fetchFile(baseUrl + index, 'json'));
|
|
66
66
|
this.baseUrl = baseUrl;
|
|
67
67
|
}
|
|
68
68
|
metadata() {
|
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/config.js
CHANGED
|
@@ -19,6 +19,7 @@ export async function resolveMountConfig(configuration, _depth = 0) {
|
|
|
19
19
|
throw new ErrnoError(Errno.EINVAL, 'Invalid mount configuration');
|
|
20
20
|
}
|
|
21
21
|
if (configuration instanceof FileSystem) {
|
|
22
|
+
await configuration.ready();
|
|
22
23
|
return configuration;
|
|
23
24
|
}
|
|
24
25
|
if (isBackend(configuration)) {
|
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/license.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Copyright (c) James Prevett and other ZenFS contributors.
|
|
2
2
|
Copyright (c) 2013-2023 John Vilk and other BrowserFS contributors.
|
|
3
|
-
Copyright (c) Joyent, Inc. and other Node contributors for `test/
|
|
3
|
+
Copyright (c) Joyent, Inc. and other Node contributors for `test/data`.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "A filesystem, anywhere",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
"storage"
|
|
15
15
|
],
|
|
16
16
|
"bin": {
|
|
17
|
-
"make-index": "scripts/make-index.js"
|
|
17
|
+
"make-index": "scripts/make-index.js",
|
|
18
|
+
"zenfs-test": "scripts/test.js"
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
21
|
"dist",
|
|
21
|
-
"
|
|
22
|
+
"tests",
|
|
22
23
|
"license.md",
|
|
23
24
|
"tsconfig.json",
|
|
24
25
|
"eslint.shared.js"
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"format": "prettier --write .",
|
|
53
54
|
"format:check": "prettier --check .",
|
|
54
55
|
"lint": "eslint src tests",
|
|
56
|
+
"test:common": "tsx --test --experimental-test-coverage 'tests/**/!(fs)/*.test.ts' 'tests/*.test.ts'",
|
|
55
57
|
"test": "tsx --test --experimental-test-coverage",
|
|
56
58
|
"build": "tsc -p tsconfig.json",
|
|
57
59
|
"build:docs": "typedoc",
|
package/readme.md
CHANGED
|
@@ -29,15 +29,10 @@ npm install @zenfs/core
|
|
|
29
29
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
32
|
-
> [!NOTE]
|
|
33
|
-
> The examples are written in ESM.
|
|
34
|
-
> If you are using CJS, you can `require` the package.
|
|
35
|
-
> If using a browser environment, you can use a `<script>` with `type=module` (you may need to use import maps)
|
|
36
|
-
|
|
37
32
|
```js
|
|
38
|
-
import fs from '@zenfs/core'; // You can also use the
|
|
33
|
+
import { fs } from '@zenfs/core'; // You can also use the default export
|
|
39
34
|
|
|
40
|
-
fs.writeFileSync('/test.txt', '
|
|
35
|
+
fs.writeFileSync('/test.txt', 'You can do this in anywhere (including browsers)!');
|
|
41
36
|
|
|
42
37
|
const contents = fs.readFileSync('/test.txt', 'utf-8');
|
|
43
38
|
console.log(contents);
|
|
@@ -148,7 +143,7 @@ fs.umount('/mnt/zip'); // finished using the zip
|
|
|
148
143
|
> [!CAUTION]
|
|
149
144
|
> Instances of backends follow the _internal_ API. You should never use a backend's methods unless you are extending a backend.
|
|
150
145
|
|
|
151
|
-
|
|
146
|
+
### Devices and device files
|
|
152
147
|
|
|
153
148
|
> [!WARNING]
|
|
154
149
|
> This is an **experimental** feature. Breaking changes may occur during non-major releases. Using this feature is the fastest way to make it stable.
|
package/scripts/make-index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readdirSync, statSync, writeFileSync } from 'fs';
|
|
2
|
+
import { readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { minimatch } from 'minimatch';
|
|
4
|
-
import { join, relative, resolve } from 'path/posix';
|
|
5
|
-
import { parseArgs } from 'util';
|
|
4
|
+
import { join, relative, resolve } from 'node:path/posix';
|
|
5
|
+
import { parseArgs } from 'node:util';
|
|
6
6
|
|
|
7
7
|
const { values: options, positionals } = parseArgs({
|
|
8
8
|
options: {
|
package/scripts/test.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { parseArgs } from 'node:util';
|
|
6
|
+
|
|
7
|
+
const { values: options, positionals } = parseArgs({
|
|
8
|
+
options: {
|
|
9
|
+
help: { short: 'h', type: 'boolean', default: false },
|
|
10
|
+
verbose: { type: 'boolean', default: false },
|
|
11
|
+
},
|
|
12
|
+
allowPositionals: true,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (options.help) {
|
|
16
|
+
console.log(`zenfs-test [...options] <...paths>
|
|
17
|
+
|
|
18
|
+
paths: The setup files to run tests on
|
|
19
|
+
|
|
20
|
+
options:
|
|
21
|
+
--help, -h Outputs this help message
|
|
22
|
+
--verbose Output verbose messages
|
|
23
|
+
`);
|
|
24
|
+
process.exit();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const setupFile of positionals) {
|
|
28
|
+
if (options.verbose) console.debug('Running tests for:', setupFile);
|
|
29
|
+
process.env.SETUP = setupFile;
|
|
30
|
+
execSync('tsx --test --experimental-test-coverage ' + join(import.meta.dirname, '../tests/fs/*.test.ts'), { stdio: 'inherit' });
|
|
31
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
This test assigns the exported fs module from ZenFS with the one exported from Node.
|
|
5
|
+
This ensures anything new that is added will be caught
|
|
6
|
+
|
|
7
|
+
Notes on omissions and exclusions:
|
|
8
|
+
- __promisify__ is omitted as it is metadata
|
|
9
|
+
- native is omitted as zenfs isn't native
|
|
10
|
+
- ReadStream and WriteStream are excluded since they are polfilled from another module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { fs as zen } from '../src/index.js';
|
|
14
|
+
import type * as node from 'node:fs';
|
|
15
|
+
|
|
16
|
+
type Mock = {
|
|
17
|
+
[K in Exclude<keyof typeof node, 'ReadStream' | 'WriteStream'>]: Omit<(typeof node)[K], '__promisify__' | 'native'>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const _module: Mock = zen;
|
package/tests/common.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { join, resolve } from 'node:path';
|
|
2
|
+
import { Worker } from 'node:worker_threads';
|
|
3
|
+
import { fs } from '../src/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a Typescript Worker
|
|
7
|
+
* @see https://github.com/privatenumber/tsx/issues/354
|
|
8
|
+
* @see https://github.com/nodejs/node/issues/47747#issuecomment-2287745567
|
|
9
|
+
*/
|
|
10
|
+
export function createTSWorker(source: string): Worker {
|
|
11
|
+
return new Worker(`import('tsx/esm/api').then(tsx => {tsx.register();import('${source}');});`, { eval: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const setupPath = resolve(process.env.SETUP || join(import.meta.dirname, 'setup/memory.ts'));
|
|
15
|
+
|
|
16
|
+
await import(setupPath).catch(error => {
|
|
17
|
+
console.log('Failed to import test setup:');
|
|
18
|
+
throw error;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export { fs };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0123456789abcdef0123456789abcdef0123456789abcdef
|