@zenfs/core 1.7.2 → 1.8.1
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 +8 -14
- package/dist/backends/overlay.js +38 -31
- package/dist/backends/passthrough.d.ts +8 -3
- package/dist/backends/passthrough.js +148 -4
- package/dist/backends/port/fs.d.ts +15 -49
- package/dist/backends/port/fs.js +28 -116
- package/dist/backends/port/rpc.d.ts +13 -6
- 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 +39 -34
- package/dist/backends/store/fs.js +407 -238
- 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 +44 -21
- package/dist/devices.js +110 -55
- package/dist/file.d.ts +111 -7
- package/dist/file.js +324 -92
- package/dist/filesystem.d.ts +44 -4
- package/dist/mixins/async.js +12 -6
- package/dist/mixins/mutexed.d.ts +8 -3
- package/dist/mixins/mutexed.js +57 -1
- package/dist/mixins/readonly.d.ts +17 -16
- package/dist/mixins/readonly.js +6 -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 +23 -3
- package/dist/utils.js +58 -10
- package/dist/vfs/async.js +1 -1
- 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 +33 -13
- package/dist/vfs/shared.js +2 -0
- package/dist/vfs/sync.js +25 -13
- package/dist/vfs/types.d.ts +15 -0
- package/eslint.shared.js +1 -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/fs/write.test.ts +6 -11
- 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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Errno, ErrnoError } from './error.js';
|
|
2
2
|
import './polyfills.js';
|
|
3
3
|
import { _chown, Stats } from './stats.js';
|
|
4
|
+
import { growBuffer } from './utils.js';
|
|
4
5
|
import { config } from './vfs/config.js';
|
|
5
6
|
import * as c from './vfs/constants.js';
|
|
7
|
+
const maxByteLength = 0x100000; // 1 MiB
|
|
6
8
|
const validFlags = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
|
|
7
9
|
export function parseFlag(flag) {
|
|
8
10
|
if (typeof flag === 'number') {
|
|
@@ -146,7 +148,7 @@ export class PreloadFile extends File {
|
|
|
146
148
|
/**
|
|
147
149
|
* A buffer containing the entire contents of the file.
|
|
148
150
|
*/
|
|
149
|
-
_buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength
|
|
151
|
+
_buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength }))) {
|
|
150
152
|
super(fs, path);
|
|
151
153
|
this.flag = flag;
|
|
152
154
|
this.stats = stats;
|
|
@@ -171,9 +173,10 @@ export class PreloadFile extends File {
|
|
|
171
173
|
if (this.stats.size == _buffer.byteLength) {
|
|
172
174
|
return;
|
|
173
175
|
}
|
|
174
|
-
if (
|
|
175
|
-
throw new
|
|
176
|
+
if (!isWriteable(this.flag)) {
|
|
177
|
+
throw new ErrnoError(Errno.EIO, `Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`, path);
|
|
176
178
|
}
|
|
179
|
+
this.stats.size = _buffer.byteLength;
|
|
177
180
|
this.dirty = true;
|
|
178
181
|
}
|
|
179
182
|
/**
|
|
@@ -201,36 +204,32 @@ export class PreloadFile extends File {
|
|
|
201
204
|
this._position = value;
|
|
202
205
|
}
|
|
203
206
|
async sync() {
|
|
204
|
-
if (this.closed)
|
|
205
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
206
|
-
|
|
207
|
-
if (!this.dirty) {
|
|
207
|
+
if (this.closed)
|
|
208
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
209
|
+
if (!this.dirty)
|
|
208
210
|
return;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
+
if (!this.fs.metadata().readonly)
|
|
212
|
+
await this.fs.sync(this.path, this._buffer, this.stats);
|
|
211
213
|
this.dirty = false;
|
|
212
214
|
}
|
|
213
215
|
syncSync() {
|
|
214
|
-
if (this.closed)
|
|
215
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
216
|
-
|
|
217
|
-
if (!this.dirty) {
|
|
216
|
+
if (this.closed)
|
|
217
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
218
|
+
if (!this.dirty)
|
|
218
219
|
return;
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
if (!this.fs.metadata().readonly)
|
|
221
|
+
this.fs.syncSync(this.path, this._buffer, this.stats);
|
|
221
222
|
this.dirty = false;
|
|
222
223
|
}
|
|
223
224
|
async close() {
|
|
224
|
-
if (this.closed)
|
|
225
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
226
|
-
}
|
|
225
|
+
if (this.closed)
|
|
226
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
227
227
|
await this.sync();
|
|
228
228
|
this.dispose();
|
|
229
229
|
}
|
|
230
230
|
closeSync() {
|
|
231
|
-
if (this.closed)
|
|
232
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
233
|
-
}
|
|
231
|
+
if (this.closed)
|
|
232
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
234
233
|
this.syncSync();
|
|
235
234
|
this.dispose();
|
|
236
235
|
}
|
|
@@ -238,34 +237,26 @@ export class PreloadFile extends File {
|
|
|
238
237
|
* Cleans up. This will *not* sync the file data to the FS
|
|
239
238
|
*/
|
|
240
239
|
dispose(force) {
|
|
241
|
-
if (this.closed)
|
|
242
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
243
|
-
}
|
|
240
|
+
if (this.closed)
|
|
241
|
+
throw ErrnoError.With('EBADF', this.path, 'dispose');
|
|
244
242
|
if (this.dirty && !force) {
|
|
245
|
-
throw ErrnoError.With('EBUSY', this.path, '
|
|
243
|
+
throw ErrnoError.With('EBUSY', this.path, 'dispose');
|
|
246
244
|
}
|
|
247
|
-
// @ts-expect-error 2790
|
|
248
|
-
delete this._buffer;
|
|
249
|
-
// @ts-expect-error 2790
|
|
250
|
-
delete this.stats;
|
|
251
245
|
this.closed = true;
|
|
252
246
|
}
|
|
253
247
|
stat() {
|
|
254
|
-
if (this.closed)
|
|
255
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
256
|
-
}
|
|
248
|
+
if (this.closed)
|
|
249
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
257
250
|
return Promise.resolve(new Stats(this.stats));
|
|
258
251
|
}
|
|
259
252
|
statSync() {
|
|
260
|
-
if (this.closed)
|
|
261
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
262
|
-
}
|
|
253
|
+
if (this.closed)
|
|
254
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
263
255
|
return new Stats(this.stats);
|
|
264
256
|
}
|
|
265
257
|
_truncate(length) {
|
|
266
|
-
if (this.closed)
|
|
267
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
268
|
-
}
|
|
258
|
+
if (this.closed)
|
|
259
|
+
throw ErrnoError.With('EBADF', this.path, 'truncate');
|
|
269
260
|
this.dirty = true;
|
|
270
261
|
if (!isWriteable(this.flag)) {
|
|
271
262
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
@@ -279,7 +270,7 @@ export class PreloadFile extends File {
|
|
|
279
270
|
}
|
|
280
271
|
this.stats.size = length;
|
|
281
272
|
// Truncate.
|
|
282
|
-
this._buffer = length ? this._buffer.
|
|
273
|
+
this._buffer = length ? this._buffer.subarray(0, length) : new Uint8Array();
|
|
283
274
|
}
|
|
284
275
|
async truncate(length) {
|
|
285
276
|
this._truncate(length);
|
|
@@ -291,37 +282,18 @@ export class PreloadFile extends File {
|
|
|
291
282
|
if (config.syncImmediately)
|
|
292
283
|
this.syncSync();
|
|
293
284
|
}
|
|
294
|
-
_write(buffer, offset = 0, length =
|
|
295
|
-
if (this.closed)
|
|
296
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
297
|
-
}
|
|
285
|
+
_write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
286
|
+
if (this.closed)
|
|
287
|
+
throw ErrnoError.With('EBADF', this.path, 'write');
|
|
298
288
|
if (!isWriteable(this.flag)) {
|
|
299
289
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
300
290
|
}
|
|
301
291
|
this.dirty = true;
|
|
302
292
|
const end = position + length;
|
|
303
|
-
const slice = buffer.
|
|
304
|
-
|
|
293
|
+
const slice = buffer.subarray(offset, offset + length);
|
|
294
|
+
this._buffer = growBuffer(this._buffer, end);
|
|
295
|
+
if (end > this.stats.size)
|
|
305
296
|
this.stats.size = end;
|
|
306
|
-
if (end > this._buffer.byteLength) {
|
|
307
|
-
const { buffer } = this._buffer;
|
|
308
|
-
if ('resizable' in buffer && buffer.resizable && buffer.maxByteLength <= end) {
|
|
309
|
-
buffer.resize(end);
|
|
310
|
-
}
|
|
311
|
-
else if ('growable' in buffer && buffer.growable && buffer.maxByteLength <= end) {
|
|
312
|
-
buffer.grow(end);
|
|
313
|
-
}
|
|
314
|
-
else if (config.unsafeBufferReplace) {
|
|
315
|
-
this._buffer = slice;
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
// Extend the buffer!
|
|
319
|
-
const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength: c.size_max }));
|
|
320
|
-
newBuffer.set(this._buffer);
|
|
321
|
-
this._buffer = newBuffer;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
297
|
this._buffer.set(slice, position);
|
|
326
298
|
this.stats.mtimeMs = Date.now();
|
|
327
299
|
this.position = position + slice.byteLength;
|
|
@@ -350,16 +322,15 @@ export class PreloadFile extends File {
|
|
|
350
322
|
* If position is null, the data will be written at the current position.
|
|
351
323
|
* @returns bytes written
|
|
352
324
|
*/
|
|
353
|
-
writeSync(buffer, offset
|
|
325
|
+
writeSync(buffer, offset, length, position) {
|
|
354
326
|
const bytesWritten = this._write(buffer, offset, length, position);
|
|
355
327
|
if (config.syncImmediately)
|
|
356
328
|
this.syncSync();
|
|
357
329
|
return bytesWritten;
|
|
358
330
|
}
|
|
359
|
-
_read(buffer, offset = 0, length =
|
|
360
|
-
if (this.closed)
|
|
361
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
362
|
-
}
|
|
331
|
+
_read(buffer, offset = 0, length = buffer.byteLength - offset, position) {
|
|
332
|
+
if (this.closed)
|
|
333
|
+
throw ErrnoError.With('EBADF', this.path, 'read');
|
|
363
334
|
if (!isReadable(this.flag)) {
|
|
364
335
|
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
|
|
365
336
|
}
|
|
@@ -378,7 +349,8 @@ export class PreloadFile extends File {
|
|
|
378
349
|
// No copy/read. Return immediately for better performance
|
|
379
350
|
return bytesRead;
|
|
380
351
|
}
|
|
381
|
-
|
|
352
|
+
const slice = this._buffer.subarray(position, end);
|
|
353
|
+
new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength).set(slice, offset);
|
|
382
354
|
return bytesRead;
|
|
383
355
|
}
|
|
384
356
|
/**
|
|
@@ -411,65 +383,61 @@ export class PreloadFile extends File {
|
|
|
411
383
|
return bytesRead;
|
|
412
384
|
}
|
|
413
385
|
async chmod(mode) {
|
|
414
|
-
if (this.closed)
|
|
415
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
416
|
-
}
|
|
386
|
+
if (this.closed)
|
|
387
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
417
388
|
this.dirty = true;
|
|
418
389
|
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
419
390
|
if (config.syncImmediately || mode > c.S_IFMT)
|
|
420
391
|
await this.sync();
|
|
421
392
|
}
|
|
422
393
|
chmodSync(mode) {
|
|
423
|
-
if (this.closed)
|
|
424
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
425
|
-
}
|
|
394
|
+
if (this.closed)
|
|
395
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
426
396
|
this.dirty = true;
|
|
427
397
|
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
428
398
|
if (config.syncImmediately || mode > c.S_IFMT)
|
|
429
399
|
this.syncSync();
|
|
430
400
|
}
|
|
431
401
|
async chown(uid, gid) {
|
|
432
|
-
if (this.closed)
|
|
433
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
434
|
-
}
|
|
402
|
+
if (this.closed)
|
|
403
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
435
404
|
this.dirty = true;
|
|
436
405
|
_chown(this.stats, uid, gid);
|
|
437
406
|
if (config.syncImmediately)
|
|
438
407
|
await this.sync();
|
|
439
408
|
}
|
|
440
409
|
chownSync(uid, gid) {
|
|
441
|
-
if (this.closed)
|
|
442
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
443
|
-
}
|
|
410
|
+
if (this.closed)
|
|
411
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
444
412
|
this.dirty = true;
|
|
445
413
|
_chown(this.stats, uid, gid);
|
|
446
414
|
if (config.syncImmediately)
|
|
447
415
|
this.syncSync();
|
|
448
416
|
}
|
|
449
417
|
async utimes(atime, mtime) {
|
|
450
|
-
if (this.closed)
|
|
451
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
452
|
-
}
|
|
418
|
+
if (this.closed)
|
|
419
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
453
420
|
this.dirty = true;
|
|
454
|
-
this.stats.
|
|
455
|
-
this.stats.
|
|
421
|
+
this.stats.atimeMs = atime;
|
|
422
|
+
this.stats.mtimeMs = mtime;
|
|
456
423
|
if (config.syncImmediately)
|
|
457
424
|
await this.sync();
|
|
458
425
|
}
|
|
459
426
|
utimesSync(atime, mtime) {
|
|
460
|
-
if (this.closed)
|
|
461
|
-
throw ErrnoError.With('EBADF', this.path, '
|
|
462
|
-
}
|
|
427
|
+
if (this.closed)
|
|
428
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
463
429
|
this.dirty = true;
|
|
464
|
-
this.stats.
|
|
465
|
-
this.stats.
|
|
430
|
+
this.stats.atimeMs = atime;
|
|
431
|
+
this.stats.mtimeMs = mtime;
|
|
466
432
|
if (config.syncImmediately)
|
|
467
433
|
this.syncSync();
|
|
468
434
|
}
|
|
469
435
|
}
|
|
470
436
|
/**
|
|
471
437
|
* For the file systems which do not sync to anything.
|
|
438
|
+
* @deprecated
|
|
472
439
|
*/
|
|
440
|
+
/* node:coverage disable */
|
|
473
441
|
export class NoSyncFile extends PreloadFile {
|
|
474
442
|
sync() {
|
|
475
443
|
return Promise.resolve();
|
|
@@ -480,3 +448,267 @@ export class NoSyncFile extends PreloadFile {
|
|
|
480
448
|
}
|
|
481
449
|
closeSync() { }
|
|
482
450
|
}
|
|
451
|
+
/* node:coverage enable */
|
|
452
|
+
/**
|
|
453
|
+
* An implementation of `File` that uses the FS
|
|
454
|
+
*/
|
|
455
|
+
export class LazyFile extends File {
|
|
456
|
+
/**
|
|
457
|
+
* Get the current file position.
|
|
458
|
+
*
|
|
459
|
+
* We emulate the following bug mentioned in the Node documentation:
|
|
460
|
+
*
|
|
461
|
+
* On Linux, positional writes don't work when the file is opened in append mode.
|
|
462
|
+
* The kernel ignores the position argument and always appends the data to the end of the file.
|
|
463
|
+
* @returns The current file position.
|
|
464
|
+
*/
|
|
465
|
+
get position() {
|
|
466
|
+
return isAppendable(this.flag) ? this.stats.size : this._position;
|
|
467
|
+
}
|
|
468
|
+
set position(value) {
|
|
469
|
+
this._position = value;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Creates a file with `path` and, optionally, the given contents.
|
|
473
|
+
* Note that, if contents is specified, it will be mutated by the file.
|
|
474
|
+
*/
|
|
475
|
+
constructor(fs, path, flag, stats) {
|
|
476
|
+
super(fs, path);
|
|
477
|
+
this.flag = flag;
|
|
478
|
+
this.stats = stats;
|
|
479
|
+
/**
|
|
480
|
+
* Current position
|
|
481
|
+
*/
|
|
482
|
+
this._position = 0;
|
|
483
|
+
/**
|
|
484
|
+
* Whether the file has changes which have not been written to the FS
|
|
485
|
+
*/
|
|
486
|
+
this.dirty = false;
|
|
487
|
+
/**
|
|
488
|
+
* Whether the file is open or closed
|
|
489
|
+
*/
|
|
490
|
+
this.closed = false;
|
|
491
|
+
this.dirty = true;
|
|
492
|
+
}
|
|
493
|
+
async sync() {
|
|
494
|
+
if (this.closed)
|
|
495
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
496
|
+
if (!this.dirty)
|
|
497
|
+
return;
|
|
498
|
+
if (!this.fs.metadata().readonly)
|
|
499
|
+
await this.fs.sync(this.path, undefined, this.stats);
|
|
500
|
+
this.dirty = false;
|
|
501
|
+
}
|
|
502
|
+
syncSync() {
|
|
503
|
+
if (this.closed)
|
|
504
|
+
throw ErrnoError.With('EBADF', this.path, 'sync');
|
|
505
|
+
if (!this.dirty)
|
|
506
|
+
return;
|
|
507
|
+
if (!this.fs.metadata().readonly)
|
|
508
|
+
this.fs.syncSync(this.path, undefined, this.stats);
|
|
509
|
+
this.dirty = false;
|
|
510
|
+
}
|
|
511
|
+
async close() {
|
|
512
|
+
if (this.closed)
|
|
513
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
514
|
+
await this.sync();
|
|
515
|
+
this.dispose();
|
|
516
|
+
}
|
|
517
|
+
closeSync() {
|
|
518
|
+
if (this.closed)
|
|
519
|
+
throw ErrnoError.With('EBADF', this.path, 'close');
|
|
520
|
+
this.syncSync();
|
|
521
|
+
this.dispose();
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Cleans up. This will *not* sync the file data to the FS
|
|
525
|
+
*/
|
|
526
|
+
dispose(force) {
|
|
527
|
+
if (this.closed)
|
|
528
|
+
throw ErrnoError.With('EBADF', this.path, 'dispose');
|
|
529
|
+
if (this.dirty && !force)
|
|
530
|
+
throw ErrnoError.With('EBUSY', this.path, 'dispose');
|
|
531
|
+
this.closed = true;
|
|
532
|
+
}
|
|
533
|
+
stat() {
|
|
534
|
+
if (this.closed)
|
|
535
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
536
|
+
return Promise.resolve(new Stats(this.stats));
|
|
537
|
+
}
|
|
538
|
+
statSync() {
|
|
539
|
+
if (this.closed)
|
|
540
|
+
throw ErrnoError.With('EBADF', this.path, 'stat');
|
|
541
|
+
return new Stats(this.stats);
|
|
542
|
+
}
|
|
543
|
+
async truncate(length) {
|
|
544
|
+
if (this.closed)
|
|
545
|
+
throw ErrnoError.With('EBADF', this.path, 'truncate');
|
|
546
|
+
this.dirty = true;
|
|
547
|
+
if (!isWriteable(this.flag)) {
|
|
548
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
549
|
+
}
|
|
550
|
+
this.stats.mtimeMs = Date.now();
|
|
551
|
+
this.stats.size = length;
|
|
552
|
+
if (config.syncImmediately)
|
|
553
|
+
await this.sync();
|
|
554
|
+
}
|
|
555
|
+
truncateSync(length) {
|
|
556
|
+
if (this.closed)
|
|
557
|
+
throw ErrnoError.With('EBADF', this.path, 'truncate');
|
|
558
|
+
this.dirty = true;
|
|
559
|
+
if (!isWriteable(this.flag)) {
|
|
560
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
561
|
+
}
|
|
562
|
+
this.stats.mtimeMs = Date.now();
|
|
563
|
+
this.stats.size = length;
|
|
564
|
+
if (config.syncImmediately)
|
|
565
|
+
this.syncSync();
|
|
566
|
+
}
|
|
567
|
+
prepareWrite(buffer, offset, length, position) {
|
|
568
|
+
if (this.closed)
|
|
569
|
+
throw ErrnoError.With('EBADF', this.path, 'write');
|
|
570
|
+
if (!isWriteable(this.flag)) {
|
|
571
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
|
|
572
|
+
}
|
|
573
|
+
this.dirty = true;
|
|
574
|
+
const end = position + length;
|
|
575
|
+
const slice = buffer.subarray(offset, offset + length);
|
|
576
|
+
if (end > this.stats.size)
|
|
577
|
+
this.stats.size = end;
|
|
578
|
+
this.stats.mtimeMs = Date.now();
|
|
579
|
+
this._position = position + slice.byteLength;
|
|
580
|
+
return slice;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Write buffer to the file.
|
|
584
|
+
* @param buffer Uint8Array containing the data to write to the file.
|
|
585
|
+
* @param offset Offset in the buffer to start reading data from.
|
|
586
|
+
* @param length The amount of bytes to write to the file.
|
|
587
|
+
* @param position Offset from the beginning of the file where this data should be written.
|
|
588
|
+
* If position is null, the data will be written at the current position.
|
|
589
|
+
*/
|
|
590
|
+
async write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
591
|
+
const slice = this.prepareWrite(buffer, offset, length, position);
|
|
592
|
+
await this.fs.write(this.path, slice, position);
|
|
593
|
+
if (config.syncImmediately)
|
|
594
|
+
await this.sync();
|
|
595
|
+
return slice.byteLength;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Write buffer to the file.
|
|
599
|
+
* @param buffer Uint8Array containing the data to write to the file.
|
|
600
|
+
* @param offset Offset in the buffer to start reading data from.
|
|
601
|
+
* @param length The amount of bytes to write to the file.
|
|
602
|
+
* @param position Offset from the beginning of the file where this data should be written.
|
|
603
|
+
* If position is null, the data will be written at the current position.
|
|
604
|
+
* @returns bytes written
|
|
605
|
+
*/
|
|
606
|
+
writeSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
607
|
+
const slice = this.prepareWrite(buffer, offset, length, position);
|
|
608
|
+
this.fs.writeSync(this.path, slice, position);
|
|
609
|
+
if (config.syncImmediately)
|
|
610
|
+
this.syncSync();
|
|
611
|
+
return slice.byteLength;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Computes position information for reading
|
|
615
|
+
*/
|
|
616
|
+
prepareRead(length, position) {
|
|
617
|
+
if (this.closed)
|
|
618
|
+
throw ErrnoError.With('EBADF', this.path, 'read');
|
|
619
|
+
if (!isReadable(this.flag))
|
|
620
|
+
throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
|
|
621
|
+
if (config.updateOnRead)
|
|
622
|
+
this.dirty = true;
|
|
623
|
+
this.stats.atimeMs = Date.now();
|
|
624
|
+
let end = position + length;
|
|
625
|
+
if (end > this.stats.size) {
|
|
626
|
+
end = position + Math.max(this.stats.size - position, 0);
|
|
627
|
+
}
|
|
628
|
+
this._position = end;
|
|
629
|
+
return end - position;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Read data from the file.
|
|
633
|
+
* @param buffer The buffer that the data will be written to.
|
|
634
|
+
* @param offset The offset within the buffer where writing will start.
|
|
635
|
+
* @param length An integer specifying the number of bytes to read.
|
|
636
|
+
* @param position An integer specifying where to begin reading from in the file.
|
|
637
|
+
* If position is unset, data will be read from the current file position.
|
|
638
|
+
*/
|
|
639
|
+
async read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
640
|
+
const bytesRead = this.prepareRead(length, position);
|
|
641
|
+
const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
642
|
+
await this.fs.read(this.path, uint8.subarray(offset, offset + length), position, bytesRead);
|
|
643
|
+
if (config.syncImmediately)
|
|
644
|
+
await this.sync();
|
|
645
|
+
return { bytesRead, buffer };
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Read data from the file.
|
|
649
|
+
* @param buffer The buffer that the data will be written to.
|
|
650
|
+
* @param offset The offset within the buffer where writing will start.
|
|
651
|
+
* @param length An integer specifying the number of bytes to read.
|
|
652
|
+
* @param position An integer specifying where to begin reading from in the file.
|
|
653
|
+
* If position is null, data will be read from the current file position.
|
|
654
|
+
* @returns number of bytes written
|
|
655
|
+
*/
|
|
656
|
+
readSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
|
|
657
|
+
const bytesRead = this.prepareRead(length, position);
|
|
658
|
+
const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
659
|
+
this.fs.readSync(this.path, uint8.subarray(offset, offset + length), position, bytesRead);
|
|
660
|
+
if (config.syncImmediately)
|
|
661
|
+
this.syncSync();
|
|
662
|
+
return bytesRead;
|
|
663
|
+
}
|
|
664
|
+
async chmod(mode) {
|
|
665
|
+
if (this.closed)
|
|
666
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
667
|
+
this.dirty = true;
|
|
668
|
+
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
669
|
+
if (config.syncImmediately || mode > c.S_IFMT)
|
|
670
|
+
await this.sync();
|
|
671
|
+
}
|
|
672
|
+
chmodSync(mode) {
|
|
673
|
+
if (this.closed)
|
|
674
|
+
throw ErrnoError.With('EBADF', this.path, 'chmod');
|
|
675
|
+
this.dirty = true;
|
|
676
|
+
this.stats.mode = (this.stats.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
|
|
677
|
+
if (config.syncImmediately || mode > c.S_IFMT)
|
|
678
|
+
this.syncSync();
|
|
679
|
+
}
|
|
680
|
+
async chown(uid, gid) {
|
|
681
|
+
if (this.closed)
|
|
682
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
683
|
+
this.dirty = true;
|
|
684
|
+
_chown(this.stats, uid, gid);
|
|
685
|
+
if (config.syncImmediately)
|
|
686
|
+
await this.sync();
|
|
687
|
+
}
|
|
688
|
+
chownSync(uid, gid) {
|
|
689
|
+
if (this.closed)
|
|
690
|
+
throw ErrnoError.With('EBADF', this.path, 'chown');
|
|
691
|
+
this.dirty = true;
|
|
692
|
+
_chown(this.stats, uid, gid);
|
|
693
|
+
if (config.syncImmediately)
|
|
694
|
+
this.syncSync();
|
|
695
|
+
}
|
|
696
|
+
async utimes(atime, mtime) {
|
|
697
|
+
if (this.closed)
|
|
698
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
699
|
+
this.dirty = true;
|
|
700
|
+
this.stats.atimeMs = atime;
|
|
701
|
+
this.stats.mtimeMs = mtime;
|
|
702
|
+
if (config.syncImmediately)
|
|
703
|
+
await this.sync();
|
|
704
|
+
}
|
|
705
|
+
utimesSync(atime, mtime) {
|
|
706
|
+
if (this.closed)
|
|
707
|
+
throw ErrnoError.With('EBADF', this.path, 'utimes');
|
|
708
|
+
this.dirty = true;
|
|
709
|
+
this.stats.atimeMs = atime;
|
|
710
|
+
this.stats.mtimeMs = mtime;
|
|
711
|
+
if (config.syncImmediately)
|
|
712
|
+
this.syncSync();
|
|
713
|
+
}
|
|
714
|
+
}
|
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,32 @@ 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
|
+
/**
|
|
155
|
+
* Reads into a buffer
|
|
156
|
+
* @param buffer The buffer to read into. You must set the `byteOffset` and `byteLength` appropriately!
|
|
157
|
+
* @param offset The offset into the file to start reading from
|
|
158
|
+
* @param end The position in the file to stop reading
|
|
159
|
+
*/
|
|
160
|
+
abstract read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* Reads into a buffer
|
|
163
|
+
* @param buffer The buffer to read into. You must set the `byteOffset` and `byteLength` appropriately!
|
|
164
|
+
* @param offset The offset into the file to start reading from
|
|
165
|
+
* @param end The position in the file to stop reading
|
|
166
|
+
*/
|
|
167
|
+
abstract readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
|
|
168
|
+
/**
|
|
169
|
+
* Writes a buffer to a file
|
|
170
|
+
* @param buffer The buffer to write. You must set the `byteOffset` and `byteLength` appropriately!
|
|
171
|
+
* @param offset The offset in the file to start writing
|
|
172
|
+
*/
|
|
173
|
+
abstract write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
|
|
174
|
+
/**
|
|
175
|
+
* Writes a buffer to a file
|
|
176
|
+
* @param buffer The buffer to write. You must set the `byteOffset` and `byteLength` appropriately!
|
|
177
|
+
* @param offset The offset in the file to start writing
|
|
178
|
+
*/
|
|
179
|
+
abstract writeSync(path: string, buffer: Uint8Array, offset: number): void;
|
|
140
180
|
}
|
package/dist/mixins/async.js
CHANGED
|
@@ -52,7 +52,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
52
52
|
});
|
|
53
53
|
import { StoreFS } from '../backends/store/fs.js';
|
|
54
54
|
import { Errno, ErrnoError } from '../error.js';
|
|
55
|
-
import {
|
|
55
|
+
import { LazyFile, parseFlag } from '../file.js';
|
|
56
56
|
import { join } from '../vfs/path.js';
|
|
57
57
|
/**
|
|
58
58
|
* Async() implements synchronous methods on an asynchronous file system
|
|
@@ -138,11 +138,8 @@ export function Async(FS) {
|
|
|
138
138
|
}
|
|
139
139
|
openFileSync(path, flag) {
|
|
140
140
|
this.checkSync(path, 'openFile');
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
const buffer = new Uint8Array(stats.size);
|
|
144
|
-
file.readSync(buffer);
|
|
145
|
-
return new PreloadFile(this, path, flag, stats, buffer);
|
|
141
|
+
const stats = this._sync.statSync(path);
|
|
142
|
+
return new LazyFile(this, path, flag, stats);
|
|
146
143
|
}
|
|
147
144
|
unlinkSync(path) {
|
|
148
145
|
this.checkSync(path, 'unlinkSync');
|
|
@@ -177,6 +174,15 @@ export function Async(FS) {
|
|
|
177
174
|
this.checkSync(path, 'exists');
|
|
178
175
|
return this._sync.existsSync(path);
|
|
179
176
|
}
|
|
177
|
+
readSync(path, buffer, offset, end) {
|
|
178
|
+
this.checkSync(path, 'read');
|
|
179
|
+
this._sync.readSync(path, buffer, offset, end);
|
|
180
|
+
}
|
|
181
|
+
writeSync(path, buffer, offset) {
|
|
182
|
+
this.checkSync(path, 'write');
|
|
183
|
+
this._sync.writeSync(path, buffer, offset);
|
|
184
|
+
this.queue('write', path, buffer, offset);
|
|
185
|
+
}
|
|
180
186
|
/**
|
|
181
187
|
* @internal
|
|
182
188
|
*/
|