@zenfs/core 1.7.2 → 1.8.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/backend.js +3 -4
- package/dist/backends/fetch.d.ts +17 -18
- package/dist/backends/fetch.js +95 -58
- package/dist/backends/index.d.ts +2 -1
- package/dist/backends/index.js +2 -1
- package/dist/backends/memory.d.ts +1 -1
- package/dist/backends/overlay.d.ts +7 -2
- package/dist/backends/overlay.js +32 -9
- package/dist/backends/passthrough.d.ts +4 -0
- package/dist/backends/passthrough.js +128 -0
- package/dist/backends/port/fs.d.ts +9 -44
- package/dist/backends/port/fs.js +93 -116
- package/dist/backends/port/rpc.d.ts +8 -5
- package/dist/backends/port/rpc.js +9 -7
- package/dist/backends/store/file_index.d.ts +38 -0
- package/dist/backends/store/file_index.js +76 -0
- package/dist/backends/store/fs.d.ts +55 -34
- package/dist/backends/store/fs.js +417 -233
- package/dist/backends/store/index_fs.d.ts +34 -0
- package/dist/backends/store/index_fs.js +67 -0
- package/dist/backends/store/inode.d.ts +26 -8
- package/dist/backends/store/inode.js +92 -91
- package/dist/backends/store/simple.d.ts +20 -20
- package/dist/backends/store/simple.js +3 -4
- package/dist/backends/store/store.d.ts +12 -12
- package/dist/backends/store/store.js +4 -6
- package/dist/devices.d.ts +11 -10
- package/dist/devices.js +15 -11
- package/dist/file.d.ts +111 -7
- package/dist/file.js +319 -71
- package/dist/filesystem.d.ts +22 -4
- package/dist/mixins/mutexed.d.ts +7 -2
- package/dist/mixins/mutexed.js +56 -0
- package/dist/mixins/sync.d.ts +1 -1
- package/dist/stats.d.ts +12 -6
- package/dist/stats.js +14 -6
- package/dist/utils.d.ts +17 -3
- package/dist/utils.js +32 -10
- package/dist/vfs/constants.d.ts +2 -2
- package/dist/vfs/constants.js +2 -2
- package/dist/vfs/dir.js +3 -1
- package/dist/vfs/index.js +4 -1
- package/dist/vfs/promises.js +31 -11
- package/dist/vfs/shared.js +2 -0
- package/dist/vfs/sync.js +25 -13
- package/dist/vfs/types.d.ts +15 -0
- package/package.json +2 -3
- package/readme.md +2 -2
- package/scripts/test.js +73 -11
- package/tests/common/mutex.test.ts +1 -1
- package/tests/fetch/run.sh +16 -0
- package/tests/fetch/server.ts +49 -0
- package/tests/fetch/setup.ts +13 -0
- package/tests/fs/read.test.ts +10 -10
- package/tests/fs/times.test.ts +2 -2
- package/tests/setup/index.ts +38 -0
- package/tests/setup/port.ts +15 -0
- package/dist/backends/file_index.d.ts +0 -63
- package/dist/backends/file_index.js +0 -163
- package/tests/common/async.test.ts +0 -31
- package/tests/setup/cow+fetch.ts +0 -45
- /package/tests/fs/{appendFile.test.ts → append.test.ts} +0 -0
package/dist/file.js
CHANGED
|
@@ -3,6 +3,7 @@ import './polyfills.js';
|
|
|
3
3
|
import { _chown, Stats } from './stats.js';
|
|
4
4
|
import { config } from './vfs/config.js';
|
|
5
5
|
import * as c from './vfs/constants.js';
|
|
6
|
+
const maxByteLength = 0x100000; // 1 MiB
|
|
6
7
|
const validFlags = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
|
|
7
8
|
export function parseFlag(flag) {
|
|
8
9
|
if (typeof flag === 'number') {
|
|
@@ -146,7 +147,7 @@ export class PreloadFile extends File {
|
|
|
146
147
|
/**
|
|
147
148
|
* A buffer containing the entire contents of the file.
|
|
148
149
|
*/
|
|
149
|
-
_buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength
|
|
150
|
+
_buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength }))) {
|
|
150
151
|
super(fs, path);
|
|
151
152
|
this.flag = flag;
|
|
152
153
|
this.stats = stats;
|
|
@@ -171,9 +172,10 @@ export class PreloadFile extends File {
|
|
|
171
172
|
if (this.stats.size == _buffer.byteLength) {
|
|
172
173
|
return;
|
|
173
174
|
}
|
|
174
|
-
if (
|
|
175
|
-
throw new
|
|
175
|
+
if (!isWriteable(this.flag)) {
|
|
176
|
+
throw new ErrnoError(Errno.EIO, `Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`, path);
|
|
176
177
|
}
|
|
178
|
+
this.stats.size = _buffer.byteLength;
|
|
177
179
|
this.dirty = true;
|
|
178
180
|
}
|
|
179
181
|
/**
|
|
@@ -201,36 +203,32 @@ export class PreloadFile extends File {
|
|
|
201
203
|
this._position = value;
|
|
202
204
|
}
|
|
203
205
|
async sync() {
|
|
204
|
-
if (this.closed)
|
|
205
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
206
|
-
|
|
207
|
-
if (!this.dirty) {
|
|
206
|
+
if (this.closed)
|
|
207
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
208
|
+
if (!this.dirty)
|
|
208
209
|
return;
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
if (!this.fs.metadata().readonly)
|
|
211
|
+
await this.fs.sync(this.path, this._buffer, this.stats);
|
|
211
212
|
this.dirty = false;
|
|
212
213
|
}
|
|
213
214
|
syncSync() {
|
|
214
|
-
if (this.closed)
|
|
215
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
216
|
-
|
|
217
|
-
if (!this.dirty) {
|
|
215
|
+
if (this.closed)
|
|
216
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
217
|
+
if (!this.dirty)
|
|
218
218
|
return;
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
if (!this.fs.metadata().readonly)
|
|
220
|
+
this.fs.syncSync(this.path, this._buffer, this.stats);
|
|
221
221
|
this.dirty = false;
|
|
222
222
|
}
|
|
223
223
|
async close() {
|
|
224
|
-
if (this.closed)
|
|
225
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
226
|
-
}
|
|
224
|
+
if (this.closed)
|
|
225
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
227
226
|
await this.sync();
|
|
228
227
|
this.dispose();
|
|
229
228
|
}
|
|
230
229
|
closeSync() {
|
|
231
|
-
if (this.closed)
|
|
232
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
233
|
-
}
|
|
230
|
+
if (this.closed)
|
|
231
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
234
232
|
this.syncSync();
|
|
235
233
|
this.dispose();
|
|
236
234
|
}
|
|
@@ -238,34 +236,26 @@ export class PreloadFile extends File {
|
|
|
238
236
|
* Cleans up. This will *not* sync the file data to the FS
|
|
239
237
|
*/
|
|
240
238
|
dispose(force) {
|
|
241
|
-
if (this.closed)
|
|
242
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
243
|
-
}
|
|
239
|
+
if (this.closed)
|
|
240
|
+
throw ErrnoError.With('EBADF', this.path, 'dispose');
|
|
244
241
|
if (this.dirty && !force) {
|
|
245
|
-
throw ErrnoError.With('EBUSY', this.path, '
|
|
242
|
+
throw ErrnoError.With('EBUSY', this.path, 'dispose');
|
|
246
243
|
}
|
|
247
|
-
// @ts-expect-error 2790
|
|
248
|
-
delete this._buffer;
|
|
249
|
-
// @ts-expect-error 2790
|
|
250
|
-
delete this.stats;
|
|
251
244
|
this.closed = true;
|
|
252
245
|
}
|
|
253
246
|
stat() {
|
|
254
|
-
if (this.closed)
|
|
255
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
256
|
-
}
|
|
247
|
+
if (this.closed)
|
|
248
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
257
249
|
return Promise.resolve(new Stats(this.stats));
|
|
258
250
|
}
|
|
259
251
|
statSync() {
|
|
260
|
-
if (this.closed)
|
|
261
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
262
|
-
}
|
|
252
|
+
if (this.closed)
|
|
253
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
263
254
|
return new Stats(this.stats);
|
|
264
255
|
}
|
|
265
256
|
_truncate(length) {
|
|
266
|
-
if (this.closed)
|
|
267
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
268
|
-
}
|
|
257
|
+
if (this.closed)
|
|
258
|
+
throw ErrnoError.With('EBADF', this.path, 'truncate');
|
|
269
259
|
this.dirty = true;
|
|
270
260
|
if (!isWriteable(this.flag)) {
|
|
271
261
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
@@ -291,10 +281,9 @@ export class PreloadFile extends File {
|
|
|
291
281
|
if (config.syncImmediately)
|
|
292
282
|
this.syncSync();
|
|
293
283
|
}
|
|
294
|
-
_write(buffer, offset = 0, length =
|
|
295
|
-
if (this.closed)
|
|
296
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
297
|
-
}
|
|
284
|
+
_write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
285
|
+
if (this.closed)
|
|
286
|
+
throw ErrnoError.With('EBADF', this.path, 'write');
|
|
298
287
|
if (!isWriteable(this.flag)) {
|
|
299
288
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
300
289
|
}
|
|
@@ -316,7 +305,7 @@ export class PreloadFile extends File {
|
|
|
316
305
|
}
|
|
317
306
|
else {
|
|
318
307
|
// Extend the buffer!
|
|
319
|
-
const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength
|
|
308
|
+
const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength }));
|
|
320
309
|
newBuffer.set(this._buffer);
|
|
321
310
|
this._buffer = newBuffer;
|
|
322
311
|
}
|
|
@@ -350,16 +339,15 @@ export class PreloadFile extends File {
|
|
|
350
339
|
* If position is null, the data will be written at the current position.
|
|
351
340
|
* @returns bytes written
|
|
352
341
|
*/
|
|
353
|
-
writeSync(buffer, offset
|
|
342
|
+
writeSync(buffer, offset, length, position) {
|
|
354
343
|
const bytesWritten = this._write(buffer, offset, length, position);
|
|
355
344
|
if (config.syncImmediately)
|
|
356
345
|
this.syncSync();
|
|
357
346
|
return bytesWritten;
|
|
358
347
|
}
|
|
359
|
-
_read(buffer, offset = 0, length =
|
|
360
|
-
if (this.closed)
|
|
361
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
362
|
-
}
|
|
348
|
+
_read(buffer, offset = 0, length = buffer.byteLength - offset, position) {
|
|
349
|
+
if (this.closed)
|
|
350
|
+
throw ErrnoError.With('EBADF', this.path, 'read');
|
|
363
351
|
if (!isReadable(this.flag)) {
|
|
364
352
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
|
|
365
353
|
}
|
|
@@ -378,7 +366,8 @@ export class PreloadFile extends File {
|
|
|
378
366
|
// No copy/read. Return immediately for better performance
|
|
379
367
|
return bytesRead;
|
|
380
368
|
}
|
|
381
|
-
|
|
369
|
+
const slice = this._buffer.slice(position, end);
|
|
370
|
+
new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength).set(slice, offset);
|
|
382
371
|
return bytesRead;
|
|
383
372
|
}
|
|
384
373
|
/**
|
|
@@ -411,65 +400,61 @@ export class PreloadFile extends File {
|
|
|
411
400
|
return bytesRead;
|
|
412
401
|
}
|
|
413
402
|
async chmod(mode) {
|
|
414
|
-
if (this.closed)
|
|
415
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
416
|
-
}
|
|
403
|
+
if (this.closed)
|
|
404
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
417
405
|
this.dirty = true;
|
|
418
406
|
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
419
407
|
if (config.syncImmediately || mode > c.S_IFMT)
|
|
420
408
|
await this.sync();
|
|
421
409
|
}
|
|
422
410
|
chmodSync(mode) {
|
|
423
|
-
if (this.closed)
|
|
424
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
425
|
-
}
|
|
411
|
+
if (this.closed)
|
|
412
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
426
413
|
this.dirty = true;
|
|
427
414
|
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
428
415
|
if (config.syncImmediately || mode > c.S_IFMT)
|
|
429
416
|
this.syncSync();
|
|
430
417
|
}
|
|
431
418
|
async chown(uid, gid) {
|
|
432
|
-
if (this.closed)
|
|
433
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
434
|
-
}
|
|
419
|
+
if (this.closed)
|
|
420
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
435
421
|
this.dirty = true;
|
|
436
422
|
_chown(this.stats, uid, gid);
|
|
437
423
|
if (config.syncImmediately)
|
|
438
424
|
await this.sync();
|
|
439
425
|
}
|
|
440
426
|
chownSync(uid, gid) {
|
|
441
|
-
if (this.closed)
|
|
442
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
443
|
-
}
|
|
427
|
+
if (this.closed)
|
|
428
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
444
429
|
this.dirty = true;
|
|
445
430
|
_chown(this.stats, uid, gid);
|
|
446
431
|
if (config.syncImmediately)
|
|
447
432
|
this.syncSync();
|
|
448
433
|
}
|
|
449
434
|
async utimes(atime, mtime) {
|
|
450
|
-
if (this.closed)
|
|
451
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
452
|
-
}
|
|
435
|
+
if (this.closed)
|
|
436
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
453
437
|
this.dirty = true;
|
|
454
|
-
this.stats.
|
|
455
|
-
this.stats.
|
|
438
|
+
this.stats.atimeMs = atime;
|
|
439
|
+
this.stats.mtimeMs = mtime;
|
|
456
440
|
if (config.syncImmediately)
|
|
457
441
|
await this.sync();
|
|
458
442
|
}
|
|
459
443
|
utimesSync(atime, mtime) {
|
|
460
|
-
if (this.closed)
|
|
461
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
462
|
-
}
|
|
444
|
+
if (this.closed)
|
|
445
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
463
446
|
this.dirty = true;
|
|
464
|
-
this.stats.
|
|
465
|
-
this.stats.
|
|
447
|
+
this.stats.atimeMs = atime;
|
|
448
|
+
this.stats.mtimeMs = mtime;
|
|
466
449
|
if (config.syncImmediately)
|
|
467
450
|
this.syncSync();
|
|
468
451
|
}
|
|
469
452
|
}
|
|
470
453
|
/**
|
|
471
454
|
* For the file systems which do not sync to anything.
|
|
455
|
+
* @deprecated
|
|
472
456
|
*/
|
|
457
|
+
/* node:coverage disable */
|
|
473
458
|
export class NoSyncFile extends PreloadFile {
|
|
474
459
|
sync() {
|
|
475
460
|
return Promise.resolve();
|
|
@@ -480,3 +465,266 @@ export class NoSyncFile extends PreloadFile {
|
|
|
480
465
|
}
|
|
481
466
|
closeSync() { }
|
|
482
467
|
}
|
|
468
|
+
/* node:coverage enable */
|
|
469
|
+
/**
|
|
470
|
+
* An implementation of `File` that uses the FS
|
|
471
|
+
*/
|
|
472
|
+
export class LazyFile extends File {
|
|
473
|
+
/**
|
|
474
|
+
* Get the current file position.
|
|
475
|
+
*
|
|
476
|
+
* We emulate the following bug mentioned in the Node documentation:
|
|
477
|
+
*
|
|
478
|
+
* On Linux, positional writes don't work when the file is opened in append mode.
|
|
479
|
+
* The kernel ignores the position argument and always appends the data to the end of the file.
|
|
480
|
+
* @returns The current file position.
|
|
481
|
+
*/
|
|
482
|
+
get position() {
|
|
483
|
+
return isAppendable(this.flag) ? this.stats.size : this._position;
|
|
484
|
+
}
|
|
485
|
+
set position(value) {
|
|
486
|
+
this._position = value;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Creates a file with `path` and, optionally, the given contents.
|
|
490
|
+
* Note that, if contents is specified, it will be mutated by the file.
|
|
491
|
+
*/
|
|
492
|
+
constructor(fs, path, flag, stats) {
|
|
493
|
+
super(fs, path);
|
|
494
|
+
this.flag = flag;
|
|
495
|
+
this.stats = stats;
|
|
496
|
+
/**
|
|
497
|
+
* Current position
|
|
498
|
+
*/
|
|
499
|
+
this._position = 0;
|
|
500
|
+
/**
|
|
501
|
+
* Whether the file has changes which have not been written to the FS
|
|
502
|
+
*/
|
|
503
|
+
this.dirty = false;
|
|
504
|
+
/**
|
|
505
|
+
* Whether the file is open or closed
|
|
506
|
+
*/
|
|
507
|
+
this.closed = false;
|
|
508
|
+
this.dirty = true;
|
|
509
|
+
}
|
|
510
|
+
async sync() {
|
|
511
|
+
if (this.closed)
|
|
512
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
513
|
+
if (!this.dirty)
|
|
514
|
+
return;
|
|
515
|
+
if (!this.fs.metadata().readonly)
|
|
516
|
+
await this.fs.sync(this.path, undefined, this.stats);
|
|
517
|
+
this.dirty = false;
|
|
518
|
+
}
|
|
519
|
+
syncSync() {
|
|
520
|
+
if (this.closed)
|
|
521
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
522
|
+
if (!this.dirty)
|
|
523
|
+
return;
|
|
524
|
+
if (!this.fs.metadata().readonly)
|
|
525
|
+
this.fs.syncSync(this.path, undefined, this.stats);
|
|
526
|
+
this.dirty = false;
|
|
527
|
+
}
|
|
528
|
+
async close() {
|
|
529
|
+
if (this.closed)
|
|
530
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
531
|
+
await this.sync();
|
|
532
|
+
this.dispose();
|
|
533
|
+
}
|
|
534
|
+
closeSync() {
|
|
535
|
+
if (this.closed)
|
|
536
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
537
|
+
this.syncSync();
|
|
538
|
+
this.dispose();
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Cleans up. This will *not* sync the file data to the FS
|
|
542
|
+
*/
|
|
543
|
+
dispose(force) {
|
|
544
|
+
if (this.closed)
|
|
545
|
+
throw ErrnoError.With('EBADF', this.path, 'dispose');
|
|
546
|
+
if (this.dirty && !force)
|
|
547
|
+
throw ErrnoError.With('EBUSY', this.path, 'dispose');
|
|
548
|
+
this.closed = true;
|
|
549
|
+
}
|
|
550
|
+
stat() {
|
|
551
|
+
if (this.closed)
|
|
552
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
553
|
+
return Promise.resolve(new Stats(this.stats));
|
|
554
|
+
}
|
|
555
|
+
statSync() {
|
|
556
|
+
if (this.closed)
|
|
557
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
558
|
+
return new Stats(this.stats);
|
|
559
|
+
}
|
|
560
|
+
async truncate(length) {
|
|
561
|
+
if (this.closed)
|
|
562
|
+
throw ErrnoError.With('EBADF', this.path, 'truncate');
|
|
563
|
+
this.dirty = true;
|
|
564
|
+
if (!isWriteable(this.flag)) {
|
|
565
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
566
|
+
}
|
|
567
|
+
this.stats.mtimeMs = Date.now();
|
|
568
|
+
this.stats.size = length;
|
|
569
|
+
if (config.syncImmediately)
|
|
570
|
+
await this.sync();
|
|
571
|
+
}
|
|
572
|
+
truncateSync(length) {
|
|
573
|
+
if (this.closed)
|
|
574
|
+
throw ErrnoError.With('EBADF', this.path, 'truncate');
|
|
575
|
+
this.dirty = true;
|
|
576
|
+
if (!isWriteable(this.flag)) {
|
|
577
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
578
|
+
}
|
|
579
|
+
this.stats.mtimeMs = Date.now();
|
|
580
|
+
this.stats.size = length;
|
|
581
|
+
if (config.syncImmediately)
|
|
582
|
+
this.syncSync();
|
|
583
|
+
}
|
|
584
|
+
prepareWrite(buffer, offset, length, position) {
|
|
585
|
+
if (this.closed)
|
|
586
|
+
throw ErrnoError.With('EBADF', this.path, 'write');
|
|
587
|
+
if (!isWriteable(this.flag)) {
|
|
588
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
589
|
+
}
|
|
590
|
+
this.dirty = true;
|
|
591
|
+
const end = position + length;
|
|
592
|
+
const slice = buffer.slice(offset, offset + length);
|
|
593
|
+
if (end > this.stats.size) {
|
|
594
|
+
this.stats.size = end;
|
|
595
|
+
}
|
|
596
|
+
this.stats.mtimeMs = Date.now();
|
|
597
|
+
this._position = position + slice.byteLength;
|
|
598
|
+
return slice;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Write buffer to the file.
|
|
602
|
+
* @param buffer Uint8Array containing the data to write to the file.
|
|
603
|
+
* @param offset Offset in the buffer to start reading data from.
|
|
604
|
+
* @param length The amount of bytes to write to the file.
|
|
605
|
+
* @param position Offset from the beginning of the file where this data should be written.
|
|
606
|
+
* If position is null, the data will be written at the current position.
|
|
607
|
+
*/
|
|
608
|
+
async write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
609
|
+
const slice = this.prepareWrite(buffer, offset, length, position);
|
|
610
|
+
await this.fs.write(this.path, slice, offset);
|
|
611
|
+
if (config.syncImmediately)
|
|
612
|
+
await this.sync();
|
|
613
|
+
return slice.byteLength;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Write buffer to the file.
|
|
617
|
+
* @param buffer Uint8Array containing the data to write to the file.
|
|
618
|
+
* @param offset Offset in the buffer to start reading data from.
|
|
619
|
+
* @param length The amount of bytes to write to the file.
|
|
620
|
+
* @param position Offset from the beginning of the file where this data should be written.
|
|
621
|
+
* If position is null, the data will be written at the current position.
|
|
622
|
+
* @returns bytes written
|
|
623
|
+
*/
|
|
624
|
+
writeSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
625
|
+
const slice = this.prepareWrite(buffer, offset, length, position);
|
|
626
|
+
this.fs.writeSync(this.path, slice, offset);
|
|
627
|
+
if (config.syncImmediately)
|
|
628
|
+
this.syncSync();
|
|
629
|
+
return slice.byteLength;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Computes position information for reading
|
|
633
|
+
*/
|
|
634
|
+
prepareRead(length, position) {
|
|
635
|
+
if (this.closed)
|
|
636
|
+
throw ErrnoError.With('EBADF', this.path, 'read');
|
|
637
|
+
if (!isReadable(this.flag))
|
|
638
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
|
|
639
|
+
if (config.updateOnRead)
|
|
640
|
+
this.dirty = true;
|
|
641
|
+
this.stats.atimeMs = Date.now();
|
|
642
|
+
let end = position + length;
|
|
643
|
+
if (end > this.stats.size) {
|
|
644
|
+
end = position + Math.max(this.stats.size - position, 0);
|
|
645
|
+
}
|
|
646
|
+
this._position = end;
|
|
647
|
+
return end - position;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Read data from the file.
|
|
651
|
+
* @param buffer The buffer that the data will be written to.
|
|
652
|
+
* @param offset The offset within the buffer where writing will start.
|
|
653
|
+
* @param length An integer specifying the number of bytes to read.
|
|
654
|
+
* @param position An integer specifying where to begin reading from in the file.
|
|
655
|
+
* If position is unset, data will be read from the current file position.
|
|
656
|
+
*/
|
|
657
|
+
async read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
658
|
+
const bytesRead = this.prepareRead(length, position);
|
|
659
|
+
new Uint8Array(buffer.buffer, offset, length).set(await this.fs.read(this.path, position, bytesRead));
|
|
660
|
+
if (config.syncImmediately)
|
|
661
|
+
await this.sync();
|
|
662
|
+
return { bytesRead, buffer };
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Read data from the file.
|
|
666
|
+
* @param buffer The buffer that the data will be written to.
|
|
667
|
+
* @param offset The offset within the buffer where writing will start.
|
|
668
|
+
* @param length An integer specifying the number of bytes to read.
|
|
669
|
+
* @param position An integer specifying where to begin reading from in the file.
|
|
670
|
+
* If position is null, data will be read from the current file position.
|
|
671
|
+
* @returns number of bytes written
|
|
672
|
+
*/
|
|
673
|
+
readSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
674
|
+
const bytesRead = this.prepareRead(length, position);
|
|
675
|
+
new Uint8Array(buffer.buffer, offset, length).set(this.fs.readSync(this.path, position, bytesRead));
|
|
676
|
+
if (config.syncImmediately)
|
|
677
|
+
this.syncSync();
|
|
678
|
+
return bytesRead;
|
|
679
|
+
}
|
|
680
|
+
async chmod(mode) {
|
|
681
|
+
if (this.closed)
|
|
682
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
683
|
+
this.dirty = true;
|
|
684
|
+
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
685
|
+
if (config.syncImmediately || mode > c.S_IFMT)
|
|
686
|
+
await this.sync();
|
|
687
|
+
}
|
|
688
|
+
chmodSync(mode) {
|
|
689
|
+
if (this.closed)
|
|
690
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
691
|
+
this.dirty = true;
|
|
692
|
+
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
693
|
+
if (config.syncImmediately || mode > c.S_IFMT)
|
|
694
|
+
this.syncSync();
|
|
695
|
+
}
|
|
696
|
+
async chown(uid, gid) {
|
|
697
|
+
if (this.closed)
|
|
698
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
699
|
+
this.dirty = true;
|
|
700
|
+
_chown(this.stats, uid, gid);
|
|
701
|
+
if (config.syncImmediately)
|
|
702
|
+
await this.sync();
|
|
703
|
+
}
|
|
704
|
+
chownSync(uid, gid) {
|
|
705
|
+
if (this.closed)
|
|
706
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
707
|
+
this.dirty = true;
|
|
708
|
+
_chown(this.stats, uid, gid);
|
|
709
|
+
if (config.syncImmediately)
|
|
710
|
+
this.syncSync();
|
|
711
|
+
}
|
|
712
|
+
async utimes(atime, mtime) {
|
|
713
|
+
if (this.closed)
|
|
714
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
715
|
+
this.dirty = true;
|
|
716
|
+
this.stats.atimeMs = atime;
|
|
717
|
+
this.stats.mtimeMs = mtime;
|
|
718
|
+
if (config.syncImmediately)
|
|
719
|
+
await this.sync();
|
|
720
|
+
}
|
|
721
|
+
utimesSync(atime, mtime) {
|
|
722
|
+
if (this.closed)
|
|
723
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
724
|
+
this.dirty = true;
|
|
725
|
+
this.stats.atimeMs = atime;
|
|
726
|
+
this.stats.mtimeMs = mtime;
|
|
727
|
+
if (config.syncImmediately)
|
|
728
|
+
this.syncSync();
|
|
729
|
+
}
|
|
730
|
+
}
|
package/dist/filesystem.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { File } from './file.js';
|
|
2
|
-
import type { Stats } from './stats.js';
|
|
2
|
+
import type { Stats, StatsLike } from './stats.js';
|
|
3
3
|
export type FileContents = ArrayBufferView | string;
|
|
4
4
|
/**
|
|
5
5
|
* Metadata about a FileSystem
|
|
@@ -58,7 +58,8 @@ export interface FileSystemMetadata {
|
|
|
58
58
|
features?: ('setid' | '')[];
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
* Options used when creating files and directories
|
|
61
|
+
* Options used when creating files and directories.
|
|
62
|
+
* This weird naming and such is to preserve backward compatibility.
|
|
62
63
|
* @todo [BREAKING] Move the `mode` parameter of `createFile` and `mkdir` into this
|
|
63
64
|
* @internal
|
|
64
65
|
*/
|
|
@@ -73,6 +74,19 @@ export interface CreationOptions {
|
|
|
73
74
|
* This is ignored if the FS supports setgid and the setgid bit is set
|
|
74
75
|
*/
|
|
75
76
|
gid: number;
|
|
77
|
+
/**
|
|
78
|
+
* The mode to create the file with.
|
|
79
|
+
*/
|
|
80
|
+
mode?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* This is the correct type that will be used when the API is updated in a breaking release
|
|
84
|
+
*/
|
|
85
|
+
export interface PureCreationOptions extends CreationOptions {
|
|
86
|
+
/**
|
|
87
|
+
* The mode to create the file with.
|
|
88
|
+
*/
|
|
89
|
+
mode: number;
|
|
76
90
|
}
|
|
77
91
|
/**
|
|
78
92
|
* Provides a consistent and easy to use internal API.
|
|
@@ -135,6 +149,10 @@ export declare abstract class FileSystem {
|
|
|
135
149
|
existsSync(path: string): boolean;
|
|
136
150
|
abstract link(target: string, link: string): Promise<void>;
|
|
137
151
|
abstract linkSync(target: string, link: string): void;
|
|
138
|
-
abstract sync(path: string, data
|
|
139
|
-
abstract syncSync(path: string, data
|
|
152
|
+
abstract sync(path: string, data?: Uint8Array, stats?: Partial<Readonly<StatsLike>>): Promise<void>;
|
|
153
|
+
abstract syncSync(path: string, data?: Uint8Array, stats?: Partial<Readonly<StatsLike>>): void;
|
|
154
|
+
abstract read(path: string, offset: number, length: number): Promise<Uint8Array>;
|
|
155
|
+
abstract readSync(path: string, offset: number, length: number): Uint8Array;
|
|
156
|
+
abstract write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
|
|
157
|
+
abstract writeSync(path: string, buffer: Uint8Array, offset: number): void;
|
|
140
158
|
}
|
package/dist/mixins/mutexed.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { CreationOptions, FileSystem, FileSystemMetadata } from '../filesys
|
|
|
3
3
|
import type { Stats } from '../stats.js';
|
|
4
4
|
import type { Concrete } from '../utils.js';
|
|
5
5
|
import '../polyfills.js';
|
|
6
|
+
import type { InodeLike } from '../backends/index.js';
|
|
6
7
|
export declare class MutexLock {
|
|
7
8
|
protected readonly previous?: MutexLock | undefined;
|
|
8
9
|
protected current: PromiseWithResolvers<void>;
|
|
@@ -68,8 +69,12 @@ export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
|
|
|
68
69
|
existsSync(path: string): boolean;
|
|
69
70
|
link(srcpath: string, dstpath: string): Promise<void>;
|
|
70
71
|
linkSync(srcpath: string, dstpath: string): void;
|
|
71
|
-
sync(path: string, data
|
|
72
|
-
syncSync(path: string, data
|
|
72
|
+
sync(path: string, data?: Uint8Array, stats?: Readonly<InodeLike>): Promise<void>;
|
|
73
|
+
syncSync(path: string, data?: Uint8Array, stats?: Readonly<InodeLike>): void;
|
|
74
|
+
read(path: string, offset: number, length: number): Promise<Uint8Array>;
|
|
75
|
+
readSync(path: string, offset: number, length: number): Uint8Array;
|
|
76
|
+
write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
|
|
77
|
+
writeSync(path: string, buffer: Uint8Array, offset: number): void;
|
|
73
78
|
}
|
|
74
79
|
/**
|
|
75
80
|
* This serializes access to an underlying async filesystem.
|
package/dist/mixins/mutexed.js
CHANGED
|
@@ -448,6 +448,62 @@ export class _MutexedFS {
|
|
|
448
448
|
__disposeResources(env_22);
|
|
449
449
|
}
|
|
450
450
|
}
|
|
451
|
+
async read(path, offset, length) {
|
|
452
|
+
const env_23 = { stack: [], error: void 0, hasError: false };
|
|
453
|
+
try {
|
|
454
|
+
const _ = __addDisposableResource(env_23, await this.lock(path, 'read'), false);
|
|
455
|
+
return await this._fs.read(path, offset, length);
|
|
456
|
+
}
|
|
457
|
+
catch (e_23) {
|
|
458
|
+
env_23.error = e_23;
|
|
459
|
+
env_23.hasError = true;
|
|
460
|
+
}
|
|
461
|
+
finally {
|
|
462
|
+
__disposeResources(env_23);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
readSync(path, offset, length) {
|
|
466
|
+
const env_24 = { stack: [], error: void 0, hasError: false };
|
|
467
|
+
try {
|
|
468
|
+
const _ = __addDisposableResource(env_24, this.lockSync(path, 'read'), false);
|
|
469
|
+
return this._fs.readSync(path, offset, length);
|
|
470
|
+
}
|
|
471
|
+
catch (e_24) {
|
|
472
|
+
env_24.error = e_24;
|
|
473
|
+
env_24.hasError = true;
|
|
474
|
+
}
|
|
475
|
+
finally {
|
|
476
|
+
__disposeResources(env_24);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async write(path, buffer, offset) {
|
|
480
|
+
const env_25 = { stack: [], error: void 0, hasError: false };
|
|
481
|
+
try {
|
|
482
|
+
const _ = __addDisposableResource(env_25, await this.lock(path, 'write'), false);
|
|
483
|
+
return await this._fs.write(path, buffer, offset);
|
|
484
|
+
}
|
|
485
|
+
catch (e_25) {
|
|
486
|
+
env_25.error = e_25;
|
|
487
|
+
env_25.hasError = true;
|
|
488
|
+
}
|
|
489
|
+
finally {
|
|
490
|
+
__disposeResources(env_25);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
writeSync(path, buffer, offset) {
|
|
494
|
+
const env_26 = { stack: [], error: void 0, hasError: false };
|
|
495
|
+
try {
|
|
496
|
+
const _ = __addDisposableResource(env_26, this.lockSync(path, 'write'), false);
|
|
497
|
+
return this._fs.writeSync(path, buffer, offset);
|
|
498
|
+
}
|
|
499
|
+
catch (e_26) {
|
|
500
|
+
env_26.error = e_26;
|
|
501
|
+
env_26.hasError = true;
|
|
502
|
+
}
|
|
503
|
+
finally {
|
|
504
|
+
__disposeResources(env_26);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
451
507
|
}
|
|
452
508
|
/**
|
|
453
509
|
* This serializes access to an underlying async filesystem.
|
package/dist/mixins/sync.d.ts
CHANGED