@zenfs/core 1.10.0 → 1.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/{overlay.d.ts → cow.d.ts} +91 -41
- package/dist/backends/{overlay.js → cow.js} +136 -196
- package/dist/backends/index.d.ts +1 -1
- package/dist/backends/index.js +1 -1
- package/dist/backends/port/fs.js +1 -1
- package/dist/backends/single_buffer.js +4 -4
- package/dist/backends/store/fs.js +3 -3
- package/dist/index.d.ts +1 -7
- package/dist/index.js +2 -0
- package/dist/internal/devices.js +1 -1
- package/dist/internal/file.js +7 -7
- package/dist/internal/log.d.ts +6 -5
- package/dist/internal/log.js +48 -7
- package/dist/mixins/async.js +21 -25
- package/dist/mixins/shared.d.ts +2 -2
- package/dist/polyfills.d.ts +0 -4
- package/dist/polyfills.js +16 -13
- package/dist/vfs/async.js +42 -19
- package/dist/vfs/promises.d.ts +7 -13
- package/dist/vfs/promises.js +55 -58
- package/dist/vfs/shared.js +2 -2
- package/dist/vfs/streams.d.ts +2 -2
- package/dist/vfs/streams.js +24 -18
- package/dist/vfs/sync.js +5 -5
- package/package.json +3 -3
- package/readme.md +1 -4
- package/tests/common/mutex.test.ts +1 -1
- package/tests/fetch/server.js +1 -1
- package/tests/fs/directory.test.ts +11 -59
- package/tests/fs/errors.test.ts +1 -1
- package/tests/fs/stat.test.ts +2 -6
- package/tests/fs/streams.test.ts +71 -66
- package/tests/setup/cow.ts +13 -0
- package/tests/tsconfig.json +1 -4
- package/types/README.md +1 -0
- package/types/readable-stream.d.ts +17 -0
- package/tests/setup/_overlay.ts +0 -7
package/dist/vfs/promises.js
CHANGED
|
@@ -90,7 +90,7 @@ export class FileHandle {
|
|
|
90
90
|
async chmod(mode) {
|
|
91
91
|
const numMode = normalizeMode(mode, -1);
|
|
92
92
|
if (numMode < 0)
|
|
93
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid mode
|
|
93
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid mode');
|
|
94
94
|
await this.file.chmod(numMode);
|
|
95
95
|
this._emitChange();
|
|
96
96
|
}
|
|
@@ -140,7 +140,7 @@ export class FileHandle {
|
|
|
140
140
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
141
141
|
const flag = parseFlag(options.flag);
|
|
142
142
|
if (!isAppendable(flag)) {
|
|
143
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending
|
|
143
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
|
|
144
144
|
}
|
|
145
145
|
if (typeof data != 'string' && !options.encoding) {
|
|
146
146
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
@@ -161,7 +161,7 @@ export class FileHandle {
|
|
|
161
161
|
offset = buffer.offset;
|
|
162
162
|
buffer = buffer.buffer;
|
|
163
163
|
}
|
|
164
|
-
if (
|
|
164
|
+
if (!Number.isSafeInteger(position)) {
|
|
165
165
|
position = this.file.position;
|
|
166
166
|
}
|
|
167
167
|
buffer || (buffer = new Uint8Array((await this.file.stat()).size));
|
|
@@ -171,7 +171,7 @@ export class FileHandle {
|
|
|
171
171
|
const options = normalizeOptions(_options, null, 'r', 0o444);
|
|
172
172
|
const flag = parseFlag(options.flag);
|
|
173
173
|
if (!isReadable(flag)) {
|
|
174
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading
|
|
174
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for reading');
|
|
175
175
|
}
|
|
176
176
|
const { size } = await this.stat();
|
|
177
177
|
const { buffer: data } = await this.file.read(new Uint8Array(size), 0, size, 0);
|
|
@@ -179,44 +179,25 @@ export class FileHandle {
|
|
|
179
179
|
return options.encoding ? buffer.toString(options.encoding) : buffer;
|
|
180
180
|
}
|
|
181
181
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* An error will be thrown if this method is called more than once or is called after the `FileHandle` is closed or closing.
|
|
185
|
-
*
|
|
186
|
-
* While the `ReadableStream` will read the file to completion,
|
|
187
|
-
* it will not close the `FileHandle` automatically.
|
|
188
|
-
* User code must still call the `fileHandle.close()` method.
|
|
182
|
+
* Read file data using a `ReadableStream`.
|
|
183
|
+
* The handle will not be closed automatically.
|
|
189
184
|
*/
|
|
190
185
|
readableWebStream(options = {}) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
186
|
+
return new ReadableStream({
|
|
187
|
+
start: async (controller) => {
|
|
188
|
+
const chunkSize = 0x1000;
|
|
189
|
+
for (let i = 0; i < 1e7; i++) {
|
|
190
|
+
const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize).catch(controller.error);
|
|
191
|
+
if (!result)
|
|
192
|
+
return;
|
|
198
193
|
if (!result.bytesRead) {
|
|
199
194
|
controller.close();
|
|
200
195
|
return;
|
|
201
196
|
}
|
|
202
197
|
controller.enqueue(result.buffer.subarray(0, result.bytesRead));
|
|
203
|
-
position += result.bytesRead;
|
|
204
|
-
if (++i >= maxChunks) {
|
|
205
|
-
throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
|
|
206
|
-
}
|
|
207
|
-
bytesRead = result.bytesRead;
|
|
208
198
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
controller.error(e);
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
const _gt = globalThis;
|
|
215
|
-
if (!('ReadableStream' in _gt)) {
|
|
216
|
-
throw new ErrnoError(Errno.ENOSYS, 'ReadableStream is missing on globalThis');
|
|
217
|
-
}
|
|
218
|
-
return new _gt.ReadableStream({
|
|
219
|
-
start,
|
|
199
|
+
controller.error(new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'readableWebStream'));
|
|
200
|
+
},
|
|
220
201
|
type: options.type,
|
|
221
202
|
});
|
|
222
203
|
}
|
|
@@ -240,26 +221,25 @@ export class FileHandle {
|
|
|
240
221
|
* Asynchronously writes `string` to the file.
|
|
241
222
|
* The `FileHandle` must have been opened for writing.
|
|
242
223
|
* It is unsafe to call `write()` multiple times on the same file without waiting for the `Promise`
|
|
243
|
-
* to be resolved (or rejected). For this scenario, `
|
|
224
|
+
* to be resolved (or rejected). For this scenario, `createWriteStream` is strongly recommended.
|
|
244
225
|
*/
|
|
245
226
|
async write(data, options, lenOrEnc, position) {
|
|
246
227
|
let buffer, offset, length;
|
|
247
|
-
if (typeof options == 'object') {
|
|
248
|
-
lenOrEnc = options
|
|
249
|
-
position = options
|
|
250
|
-
options = options
|
|
228
|
+
if (typeof options == 'object' && options != null) {
|
|
229
|
+
lenOrEnc = options.length;
|
|
230
|
+
position = options.position;
|
|
231
|
+
options = options.offset;
|
|
251
232
|
}
|
|
252
233
|
if (typeof data === 'string') {
|
|
253
234
|
position = typeof options === 'number' ? options : null;
|
|
254
|
-
const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8';
|
|
255
235
|
offset = 0;
|
|
256
|
-
buffer = Buffer.from(data,
|
|
236
|
+
buffer = Buffer.from(data, typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
|
|
257
237
|
length = buffer.length;
|
|
258
238
|
}
|
|
259
239
|
else {
|
|
260
240
|
buffer = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
261
|
-
offset = options;
|
|
262
|
-
length = lenOrEnc;
|
|
241
|
+
offset = options !== null && options !== void 0 ? options : 0;
|
|
242
|
+
length = typeof lenOrEnc == 'number' ? lenOrEnc : buffer.byteLength;
|
|
263
243
|
position = typeof position === 'number' ? position : null;
|
|
264
244
|
}
|
|
265
245
|
position !== null && position !== void 0 ? position : (position = this.file.position);
|
|
@@ -281,7 +261,7 @@ export class FileHandle {
|
|
|
281
261
|
const options = normalizeOptions(_options, 'utf8', 'w', 0o644);
|
|
282
262
|
const flag = parseFlag(options.flag);
|
|
283
263
|
if (!isWriteable(flag)) {
|
|
284
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing
|
|
264
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed must allow for writing');
|
|
285
265
|
}
|
|
286
266
|
if (typeof data != 'string' && !options.encoding) {
|
|
287
267
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
@@ -304,9 +284,11 @@ export class FileHandle {
|
|
|
304
284
|
* @returns The number of bytes written.
|
|
305
285
|
*/
|
|
306
286
|
async writev(buffers, position) {
|
|
287
|
+
if (typeof position == 'number')
|
|
288
|
+
this.file.position = position;
|
|
307
289
|
let bytesWritten = 0;
|
|
308
290
|
for (const buffer of buffers) {
|
|
309
|
-
bytesWritten += (await this.write(buffer
|
|
291
|
+
bytesWritten += (await this.write(buffer)).bytesWritten;
|
|
310
292
|
}
|
|
311
293
|
return { bytesWritten, buffers };
|
|
312
294
|
}
|
|
@@ -317,9 +299,11 @@ export class FileHandle {
|
|
|
317
299
|
* @returns The number of bytes read.
|
|
318
300
|
*/
|
|
319
301
|
async readv(buffers, position) {
|
|
302
|
+
if (typeof position == 'number')
|
|
303
|
+
this.file.position = position;
|
|
320
304
|
let bytesRead = 0;
|
|
321
305
|
for (const buffer of buffers) {
|
|
322
|
-
bytesRead += (await this.read(buffer
|
|
306
|
+
bytesRead += (await this.read(buffer)).bytesRead;
|
|
323
307
|
}
|
|
324
308
|
return { bytesRead, buffers };
|
|
325
309
|
}
|
|
@@ -328,15 +312,23 @@ export class FileHandle {
|
|
|
328
312
|
* @param options Options for the readable stream
|
|
329
313
|
*/
|
|
330
314
|
createReadStream(options) {
|
|
315
|
+
var _a, _b;
|
|
316
|
+
const start = (_a = options === null || options === void 0 ? void 0 : options.start) !== null && _a !== void 0 ? _a : this.file.position;
|
|
331
317
|
const stream = new ReadStream({
|
|
332
318
|
highWaterMark: (options === null || options === void 0 ? void 0 : options.highWaterMark) || 64 * 1024,
|
|
333
|
-
encoding: options.encoding,
|
|
319
|
+
encoding: (_b = options === null || options === void 0 ? void 0 : options.encoding) !== null && _b !== void 0 ? _b : undefined,
|
|
334
320
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
335
321
|
read: async (size) => {
|
|
336
322
|
try {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
323
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.end) === 'number' && start >= options.end) {
|
|
324
|
+
stream.push(null);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.end) === 'number') {
|
|
328
|
+
size = Math.min(size, options.end - start);
|
|
329
|
+
}
|
|
330
|
+
const result = await this.read(new Uint8Array(size), 0, size, options === null || options === void 0 ? void 0 : options.start);
|
|
331
|
+
stream.push(!result.bytesRead ? null : result.buffer.subarray(0, result.bytesRead));
|
|
340
332
|
}
|
|
341
333
|
catch (error) {
|
|
342
334
|
stream.destroy(error);
|
|
@@ -351,20 +343,25 @@ export class FileHandle {
|
|
|
351
343
|
* @param options Options for the writeable stream.
|
|
352
344
|
*/
|
|
353
345
|
createWriteStream(options) {
|
|
354
|
-
|
|
346
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.start) == 'number')
|
|
347
|
+
this.file.position = options.start;
|
|
348
|
+
const { stack } = new Error();
|
|
349
|
+
const stream = new WriteStream({
|
|
355
350
|
highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
|
|
356
|
-
|
|
351
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
357
352
|
write: async (chunk, encoding, callback) => {
|
|
358
353
|
try {
|
|
359
354
|
const { bytesWritten } = await this.write(chunk, null, encoding);
|
|
360
|
-
|
|
355
|
+
if (bytesWritten == chunk.length)
|
|
356
|
+
return callback();
|
|
357
|
+
throw new ErrnoError(Errno.EIO, `Failed to write full chunk of write stream (wrote ${bytesWritten}/${chunk.length} bytes)`);
|
|
361
358
|
}
|
|
362
359
|
catch (error) {
|
|
360
|
+
error.stack += stack === null || stack === void 0 ? void 0 : stack.slice(5);
|
|
363
361
|
callback(error);
|
|
364
362
|
}
|
|
365
363
|
},
|
|
366
|
-
};
|
|
367
|
-
const stream = new WriteStream(streamOptions);
|
|
364
|
+
});
|
|
368
365
|
stream.path = this.file.path;
|
|
369
366
|
return stream;
|
|
370
367
|
}
|
|
@@ -596,7 +593,7 @@ export async function appendFile(path, data, _options) {
|
|
|
596
593
|
const options = normalizeOptions(_options, 'utf8', 'a', 0o644);
|
|
597
594
|
const flag = parseFlag(options.flag);
|
|
598
595
|
if (!isAppendable(flag)) {
|
|
599
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending
|
|
596
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
|
|
600
597
|
}
|
|
601
598
|
if (typeof data != 'string' && !options.encoding) {
|
|
602
599
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
@@ -1086,7 +1083,7 @@ export async function copyFile(src, dest, mode) {
|
|
|
1086
1083
|
src = normalizePath(src);
|
|
1087
1084
|
dest = normalizePath(dest);
|
|
1088
1085
|
if (mode && mode & constants.COPYFILE_EXCL && (await exists.call(this, dest))) {
|
|
1089
|
-
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists
|
|
1086
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists', dest, 'copyFile');
|
|
1090
1087
|
}
|
|
1091
1088
|
await writeFile.call(this, dest, await readFile.call(this, src));
|
|
1092
1089
|
emitChange(this, 'rename', dest.toString());
|
|
@@ -1121,7 +1118,7 @@ export async function cp(source, destination, opts) {
|
|
|
1121
1118
|
destination = normalizePath(destination);
|
|
1122
1119
|
const srcStats = await lstat.call(this, source); // Use lstat to follow symlinks if not dereferencing
|
|
1123
1120
|
if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && (await exists.call(this, destination))) {
|
|
1124
|
-
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists
|
|
1121
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists', destination, 'cp');
|
|
1125
1122
|
}
|
|
1126
1123
|
switch (srcStats.mode & constants.S_IFMT) {
|
|
1127
1124
|
case constants.S_IFDIR: {
|
package/dist/vfs/shared.js
CHANGED
|
@@ -47,7 +47,7 @@ export function mount(mountPoint, fs) {
|
|
|
47
47
|
mountPoint = '/' + mountPoint;
|
|
48
48
|
mountPoint = resolve(mountPoint);
|
|
49
49
|
if (mounts.has(mountPoint)) {
|
|
50
|
-
throw err(new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use
|
|
50
|
+
throw err(new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use'));
|
|
51
51
|
}
|
|
52
52
|
fs._mountPoint = mountPoint;
|
|
53
53
|
mounts.set(mountPoint, fs);
|
|
@@ -63,7 +63,7 @@ export function umount(mountPoint) {
|
|
|
63
63
|
mountPoint = '/' + mountPoint;
|
|
64
64
|
mountPoint = resolve(mountPoint);
|
|
65
65
|
if (!mounts.has(mountPoint)) {
|
|
66
|
-
warn(mountPoint + ' is already unmounted
|
|
66
|
+
warn(mountPoint + ' is already unmounted');
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
mounts.delete(mountPoint);
|
package/dist/vfs/streams.d.ts
CHANGED
|
@@ -2,14 +2,14 @@ import type * as fs from 'node:fs';
|
|
|
2
2
|
import type { Callback } from '../utils.js';
|
|
3
3
|
import { Readable, Writable } from 'readable-stream';
|
|
4
4
|
export declare class ReadStream extends Readable implements fs.ReadStream {
|
|
5
|
-
close(callback?: Callback<[void], null>)
|
|
5
|
+
close: (callback?: Callback<[void], null>) => void;
|
|
6
6
|
wrap(oldStream: NodeJS.ReadableStream): this;
|
|
7
7
|
bytesRead: number;
|
|
8
8
|
path: string | Buffer;
|
|
9
9
|
pending: boolean;
|
|
10
10
|
}
|
|
11
11
|
export declare class WriteStream extends Writable implements fs.WriteStream {
|
|
12
|
-
close(callback?: Callback<[void], null>)
|
|
12
|
+
close: (callback?: Callback<[void], null>) => void;
|
|
13
13
|
bytesWritten: number;
|
|
14
14
|
path: string | Buffer;
|
|
15
15
|
pending: boolean;
|
package/dist/vfs/streams.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Readable, Writable } from 'readable-stream';
|
|
2
2
|
import { Errno, ErrnoError } from '../internal/error.js';
|
|
3
3
|
export class ReadStream extends Readable {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
this.close = (callback = () => null) => {
|
|
7
|
+
try {
|
|
8
|
+
super.destroy();
|
|
9
|
+
super.emit('close');
|
|
10
|
+
callback(null);
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
callback(new ErrnoError(Errno.EIO, err.toString()));
|
|
14
|
+
}
|
|
15
|
+
};
|
|
13
16
|
}
|
|
14
17
|
wrap(oldStream) {
|
|
15
18
|
super.wrap(oldStream);
|
|
@@ -17,14 +20,17 @@ export class ReadStream extends Readable {
|
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
export class WriteStream extends Writable {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.close = (callback = () => null) => {
|
|
26
|
+
try {
|
|
27
|
+
super.destroy();
|
|
28
|
+
super.emit('close');
|
|
29
|
+
callback(null);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
callback(new ErrnoError(Errno.EIO, err.toString()));
|
|
33
|
+
}
|
|
34
|
+
};
|
|
29
35
|
}
|
|
30
36
|
}
|
package/dist/vfs/sync.js
CHANGED
|
@@ -259,7 +259,7 @@ export function readFileSync(path, _options = {}) {
|
|
|
259
259
|
const options = normalizeOptions(_options, null, 'r', 0o644);
|
|
260
260
|
const flag = parseFlag(options.flag);
|
|
261
261
|
if (!isReadable(flag)) {
|
|
262
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading
|
|
262
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading');
|
|
263
263
|
}
|
|
264
264
|
const data = Buffer.from(_readFileSync.call(this, typeof path == 'number' ? fd2file(path).path : path, options.flag, false));
|
|
265
265
|
return options.encoding ? data.toString(options.encoding) : data;
|
|
@@ -271,7 +271,7 @@ export function writeFileSync(path, data, _options = {}) {
|
|
|
271
271
|
const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
|
|
272
272
|
const flag = parseFlag(options.flag);
|
|
273
273
|
if (!isWriteable(flag)) {
|
|
274
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed to writeFile must allow for writing
|
|
274
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to writeFile must allow for writing');
|
|
275
275
|
}
|
|
276
276
|
if (typeof data != 'string' && !options.encoding) {
|
|
277
277
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
@@ -309,7 +309,7 @@ export function appendFileSync(filename, data, _options = {}) {
|
|
|
309
309
|
const options = normalizeOptions(_options, 'utf8', 'a+', 0o644);
|
|
310
310
|
const flag = parseFlag(options.flag);
|
|
311
311
|
if (!isAppendable(flag)) {
|
|
312
|
-
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending
|
|
312
|
+
throw new ErrnoError(Errno.EINVAL, 'Flag passed to appendFile must allow for appending');
|
|
313
313
|
}
|
|
314
314
|
if (typeof data != 'string' && !options.encoding) {
|
|
315
315
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
@@ -743,7 +743,7 @@ export function copyFileSync(source, destination, flags) {
|
|
|
743
743
|
source = normalizePath(source);
|
|
744
744
|
destination = normalizePath(destination);
|
|
745
745
|
if (flags && flags & constants.COPYFILE_EXCL && existsSync(destination)) {
|
|
746
|
-
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists
|
|
746
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists', destination, 'copyFile');
|
|
747
747
|
}
|
|
748
748
|
writeFileSync.call(this, destination, readFileSync(source));
|
|
749
749
|
emitChange(this, 'rename', destination.toString());
|
|
@@ -810,7 +810,7 @@ export function cpSync(source, destination, opts) {
|
|
|
810
810
|
destination = normalizePath(destination);
|
|
811
811
|
const srcStats = lstatSync.call(this, source); // Use lstat to follow symlinks if not dereferencing
|
|
812
812
|
if ((opts === null || opts === void 0 ? void 0 : opts.errorOnExist) && existsSync.call(this, destination)) {
|
|
813
|
-
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists
|
|
813
|
+
throw new ErrnoError(Errno.EEXIST, 'Destination file or directory already exists', destination, 'cp');
|
|
814
814
|
}
|
|
815
815
|
switch (srcStats.mode & constants.S_IFMT) {
|
|
816
816
|
case constants.S_IFDIR:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.2",
|
|
4
4
|
"description": "A filesystem, anywhere",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"tests",
|
|
24
|
+
"types",
|
|
24
25
|
"license.md",
|
|
25
26
|
"eslint.shared.js"
|
|
26
27
|
],
|
|
@@ -66,11 +67,10 @@
|
|
|
66
67
|
},
|
|
67
68
|
"dependencies": {
|
|
68
69
|
"@types/node": "^22.10.1",
|
|
69
|
-
"@types/readable-stream": "^4.0.10",
|
|
70
70
|
"buffer": "^6.0.3",
|
|
71
71
|
"eventemitter3": "^5.0.1",
|
|
72
72
|
"readable-stream": "^4.5.2",
|
|
73
|
-
"utilium": "^1.3.
|
|
73
|
+
"utilium": "^1.3.3"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@eslint/js": "^9.8.0",
|
package/readme.md
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Overview
|
|
3
|
-
---
|
|
4
|
-
|
|
5
1
|
# ZenFS
|
|
6
2
|
|
|
7
3
|
ZenFS is a cross-platform library that emulates the [NodeJS filesystem API](http://nodejs.org/api/fs.html). It works using a system of backends, which are used by ZenFS to store and retrieve data. ZenFS can also integrate with other tools.
|
|
@@ -15,6 +11,7 @@ ZenFS is modular and extensible. The core includes some built-in backends:
|
|
|
15
11
|
- `Fetch`: Downloads files over HTTP with the `fetch` API
|
|
16
12
|
- `Port`: Interacts with a remote over a `MessagePort`-like interface (e.g. a worker)
|
|
17
13
|
- `Passthrough`: Use an existing `node:fs` interface with ZenFS
|
|
14
|
+
- `SingleBuffer`: A backend contained within a single buffer. Can be used for synchronous multi-threaded operations using `SharedArrayBuffer`
|
|
18
15
|
|
|
19
16
|
ZenFS supports a number of other backends. Many are provided as separate packages under `@zenfs`. More backends can be defined by separate libraries by extending the `FileSystem` class and providing a `Backend` object.
|
|
20
17
|
|
|
@@ -6,7 +6,7 @@ import { suite, test } from 'node:test';
|
|
|
6
6
|
import assert from 'node:assert/strict';
|
|
7
7
|
|
|
8
8
|
suite('LockFS mutex', () => {
|
|
9
|
-
const fs = new (Mutexed(StoreFS))(new InMemoryStore('test'));
|
|
9
|
+
const fs = new (Mutexed(StoreFS))(new InMemoryStore(0x10000, 'test'));
|
|
10
10
|
fs._fs.checkRootSync();
|
|
11
11
|
|
|
12
12
|
test('lock/unlock', () => {
|
package/tests/fetch/server.js
CHANGED
|
@@ -12,7 +12,7 @@ try {
|
|
|
12
12
|
execSync(`npm exec make-index -- ${data} --output ${tmp}/index.json --quiet`, { stdio: 'inherit' });
|
|
13
13
|
} catch (e) {
|
|
14
14
|
if (e.signal == 'SIGINT') {
|
|
15
|
-
console.log('Aborted whilst creating index
|
|
15
|
+
console.log('Aborted whilst creating index');
|
|
16
16
|
process.exit(0);
|
|
17
17
|
} else {
|
|
18
18
|
console.error('Index creation failed: ' + e.message);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
|
-
import { ErrnoError } from '../../dist/index.js';
|
|
4
3
|
import { fs } from '../common.js';
|
|
5
4
|
|
|
6
5
|
const testDir = 'test-dir';
|
|
@@ -25,15 +24,10 @@ suite('Directories', () => {
|
|
|
25
24
|
await assert.rejects(fs.promises.mkdir('/one', 0o755), /EEXIST/);
|
|
26
25
|
});
|
|
27
26
|
|
|
28
|
-
test('mkdirSync', () => fs.
|
|
27
|
+
test('mkdirSync', async () => await fs.promises.mkdir('/two', 0o000));
|
|
29
28
|
|
|
30
29
|
test('mkdir, nested', async () => {
|
|
31
|
-
|
|
32
|
-
await fs.promises.mkdir('/nested/dir');
|
|
33
|
-
} catch (error: any) {
|
|
34
|
-
assert(error instanceof ErrnoError);
|
|
35
|
-
assert.equal(error.code, 'ENOENT');
|
|
36
|
-
}
|
|
30
|
+
assert.rejects(fs.promises.mkdir('/nested/dir'), { code: 'ENOENT', path: '/nested' });
|
|
37
31
|
assert(!(await fs.promises.exists('/nested/dir')));
|
|
38
32
|
});
|
|
39
33
|
|
|
@@ -61,69 +55,27 @@ suite('Directories', () => {
|
|
|
61
55
|
assert.equal(fs.statSync('/recursiveS/A/B/C/D').mode, fs.constants.S_IFDIR | 0o777);
|
|
62
56
|
});
|
|
63
57
|
|
|
64
|
-
test('readdirSync without permission', () => {
|
|
65
|
-
try {
|
|
66
|
-
fs.readdirSync('/two');
|
|
67
|
-
} catch (error: any) {
|
|
68
|
-
assert(error instanceof ErrnoError);
|
|
69
|
-
assert.equal(error.code, 'EACCES');
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
58
|
test('rmdir (non-empty)', async () => {
|
|
74
59
|
await fs.promises.mkdir('/rmdirTest');
|
|
75
60
|
await fs.promises.mkdir('/rmdirTest/rmdirTest2');
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
await fs.promises.rmdir('/rmdirTest');
|
|
79
|
-
} catch (error: any) {
|
|
80
|
-
assert(error instanceof ErrnoError);
|
|
81
|
-
assert.equal(error.code, 'ENOTEMPTY');
|
|
82
|
-
}
|
|
62
|
+
assert.rejects(fs.promises.rmdir('/rmdirTest'), { code: 'ENOTEMPTY' });
|
|
83
63
|
});
|
|
84
64
|
|
|
85
65
|
test('readdirSync on file', () => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
fs.readdirSync('a.js');
|
|
90
|
-
} catch (error: any) {
|
|
91
|
-
assert(error instanceof ErrnoError);
|
|
92
|
-
wasThrown = true;
|
|
93
|
-
assert.equal(error.code, 'ENOTDIR');
|
|
94
|
-
}
|
|
95
|
-
assert(wasThrown);
|
|
66
|
+
assert.throws(() => fs.readdirSync('a.js'), { code: 'ENOTDIR' });
|
|
96
67
|
});
|
|
97
68
|
|
|
98
|
-
test('readdir on file',
|
|
99
|
-
|
|
100
|
-
await fs.promises.readdir('a.js');
|
|
101
|
-
} catch (error: any) {
|
|
102
|
-
assert(error instanceof ErrnoError);
|
|
103
|
-
assert.equal(error.code, 'ENOTDIR');
|
|
104
|
-
}
|
|
69
|
+
test('readdir on file', () => {
|
|
70
|
+
assert.rejects(fs.promises.readdir('a.js'), { code: 'ENOTDIR' });
|
|
105
71
|
});
|
|
106
72
|
|
|
107
73
|
test('readdirSync on non-existant directory', () => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
assert(error instanceof ErrnoError);
|
|
114
|
-
wasThrown = true;
|
|
115
|
-
assert.equal(error.code, 'ENOENT');
|
|
116
|
-
}
|
|
117
|
-
assert(wasThrown);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
test('readdir on non-existant directory', async () => {
|
|
121
|
-
try {
|
|
122
|
-
await fs.promises.readdir('/does/not/exist');
|
|
123
|
-
} catch (error: any) {
|
|
124
|
-
assert(error instanceof ErrnoError);
|
|
125
|
-
assert.equal(error.code, 'ENOENT');
|
|
126
|
-
}
|
|
74
|
+
assert.throws(() => fs.readdirSync('/does/not/exist'), { code: 'ENOENT' });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('readdir on non-existant directory', () => {
|
|
78
|
+
assert.rejects(fs.promises.readdir('/does/not/exist'), { code: 'ENOENT' });
|
|
127
79
|
});
|
|
128
80
|
|
|
129
81
|
test('rm recursively asynchronously', async () => {
|
package/tests/fs/errors.test.ts
CHANGED
|
@@ -12,7 +12,7 @@ suite('Error messages', () => {
|
|
|
12
12
|
assert.equal(error.bufferSize(), 4 + JSON.stringify(error.toJSON()).length);
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
const missing = { path };
|
|
15
|
+
const missing = { path, code: 'ENOENT' };
|
|
16
16
|
const existing = { path: existingFile };
|
|
17
17
|
|
|
18
18
|
test('stat', () => assert.rejects(() => fs.promises.stat(path), missing));
|
package/tests/fs/stat.test.ts
CHANGED
|
@@ -38,13 +38,9 @@ suite('Stats', () => {
|
|
|
38
38
|
test('hasAccess for non-root access', () => {
|
|
39
39
|
const newFile = 'new.txt';
|
|
40
40
|
|
|
41
|
-
fs.writeFileSync(newFile, 'hello', {
|
|
42
|
-
mode: 0o640, // allow group access
|
|
43
|
-
});
|
|
41
|
+
fs.writeFileSync(newFile, 'hello', { mode: 0o640 });
|
|
44
42
|
|
|
45
|
-
const prevCredentials = {
|
|
46
|
-
...credentials,
|
|
47
|
-
};
|
|
43
|
+
const prevCredentials = { ...credentials };
|
|
48
44
|
const uid = 33;
|
|
49
45
|
const nonRootCredentials = {
|
|
50
46
|
uid,
|