@zenfs/core 0.16.4 → 0.17.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.d.ts +3 -4
- package/dist/backends/fetch.d.ts +8 -3
- package/dist/backends/fetch.js +3 -2
- package/dist/backends/{index/fs.d.ts → file_index.d.ts} +49 -10
- package/dist/backends/{index/fs.js → file_index.js} +84 -5
- package/dist/backends/memory.d.ts +6 -1
- package/dist/backends/memory.js +2 -1
- package/dist/backends/overlay.d.ts +16 -16
- package/dist/backends/overlay.js +59 -82
- package/dist/backends/port/fs.d.ts +6 -2
- package/dist/backends/port/fs.js +4 -2
- package/dist/backends/store/fs.js +484 -304
- package/dist/backends/store/simple.js +5 -1
- package/dist/backends/store/store.d.ts +4 -1
- package/dist/backends/store/store.js +9 -5
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +3 -3
- package/dist/emulation/async.d.ts +1 -4
- package/dist/emulation/async.js +9 -4
- package/dist/emulation/dir.d.ts +4 -0
- package/dist/emulation/dir.js +8 -6
- package/dist/emulation/promises.d.ts +1 -3
- package/dist/emulation/promises.js +25 -2
- package/dist/emulation/sync.js +0 -1
- package/dist/emulation/watchers.d.ts +9 -4
- package/dist/emulation/watchers.js +7 -0
- package/dist/file.d.ts +17 -1
- package/dist/file.js +86 -1
- package/dist/filesystem.d.ts +4 -67
- package/dist/filesystem.js +2 -313
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/mixins/async.d.ts +39 -0
- package/dist/mixins/async.js +216 -0
- package/dist/mixins/index.d.ts +4 -0
- package/dist/mixins/index.js +4 -0
- package/dist/mixins/mutexed.d.ts +33 -0
- package/dist/mixins/mutexed.js +465 -0
- package/dist/mixins/readonly.d.ts +25 -0
- package/dist/mixins/readonly.js +57 -0
- package/dist/mixins/shared.d.ts +12 -0
- package/dist/mixins/shared.js +4 -0
- package/dist/mixins/sync.d.ts +6 -0
- package/dist/mixins/sync.js +43 -0
- package/dist/utils.d.ts +0 -5
- package/dist/utils.js +0 -7
- package/package.json +3 -2
- package/src/backends/backend.ts +3 -4
- package/src/backends/fetch.ts +7 -3
- package/src/backends/{index/fs.ts → file_index.ts} +106 -8
- package/src/backends/memory.ts +5 -1
- package/src/backends/overlay.ts +64 -90
- package/src/backends/port/fs.ts +7 -2
- package/src/backends/{index/readme.md → readme.md} +1 -1
- package/src/backends/store/fs.ts +97 -155
- package/src/backends/store/simple.ts +5 -1
- package/src/backends/store/store.ts +10 -5
- package/src/config.ts +3 -1
- package/src/emulation/async.ts +20 -9
- package/src/emulation/dir.ts +19 -16
- package/src/emulation/promises.ts +28 -6
- package/src/emulation/sync.ts +1 -2
- package/src/emulation/watchers.ts +10 -4
- package/src/file.ts +94 -1
- package/src/filesystem.ts +5 -368
- package/src/index.ts +2 -2
- package/src/mixins/async.ts +211 -0
- package/src/mixins/index.ts +4 -0
- package/src/mixins/mutexed.ts +245 -0
- package/src/mixins/readonly.ts +97 -0
- package/src/mixins/shared.ts +20 -0
- package/src/mixins/sync.ts +59 -0
- package/src/utils.ts +0 -8
- package/dist/backends/index/index.d.ts +0 -43
- package/dist/backends/index/index.js +0 -83
- package/dist/backends/locked.d.ts +0 -92
- package/dist/backends/locked.js +0 -487
- package/src/backends/index/index.ts +0 -104
- package/src/backends/locked.ts +0 -264
package/src/filesystem.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import { join } from './emulation/path.js';
|
|
5
|
-
import { Errno, ErrnoError } from './error.js';
|
|
6
|
-
import { PreloadFile, parseFlag, type File } from './file.js';
|
|
1
|
+
import type { Cred } from './cred.js';
|
|
2
|
+
import type { ErrnoError } from './error.js';
|
|
3
|
+
import type { File } from './file.js';
|
|
7
4
|
import { ZenFsType, type Stats } from './stats.js';
|
|
8
5
|
|
|
9
6
|
export type FileContents = ArrayBufferView | string;
|
|
@@ -184,7 +181,7 @@ export abstract class FileSystem {
|
|
|
184
181
|
public abstract readdirSync(path: string, cred: Cred): string[];
|
|
185
182
|
|
|
186
183
|
/**
|
|
187
|
-
* Test whether or not the given path exists
|
|
184
|
+
* Test whether or not the given path exists.
|
|
188
185
|
*/
|
|
189
186
|
public async exists(path: string, cred: Cred): Promise<boolean> {
|
|
190
187
|
try {
|
|
@@ -196,7 +193,7 @@ export abstract class FileSystem {
|
|
|
196
193
|
}
|
|
197
194
|
|
|
198
195
|
/**
|
|
199
|
-
* Test whether or not the given path exists
|
|
196
|
+
* Test whether or not the given path exists.
|
|
200
197
|
*/
|
|
201
198
|
public existsSync(path: string, cred: Cred): boolean {
|
|
202
199
|
try {
|
|
@@ -227,363 +224,3 @@ export abstract class FileSystem {
|
|
|
227
224
|
*/
|
|
228
225
|
public abstract syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
229
226
|
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* `TBase` with `TMixin` mixed-in.
|
|
233
|
-
* @internal @experimental
|
|
234
|
-
*/
|
|
235
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
236
|
-
export type Mixin<TBase extends typeof FileSystem, TMixin> = (abstract new (...args: any[]) => TMixin) & TBase;
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Asynchronous `FileSystem` methods. This is a convience type.
|
|
240
|
-
* @internal
|
|
241
|
-
*/
|
|
242
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
243
|
-
export type _AsyncFSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<unknown>>;
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Implements the asynchronous API in terms of the synchronous API.
|
|
247
|
-
*/
|
|
248
|
-
export function Sync<T extends typeof FileSystem>(FS: T): Mixin<T, _AsyncFSMethods> {
|
|
249
|
-
abstract class SyncFS extends FS implements _AsyncFSMethods {
|
|
250
|
-
public async exists(path: string, cred: Cred): Promise<boolean> {
|
|
251
|
-
return this.existsSync(path, cred);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
|
|
255
|
-
return this.renameSync(oldPath, newPath, cred);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
public async stat(path: string, cred: Cred): Promise<Stats> {
|
|
259
|
-
return this.statSync(path, cred);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
|
|
263
|
-
return this.createFileSync(path, flag, mode, cred);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
|
|
267
|
-
return this.openFileSync(path, flag, cred);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
public async unlink(path: string, cred: Cred): Promise<void> {
|
|
271
|
-
return this.unlinkSync(path, cred);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
public async rmdir(path: string, cred: Cred): Promise<void> {
|
|
275
|
-
return this.rmdirSync(path, cred);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
|
|
279
|
-
return this.mkdirSync(path, mode, cred);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
public async readdir(path: string, cred: Cred): Promise<string[]> {
|
|
283
|
-
return this.readdirSync(path, cred);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
|
|
287
|
-
return this.linkSync(srcpath, dstpath, cred);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
291
|
-
return this.syncSync(path, data, stats);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
return SyncFS;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* @internal
|
|
299
|
-
*/
|
|
300
|
-
type AsyncOperation = {
|
|
301
|
-
[K in keyof _AsyncFSMethods]: [K, ...Parameters<FileSystem[K]>];
|
|
302
|
-
}[keyof _AsyncFSMethods];
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Async() implements synchronous methods on an asynchronous file system
|
|
306
|
-
*
|
|
307
|
-
* Implementing classes must define `_sync` for the synchronous file system used as a cache.
|
|
308
|
-
* Synchronous methods on an asynchronous FS are implemented by:
|
|
309
|
-
* - Performing operations over the in-memory copy,
|
|
310
|
-
* while asynchronously pipelining them to the backing store.
|
|
311
|
-
* - During loading, the contents of the async file system are preloaded into the synchronous store.
|
|
312
|
-
*
|
|
313
|
-
*/
|
|
314
|
-
export function Async<T extends typeof FileSystem>(
|
|
315
|
-
FS: T
|
|
316
|
-
): Mixin<
|
|
317
|
-
T,
|
|
318
|
-
{
|
|
319
|
-
/**
|
|
320
|
-
* @internal @protected
|
|
321
|
-
*/
|
|
322
|
-
_sync?: FileSystem;
|
|
323
|
-
queueDone(): Promise<void>;
|
|
324
|
-
ready(): Promise<void>;
|
|
325
|
-
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
326
|
-
statSync(path: string, cred: Cred): Stats;
|
|
327
|
-
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
|
|
328
|
-
openFileSync(path: string, flag: string, cred: Cred): File;
|
|
329
|
-
unlinkSync(path: string, cred: Cred): void;
|
|
330
|
-
rmdirSync(path: string, cred: Cred): void;
|
|
331
|
-
mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
332
|
-
readdirSync(path: string, cred: Cred): string[];
|
|
333
|
-
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
334
|
-
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
335
|
-
}
|
|
336
|
-
> {
|
|
337
|
-
abstract class AsyncFS extends FS {
|
|
338
|
-
/**
|
|
339
|
-
* Queue of pending asynchronous operations.
|
|
340
|
-
*/
|
|
341
|
-
private _queue: AsyncOperation[] = [];
|
|
342
|
-
private get _queueRunning(): boolean {
|
|
343
|
-
return !!this._queue.length;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
public queueDone(): Promise<void> {
|
|
347
|
-
return new Promise(resolve => {
|
|
348
|
-
const check = (): unknown => (this._queueRunning ? setTimeout(check) : resolve());
|
|
349
|
-
check();
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private _isInitialized: boolean = false;
|
|
354
|
-
|
|
355
|
-
abstract _sync?: FileSystem;
|
|
356
|
-
|
|
357
|
-
public async ready(): Promise<void> {
|
|
358
|
-
await super.ready();
|
|
359
|
-
if (this._isInitialized || this._disableSync) {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
this.checkSync();
|
|
363
|
-
|
|
364
|
-
await this._sync.ready();
|
|
365
|
-
|
|
366
|
-
try {
|
|
367
|
-
await this.crossCopy('/');
|
|
368
|
-
this._isInitialized = true;
|
|
369
|
-
} catch (e) {
|
|
370
|
-
this._isInitialized = false;
|
|
371
|
-
throw e;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
protected checkSync(path?: string, syscall?: string): asserts this is { _sync: FileSystem } {
|
|
376
|
-
if (this._disableSync) {
|
|
377
|
-
throw new ErrnoError(Errno.ENOTSUP, 'Sync caching has been disabled for this async file system', path, syscall);
|
|
378
|
-
}
|
|
379
|
-
if (!this._sync) {
|
|
380
|
-
throw new ErrnoError(Errno.ENOTSUP, 'No sync cache is attached to this async file system', path, syscall);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
|
|
385
|
-
this.checkSync(oldPath, 'rename');
|
|
386
|
-
this._sync.renameSync(oldPath, newPath, cred);
|
|
387
|
-
this.queue('rename', oldPath, newPath, cred);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
public statSync(path: string, cred: Cred): Stats {
|
|
391
|
-
this.checkSync(path, 'stat');
|
|
392
|
-
return this._sync.statSync(path, cred);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
|
|
396
|
-
this.checkSync(path, 'createFile');
|
|
397
|
-
this._sync.createFileSync(path, flag, mode, cred);
|
|
398
|
-
this.queue('createFile', path, flag, mode, cred);
|
|
399
|
-
return this.openFileSync(path, flag, cred);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
|
|
403
|
-
this.checkSync(path, 'openFile');
|
|
404
|
-
const file = this._sync.openFileSync(path, flag, cred);
|
|
405
|
-
const stats = file.statSync();
|
|
406
|
-
const buffer = new Uint8Array(stats.size);
|
|
407
|
-
file.readSync(buffer);
|
|
408
|
-
return new PreloadFile(this, path, flag, stats, buffer);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
public unlinkSync(path: string, cred: Cred): void {
|
|
412
|
-
this.checkSync(path, 'unlinkSync');
|
|
413
|
-
this._sync.unlinkSync(path, cred);
|
|
414
|
-
this.queue('unlink', path, cred);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
public rmdirSync(path: string, cred: Cred): void {
|
|
418
|
-
this.checkSync(path, 'rmdir');
|
|
419
|
-
this._sync.rmdirSync(path, cred);
|
|
420
|
-
this.queue('rmdir', path, cred);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
public mkdirSync(path: string, mode: number, cred: Cred): void {
|
|
424
|
-
this.checkSync(path, 'mkdir');
|
|
425
|
-
this._sync.mkdirSync(path, mode, cred);
|
|
426
|
-
this.queue('mkdir', path, mode, cred);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
public readdirSync(path: string, cred: Cred): string[] {
|
|
430
|
-
this.checkSync(path, 'readdir');
|
|
431
|
-
return this._sync.readdirSync(path, cred);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
|
|
435
|
-
this.checkSync(srcpath, 'link');
|
|
436
|
-
this._sync.linkSync(srcpath, dstpath, cred);
|
|
437
|
-
this.queue('link', srcpath, dstpath, cred);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
441
|
-
this.checkSync(path, 'sync');
|
|
442
|
-
this._sync.syncSync(path, data, stats);
|
|
443
|
-
this.queue('sync', path, data, stats);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
public existsSync(path: string, cred: Cred): boolean {
|
|
447
|
-
this.checkSync(path, 'exists');
|
|
448
|
-
return this._sync.existsSync(path, cred);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* @internal
|
|
453
|
-
*/
|
|
454
|
-
protected async crossCopy(path: string): Promise<void> {
|
|
455
|
-
this.checkSync(path, 'crossCopy');
|
|
456
|
-
const stats = await this.stat(path, rootCred);
|
|
457
|
-
if (stats.isDirectory()) {
|
|
458
|
-
if (path !== '/') {
|
|
459
|
-
const stats = await this.stat(path, rootCred);
|
|
460
|
-
this._sync.mkdirSync(path, stats.mode, stats.cred());
|
|
461
|
-
}
|
|
462
|
-
const files = await this.readdir(path, rootCred);
|
|
463
|
-
for (const file of files) {
|
|
464
|
-
await this.crossCopy(join(path, file));
|
|
465
|
-
}
|
|
466
|
-
} else {
|
|
467
|
-
await using asyncFile = await this.openFile(path, parseFlag('r'), rootCred);
|
|
468
|
-
using syncFile = this._sync.createFileSync(path, parseFlag('w'), stats.mode, stats.cred());
|
|
469
|
-
const buffer = new Uint8Array(stats.size);
|
|
470
|
-
await asyncFile.read(buffer);
|
|
471
|
-
syncFile.writeSync(buffer, 0, stats.size);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* @internal
|
|
477
|
-
*/
|
|
478
|
-
private async _next(): Promise<void> {
|
|
479
|
-
if (!this._queueRunning) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const [method, ...args] = this._queue.shift()!;
|
|
484
|
-
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
485
|
-
await this[method](...args);
|
|
486
|
-
await this._next();
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* @internal
|
|
491
|
-
*/
|
|
492
|
-
private queue(...op: AsyncOperation) {
|
|
493
|
-
this._queue.push(op);
|
|
494
|
-
void this._next();
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
return AsyncFS;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Implements the non-readonly methods to throw `EROFS`
|
|
503
|
-
*/
|
|
504
|
-
export function Readonly<T extends typeof FileSystem>(
|
|
505
|
-
FS: T
|
|
506
|
-
): Mixin<
|
|
507
|
-
T,
|
|
508
|
-
{
|
|
509
|
-
metadata(): FileSystemMetadata;
|
|
510
|
-
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
511
|
-
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
512
|
-
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
|
|
513
|
-
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
|
|
514
|
-
unlink(path: string, cred: Cred): Promise<void>;
|
|
515
|
-
unlinkSync(path: string, cred: Cred): void;
|
|
516
|
-
rmdir(path: string, cred: Cred): Promise<void>;
|
|
517
|
-
rmdirSync(path: string, cred: Cred): void;
|
|
518
|
-
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
|
|
519
|
-
mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
520
|
-
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
|
|
521
|
-
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
522
|
-
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
|
|
523
|
-
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
524
|
-
}
|
|
525
|
-
> {
|
|
526
|
-
abstract class ReadonlyFS extends FS {
|
|
527
|
-
public metadata(): FileSystemMetadata {
|
|
528
|
-
return { ...super.metadata(), readonly: true };
|
|
529
|
-
}
|
|
530
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
531
|
-
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
|
|
532
|
-
throw new ErrnoError(Errno.EROFS);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
|
|
536
|
-
throw new ErrnoError(Errno.EROFS);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
|
|
540
|
-
throw new ErrnoError(Errno.EROFS);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
|
|
544
|
-
throw new ErrnoError(Errno.EROFS);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
public async unlink(path: string, cred: Cred): Promise<void> {
|
|
548
|
-
throw new ErrnoError(Errno.EROFS);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
public unlinkSync(path: string, cred: Cred): void {
|
|
552
|
-
throw new ErrnoError(Errno.EROFS);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
public async rmdir(path: string, cred: Cred): Promise<void> {
|
|
556
|
-
throw new ErrnoError(Errno.EROFS);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
public rmdirSync(path: string, cred: Cred): void {
|
|
560
|
-
throw new ErrnoError(Errno.EROFS);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
|
|
564
|
-
throw new ErrnoError(Errno.EROFS);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
public mkdirSync(path: string, mode: number, cred: Cred): void {
|
|
568
|
-
throw new ErrnoError(Errno.EROFS);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
|
|
572
|
-
throw new ErrnoError(Errno.EROFS);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
|
|
576
|
-
throw new ErrnoError(Errno.EROFS);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
580
|
-
throw new ErrnoError(Errno.EROFS);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
584
|
-
throw new ErrnoError(Errno.EROFS);
|
|
585
|
-
}
|
|
586
|
-
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
587
|
-
}
|
|
588
|
-
return ReadonlyFS;
|
|
589
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -2,8 +2,7 @@ export * from './error.js';
|
|
|
2
2
|
export * from './backends/port/fs.js';
|
|
3
3
|
export * from './backends/fetch.js';
|
|
4
4
|
export * from './backends/memory.js';
|
|
5
|
-
export * from './backends/
|
|
6
|
-
export * from './backends/locked.js';
|
|
5
|
+
export * from './backends/file_index.js';
|
|
7
6
|
export * from './backends/overlay.js';
|
|
8
7
|
export * from './backends/store/fs.js';
|
|
9
8
|
export * from './backends/store/simple.js';
|
|
@@ -14,6 +13,7 @@ export * from './cred.js';
|
|
|
14
13
|
export * from './file.js';
|
|
15
14
|
export * from './filesystem.js';
|
|
16
15
|
export * from './inode.js';
|
|
16
|
+
export * from './mixins/index.js';
|
|
17
17
|
export * from './stats.js';
|
|
18
18
|
export * from './utils.js';
|
|
19
19
|
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { rootCred, type Cred } from '../cred.js';
|
|
2
|
+
import { join } from '../emulation/path.js';
|
|
3
|
+
import { Errno, ErrnoError } from '../error.js';
|
|
4
|
+
import { PreloadFile, parseFlag, type File } from '../file.js';
|
|
5
|
+
import type { FileSystem } from '../filesystem.js';
|
|
6
|
+
import type { Stats } from '../stats.js';
|
|
7
|
+
import type { _AsyncFSMethods, Mixin } from './shared.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export type AsyncOperation = {
|
|
13
|
+
[K in keyof _AsyncFSMethods]: [K, ...Parameters<FileSystem[K]>];
|
|
14
|
+
}[keyof _AsyncFSMethods];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Async() implements synchronous methods on an asynchronous file system
|
|
18
|
+
*
|
|
19
|
+
* Implementing classes must define `_sync` for the synchronous file system used as a cache.
|
|
20
|
+
* Synchronous methods on an asynchronous FS are implemented by:
|
|
21
|
+
* - Performing operations over the in-memory copy,
|
|
22
|
+
* while asynchronously pipelining them to the backing store.
|
|
23
|
+
* - During loading, the contents of the async file system are preloaded into the synchronous store.
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
export function Async<T extends typeof FileSystem>(
|
|
27
|
+
FS: T
|
|
28
|
+
): Mixin<
|
|
29
|
+
T,
|
|
30
|
+
{
|
|
31
|
+
/**
|
|
32
|
+
* @internal @protected
|
|
33
|
+
*/
|
|
34
|
+
_sync?: FileSystem;
|
|
35
|
+
queueDone(): Promise<void>;
|
|
36
|
+
ready(): Promise<void>;
|
|
37
|
+
renameSync(oldPath: string, newPath: string, cred: Cred): void;
|
|
38
|
+
statSync(path: string, cred: Cred): Stats;
|
|
39
|
+
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
|
|
40
|
+
openFileSync(path: string, flag: string, cred: Cred): File;
|
|
41
|
+
unlinkSync(path: string, cred: Cred): void;
|
|
42
|
+
rmdirSync(path: string, cred: Cred): void;
|
|
43
|
+
mkdirSync(path: string, mode: number, cred: Cred): void;
|
|
44
|
+
readdirSync(path: string, cred: Cred): string[];
|
|
45
|
+
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
|
|
46
|
+
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
|
|
47
|
+
}
|
|
48
|
+
> {
|
|
49
|
+
abstract class AsyncFS extends FS {
|
|
50
|
+
/**
|
|
51
|
+
* Queue of pending asynchronous operations.
|
|
52
|
+
*/
|
|
53
|
+
private _queue: AsyncOperation[] = [];
|
|
54
|
+
private get _queueRunning(): boolean {
|
|
55
|
+
return !!this._queue.length;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public queueDone(): Promise<void> {
|
|
59
|
+
return new Promise(resolve => {
|
|
60
|
+
const check = (): unknown => (this._queueRunning ? setTimeout(check) : resolve());
|
|
61
|
+
check();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private _isInitialized: boolean = false;
|
|
66
|
+
|
|
67
|
+
abstract _sync?: FileSystem;
|
|
68
|
+
|
|
69
|
+
public async ready(): Promise<void> {
|
|
70
|
+
await super.ready();
|
|
71
|
+
if (this._isInitialized || this._disableSync) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.checkSync();
|
|
75
|
+
|
|
76
|
+
await this._sync.ready();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
await this.crossCopy('/');
|
|
80
|
+
this._isInitialized = true;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
this._isInitialized = false;
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
protected checkSync(path?: string, syscall?: string): asserts this is { _sync: FileSystem } {
|
|
88
|
+
if (this._disableSync) {
|
|
89
|
+
throw new ErrnoError(Errno.ENOTSUP, 'Sync caching has been disabled for this async file system', path, syscall);
|
|
90
|
+
}
|
|
91
|
+
if (!this._sync) {
|
|
92
|
+
throw new ErrnoError(Errno.ENOTSUP, 'No sync cache is attached to this async file system', path, syscall);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
|
|
97
|
+
this.checkSync(oldPath, 'rename');
|
|
98
|
+
this._sync.renameSync(oldPath, newPath, cred);
|
|
99
|
+
this.queue('rename', oldPath, newPath, cred);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
public statSync(path: string, cred: Cred): Stats {
|
|
103
|
+
this.checkSync(path, 'stat');
|
|
104
|
+
return this._sync.statSync(path, cred);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
|
|
108
|
+
this.checkSync(path, 'createFile');
|
|
109
|
+
this._sync.createFileSync(path, flag, mode, cred);
|
|
110
|
+
this.queue('createFile', path, flag, mode, cred);
|
|
111
|
+
return this.openFileSync(path, flag, cred);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
|
|
115
|
+
this.checkSync(path, 'openFile');
|
|
116
|
+
const file = this._sync.openFileSync(path, flag, cred);
|
|
117
|
+
const stats = file.statSync();
|
|
118
|
+
const buffer = new Uint8Array(stats.size);
|
|
119
|
+
file.readSync(buffer);
|
|
120
|
+
return new PreloadFile(this, path, flag, stats, buffer);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public unlinkSync(path: string, cred: Cred): void {
|
|
124
|
+
this.checkSync(path, 'unlinkSync');
|
|
125
|
+
this._sync.unlinkSync(path, cred);
|
|
126
|
+
this.queue('unlink', path, cred);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public rmdirSync(path: string, cred: Cred): void {
|
|
130
|
+
this.checkSync(path, 'rmdir');
|
|
131
|
+
this._sync.rmdirSync(path, cred);
|
|
132
|
+
this.queue('rmdir', path, cred);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public mkdirSync(path: string, mode: number, cred: Cred): void {
|
|
136
|
+
this.checkSync(path, 'mkdir');
|
|
137
|
+
this._sync.mkdirSync(path, mode, cred);
|
|
138
|
+
this.queue('mkdir', path, mode, cred);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public readdirSync(path: string, cred: Cred): string[] {
|
|
142
|
+
this.checkSync(path, 'readdir');
|
|
143
|
+
return this._sync.readdirSync(path, cred);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
|
|
147
|
+
this.checkSync(srcpath, 'link');
|
|
148
|
+
this._sync.linkSync(srcpath, dstpath, cred);
|
|
149
|
+
this.queue('link', srcpath, dstpath, cred);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
|
|
153
|
+
this.checkSync(path, 'sync');
|
|
154
|
+
this._sync.syncSync(path, data, stats);
|
|
155
|
+
this.queue('sync', path, data, stats);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public existsSync(path: string, cred: Cred): boolean {
|
|
159
|
+
this.checkSync(path, 'exists');
|
|
160
|
+
return this._sync.existsSync(path, cred);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @internal
|
|
165
|
+
*/
|
|
166
|
+
protected async crossCopy(path: string): Promise<void> {
|
|
167
|
+
this.checkSync(path, 'crossCopy');
|
|
168
|
+
const stats = await this.stat(path, rootCred);
|
|
169
|
+
if (stats.isDirectory()) {
|
|
170
|
+
if (path !== '/') {
|
|
171
|
+
const stats = await this.stat(path, rootCred);
|
|
172
|
+
this._sync.mkdirSync(path, stats.mode, stats.cred());
|
|
173
|
+
}
|
|
174
|
+
const files = await this.readdir(path, rootCred);
|
|
175
|
+
for (const file of files) {
|
|
176
|
+
await this.crossCopy(join(path, file));
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
await using asyncFile = await this.openFile(path, parseFlag('r'), rootCred);
|
|
180
|
+
using syncFile = this._sync.createFileSync(path, parseFlag('w'), stats.mode, stats.cred());
|
|
181
|
+
const buffer = new Uint8Array(stats.size);
|
|
182
|
+
await asyncFile.read(buffer);
|
|
183
|
+
syncFile.writeSync(buffer, 0, stats.size);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @internal
|
|
189
|
+
*/
|
|
190
|
+
private async _next(): Promise<void> {
|
|
191
|
+
if (!this._queueRunning) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const [method, ...args] = this._queue.shift()!;
|
|
196
|
+
// @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
|
|
197
|
+
await this[method](...args);
|
|
198
|
+
await this._next();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @internal
|
|
203
|
+
*/
|
|
204
|
+
private queue(...op: AsyncOperation) {
|
|
205
|
+
this._queue.push(op);
|
|
206
|
+
void this._next();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return AsyncFS;
|
|
211
|
+
}
|