@zenfs/core 0.9.2 → 0.9.4

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/src/file.ts ADDED
@@ -0,0 +1,721 @@
1
+ import type { FileReadResult } from 'node:fs/promises';
2
+ import { ApiError, ErrorCode } from './ApiError.js';
3
+ import { O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_SYNC, O_TRUNC, O_WRONLY, S_IFMT } from './emulation/constants.js';
4
+ import type { FileSystem } from './filesystem.js';
5
+ import { size_max } from './inode.js';
6
+ import { Stats, type FileType } from './stats.js';
7
+
8
+ /*
9
+ Typescript does not include a type declaration for resizable array buffers.
10
+ It has been standardized into ECMAScript though
11
+ Remove this if TS adds them to lib declarations
12
+ */
13
+ declare global {
14
+ interface ArrayBuffer {
15
+ readonly resizable: boolean;
16
+
17
+ readonly maxByteLength?: number;
18
+
19
+ resize(newLength: number): void;
20
+ }
21
+
22
+ interface SharedArrayBuffer {
23
+ readonly resizable: boolean;
24
+
25
+ readonly maxByteLength?: number;
26
+
27
+ resize(newLength: number): void;
28
+ }
29
+
30
+ interface ArrayBufferConstructor {
31
+ new (byteLength: number, options: { maxByteLength?: number }): ArrayBuffer;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * @hidden
37
+ */
38
+ export enum ActionType {
39
+ // Indicates that the code should not do anything.
40
+ NOP = 0,
41
+ // Indicates that the code should throw an exception.
42
+ THROW = 1,
43
+ // Indicates that the code should truncate the file, but only if it is a file.
44
+ TRUNCATE = 2,
45
+ // Indicates that the code should create the file.
46
+ CREATE = 3,
47
+ }
48
+
49
+ const validFlags = ['r', 'r+', 'rs', 'rs+', 'w', 'wx', 'w+', 'wx+', 'a', 'ax', 'a+', 'ax+'];
50
+
51
+ export function parseFlag(flag: string | number): string {
52
+ if (typeof flag === 'number') {
53
+ return flagToString(flag);
54
+ }
55
+ if (!validFlags.includes(flag)) {
56
+ throw new Error('Invalid flag string: ' + flag);
57
+ }
58
+ return flag;
59
+ }
60
+
61
+ export function flagToString(flag: number): string {
62
+ switch (flag) {
63
+ case O_RDONLY:
64
+ return 'r';
65
+ case O_RDONLY | O_SYNC:
66
+ return 'rs';
67
+ case O_RDWR:
68
+ return 'r+';
69
+ case O_RDWR | O_SYNC:
70
+ return 'rs+';
71
+ case O_TRUNC | O_CREAT | O_WRONLY:
72
+ return 'w';
73
+ case O_TRUNC | O_CREAT | O_WRONLY | O_EXCL:
74
+ return 'wx';
75
+ case O_TRUNC | O_CREAT | O_RDWR:
76
+ return 'w+';
77
+ case O_TRUNC | O_CREAT | O_RDWR | O_EXCL:
78
+ return 'wx+';
79
+ case O_APPEND | O_CREAT | O_WRONLY:
80
+ return 'a';
81
+ case O_APPEND | O_CREAT | O_WRONLY | O_EXCL:
82
+ return 'ax';
83
+ case O_APPEND | O_CREAT | O_RDWR:
84
+ return 'a+';
85
+ case O_APPEND | O_CREAT | O_RDWR | O_EXCL:
86
+ return 'ax+';
87
+ default:
88
+ throw new Error('Invalid flag number: ' + flag);
89
+ }
90
+ }
91
+
92
+ export function flagToNumber(flag: string): number {
93
+ switch (flag) {
94
+ case 'r':
95
+ return O_RDONLY;
96
+ case 'rs':
97
+ return O_RDONLY | O_SYNC;
98
+ case 'r+':
99
+ return O_RDWR;
100
+ case 'rs+':
101
+ return O_RDWR | O_SYNC;
102
+ case 'w':
103
+ return O_TRUNC | O_CREAT | O_WRONLY;
104
+ case 'wx':
105
+ return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
106
+ case 'w+':
107
+ return O_TRUNC | O_CREAT | O_RDWR;
108
+ case 'wx+':
109
+ return O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
110
+ case 'a':
111
+ return O_APPEND | O_CREAT | O_WRONLY;
112
+ case 'ax':
113
+ return O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
114
+ case 'a+':
115
+ return O_APPEND | O_CREAT | O_RDWR;
116
+ case 'ax+':
117
+ return O_APPEND | O_CREAT | O_RDWR | O_EXCL;
118
+ default:
119
+ throw new Error('Invalid flag string: ' + flag);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Parses a flag as a mode (W_OK, R_OK, and/or X_OK)
125
+ * @param flag the flag to parse
126
+ */
127
+ export function flagToMode(flag: string): number {
128
+ let mode = 0;
129
+ mode <<= 1;
130
+ mode += +isReadable(flag);
131
+ mode <<= 1;
132
+ mode += +isWriteable(flag);
133
+ mode <<= 1;
134
+ return mode;
135
+ }
136
+
137
+ export function isReadable(flag: string): boolean {
138
+ return flag.indexOf('r') !== -1 || flag.indexOf('+') !== -1;
139
+ }
140
+
141
+ export function isWriteable(flag: string): boolean {
142
+ return flag.indexOf('w') !== -1 || flag.indexOf('a') !== -1 || flag.indexOf('+') !== -1;
143
+ }
144
+
145
+ export function isTruncating(flag: string): boolean {
146
+ return flag.indexOf('w') !== -1;
147
+ }
148
+
149
+ export function isAppendable(flag: string): boolean {
150
+ return flag.indexOf('a') !== -1;
151
+ }
152
+
153
+ export function isSynchronous(flag: string): boolean {
154
+ return flag.indexOf('s') !== -1;
155
+ }
156
+
157
+ export function isExclusive(flag: string): boolean {
158
+ return flag.indexOf('x') !== -1;
159
+ }
160
+
161
+ export function pathExistsAction(flag: string): ActionType {
162
+ if (isExclusive(flag)) {
163
+ return ActionType.THROW;
164
+ }
165
+
166
+ if (isTruncating(flag)) {
167
+ return ActionType.TRUNCATE;
168
+ }
169
+
170
+ return ActionType.NOP;
171
+ }
172
+
173
+ export function pathNotExistsAction(flag: string): ActionType {
174
+ if ((isWriteable(flag) || isAppendable(flag)) && flag !== 'r+') {
175
+ return ActionType.CREATE;
176
+ }
177
+ return ActionType.THROW;
178
+ }
179
+
180
+ export abstract class File {
181
+ /**
182
+ * Get the current file position.
183
+ */
184
+ public abstract position?: number;
185
+
186
+ /**
187
+ * The path to the file
188
+ */
189
+ public abstract readonly path?: string;
190
+
191
+ /**
192
+ * Asynchronous `stat`.
193
+ */
194
+ public abstract stat(): Promise<Stats>;
195
+
196
+ /**
197
+ * Synchronous `stat`.
198
+ */
199
+ public abstract statSync(): Stats;
200
+
201
+ /**
202
+ * Asynchronous close.
203
+ */
204
+ public abstract close(): Promise<void>;
205
+
206
+ /**
207
+ * Synchronous close.
208
+ */
209
+ public abstract closeSync(): void;
210
+
211
+ /**
212
+ * Asynchronous truncate.
213
+ */
214
+ public abstract truncate(len: number): Promise<void>;
215
+
216
+ /**
217
+ * Synchronous truncate.
218
+ */
219
+ public abstract truncateSync(len: number): void;
220
+
221
+ /**
222
+ * Asynchronous sync.
223
+ */
224
+ public abstract sync(): Promise<void>;
225
+
226
+ /**
227
+ * Synchronous sync.
228
+ */
229
+ public abstract syncSync(): void;
230
+
231
+ /**
232
+ * Write buffer to the file.
233
+ * Note that it is unsafe to use fs.write multiple times on the same file
234
+ * without waiting for the callback.
235
+ * @param buffer Uint8Array containing the data to write to
236
+ * the file.
237
+ * @param offset Offset in the buffer to start reading data from.
238
+ * @param length The amount of bytes to write to the file.
239
+ * @param position Offset from the beginning of the file where this
240
+ * data should be written. If position is null, the data will be written at
241
+ * the current position.
242
+ * @returns Promise resolving to the new length of the buffer
243
+ */
244
+ public abstract write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number>;
245
+
246
+ /**
247
+ * Write buffer to the file.
248
+ * Note that it is unsafe to use fs.writeSync multiple times on the same file
249
+ * without waiting for it to return.
250
+ * @param buffer Uint8Array containing the data to write to
251
+ * the file.
252
+ * @param offset Offset in the buffer to start reading data from.
253
+ * @param length The amount of bytes to write to the file.
254
+ * @param position Offset from the beginning of the file where this
255
+ * data should be written. If position is null, the data will be written at
256
+ * the current position.
257
+ */
258
+ public abstract writeSync(buffer: Uint8Array, offset?: number, length?: number, position?: number): number;
259
+
260
+ /**
261
+ * Read data from the file.
262
+ * @param buffer The buffer that the data will be
263
+ * written to.
264
+ * @param offset The offset within the buffer where writing will
265
+ * start.
266
+ * @param length An integer specifying the number of bytes to read.
267
+ * @param position An integer specifying where to begin reading from
268
+ * in the file. If position is null, data will be read from the current file
269
+ * position.
270
+ * @returns Promise resolving to the new length of the buffer
271
+ */
272
+ public abstract read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>>;
273
+
274
+ /**
275
+ * Read data from the file.
276
+ * @param buffer The buffer that the data will be written to.
277
+ * @param offset The offset within the buffer where writing will start.
278
+ * @param length An integer specifying the number of bytes to read.
279
+ * @param position An integer specifying where to begin reading from
280
+ * in the file. If position is null, data will be read from the current file
281
+ * position.
282
+ */
283
+ public abstract readSync(buffer: ArrayBufferView, offset?: number, length?: number, position?: number): number;
284
+
285
+ /**
286
+ * Asynchronous `datasync`.
287
+ *
288
+ * Default implementation maps to `sync`.
289
+ */
290
+ public datasync(): Promise<void> {
291
+ return this.sync();
292
+ }
293
+
294
+ /**
295
+ * Synchronous `datasync`.
296
+ *
297
+ * Default implementation maps to `syncSync`.
298
+ */
299
+ public datasyncSync(): void {
300
+ return this.syncSync();
301
+ }
302
+
303
+ /**
304
+ * Asynchronous `chown`.
305
+ */
306
+ public abstract chown(uid: number, gid: number): Promise<void>;
307
+
308
+ /**
309
+ * Synchronous `chown`.
310
+ */
311
+ public abstract chownSync(uid: number, gid: number): void;
312
+
313
+ /**
314
+ * Asynchronous `fchmod`.
315
+ */
316
+ public abstract chmod(mode: number): Promise<void>;
317
+
318
+ /**
319
+ * Synchronous `fchmod`.
320
+ */
321
+ public abstract chmodSync(mode: number): void;
322
+
323
+ /**
324
+ * Change the file timestamps of the file.
325
+ */
326
+ public abstract utimes(atime: Date, mtime: Date): Promise<void>;
327
+
328
+ /**
329
+ * Change the file timestamps of the file.
330
+ */
331
+ public abstract utimesSync(atime: Date, mtime: Date): void;
332
+
333
+ /**
334
+ * Set the file type
335
+ * @internal
336
+ */
337
+ public abstract _setType(type: FileType): Promise<void>;
338
+
339
+ /**
340
+ * Set the file type
341
+ * @internal
342
+ */
343
+ public abstract _setTypeSync(type: FileType): void;
344
+ }
345
+
346
+ /**
347
+ * An implementation of the File interface that operates on a file that is
348
+ * completely in-memory. PreloadFiles are backed by a Uint8Array.
349
+ *
350
+ * @todo 'close' lever that disables functionality once closed.
351
+ */
352
+ export class PreloadFile<FS extends FileSystem> extends File {
353
+ protected _position: number = 0;
354
+ protected _dirty: boolean = false;
355
+ /**
356
+ * Creates a file with the given path and, optionally, the given contents. Note
357
+ * that, if contents is specified, it will be mutated by the file!
358
+ * @param _mode The mode that the file was opened using.
359
+ * Dictates permissions and where the file pointer starts.
360
+ * @param stats The stats object for the given file.
361
+ * PreloadFile will mutate this object. Note that this object must contain
362
+ * the appropriate mode that the file was opened as.
363
+ * @param buffer A buffer containing the entire
364
+ * contents of the file. PreloadFile will mutate this buffer. If not
365
+ * specified, we assume it is a new file.
366
+ */
367
+ constructor(
368
+ /**
369
+ * The file system that created the file.
370
+ */
371
+ protected fs: FS,
372
+ /**
373
+ * Path to the file
374
+ */
375
+ public readonly path: string,
376
+ public readonly flag: string,
377
+ public readonly stats: Stats,
378
+ protected _buffer: Uint8Array = new Uint8Array(new ArrayBuffer(0, { maxByteLength: size_max }))
379
+ ) {
380
+ super();
381
+
382
+ /*
383
+ Note:
384
+ This invariant is *not* maintained once the file starts getting modified.
385
+ It only actually matters if file is readable, as writeable modes may truncate/append to file.
386
+ */
387
+ if (this.stats.size == _buffer.byteLength) {
388
+ return;
389
+ }
390
+
391
+ if (isReadable(this.flag)) {
392
+ throw new Error(`Size mismatch: buffer length ${_buffer.byteLength}, stats size ${this.stats.size}`);
393
+ }
394
+
395
+ this._dirty = true;
396
+ }
397
+
398
+ /**
399
+ * Get the underlying buffer for this file. Mutating not recommended and will mess up dirty tracking.
400
+ */
401
+ public get buffer(): Uint8Array {
402
+ return this._buffer;
403
+ }
404
+
405
+ /**
406
+ * Get the current file position.
407
+ *
408
+ * We emulate the following bug mentioned in the Node documentation:
409
+ * > On Linux, positional writes don't work when the file is opened in append
410
+ * mode. The kernel ignores the position argument and always appends the data
411
+ * to the end of the file.
412
+ * @return The current file position.
413
+ */
414
+ public get position(): number {
415
+ if (isAppendable(this.flag)) {
416
+ return this.stats.size;
417
+ }
418
+ return this._position;
419
+ }
420
+
421
+ /**
422
+ * Set the file position.
423
+ * @param newPos new position
424
+ */
425
+ public set position(newPos: number) {
426
+ this._position = newPos;
427
+ }
428
+
429
+ public async sync(): Promise<void> {
430
+ if (!this.isDirty()) {
431
+ return;
432
+ }
433
+ await this.fs.sync(this.path, this._buffer, this.stats);
434
+ this._dirty = false;
435
+ }
436
+
437
+ public syncSync(): void {
438
+ if (!this.isDirty()) {
439
+ return;
440
+ }
441
+ this.fs.syncSync(this.path, this._buffer, this.stats);
442
+ this._dirty = false;
443
+ }
444
+
445
+ public async close(): Promise<void> {
446
+ await this.sync();
447
+ }
448
+
449
+ public closeSync(): void {
450
+ this.syncSync();
451
+ }
452
+
453
+ /**
454
+ * Asynchronous `stat`.
455
+ */
456
+ public async stat(): Promise<Stats> {
457
+ return new Stats(this.stats);
458
+ }
459
+
460
+ /**
461
+ * Synchronous `stat`.
462
+ */
463
+ public statSync(): Stats {
464
+ return new Stats(this.stats);
465
+ }
466
+
467
+ /**
468
+ * Asynchronous truncate.
469
+ * @param len
470
+ */
471
+ public truncate(len: number): Promise<void> {
472
+ this.truncateSync(len);
473
+ if (isSynchronous(this.flag)) {
474
+ return this.sync();
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Synchronous truncate.
480
+ * @param len
481
+ */
482
+ public truncateSync(len: number): void {
483
+ this._dirty = true;
484
+ if (!isWriteable(this.flag)) {
485
+ throw new ApiError(ErrorCode.EPERM, 'File not opened with a writeable mode.');
486
+ }
487
+ this.stats.mtimeMs = Date.now();
488
+ if (len > this._buffer.length) {
489
+ const buf = new Uint8Array(len - this._buffer.length);
490
+ // Write will set stats.size for us.
491
+ this.writeSync(buf, 0, buf.length, this._buffer.length);
492
+ if (isSynchronous(this.flag)) {
493
+ this.syncSync();
494
+ }
495
+ return;
496
+ }
497
+ this.stats.size = len;
498
+ // Truncate buffer to 'len'.
499
+ this._buffer = this._buffer.subarray(0, len);
500
+ if (isSynchronous(this.flag)) {
501
+ this.syncSync();
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Write buffer to the file.
507
+ * Note that it is unsafe to use fs.write multiple times on the same file
508
+ * without waiting for the callback.
509
+ * @param buffer Uint8Array containing the data to write to
510
+ * the file.
511
+ * @param offset Offset in the buffer to start reading data from.
512
+ * @param length The amount of bytes to write to the file.
513
+ * @param position Offset from the beginning of the file where this
514
+ * data should be written. If position is null, the data will be written at
515
+ * the current position.
516
+ */
517
+ public async write(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number = 0): Promise<number> {
518
+ return this.writeSync(buffer, offset, length, position);
519
+ }
520
+
521
+ /**
522
+ * Write buffer to the file.
523
+ * Note that it is unsafe to use fs.writeSync multiple times on the same file
524
+ * without waiting for the callback.
525
+ * @param buffer Uint8Array containing the data to write to
526
+ * the file.
527
+ * @param offset Offset in the buffer to start reading data from.
528
+ * @param length The amount of bytes to write to the file.
529
+ * @param position Offset from the beginning of the file where this
530
+ * data should be written. If position is null, the data will be written at
531
+ * the current position.
532
+ * @returns bytes written
533
+ */
534
+ public writeSync(buffer: Uint8Array, offset: number = 0, length: number = this.stats.size, position: number = 0): number {
535
+ this._dirty = true;
536
+ position ??= this.position;
537
+ if (!isWriteable(this.flag)) {
538
+ throw new ApiError(ErrorCode.EPERM, 'File not opened with a writeable mode.');
539
+ }
540
+ const endFp = position + length;
541
+ if (endFp > this.stats.size) {
542
+ this.stats.size = endFp;
543
+ if (endFp > this._buffer.byteLength) {
544
+ if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength <= endFp) {
545
+ this._buffer.buffer.resize(endFp);
546
+ } else {
547
+ // Extend the buffer!
548
+ const newBuffer = new Uint8Array(new ArrayBuffer(endFp, { maxByteLength: size_max }));
549
+ newBuffer.set(this._buffer);
550
+ this._buffer = newBuffer;
551
+ }
552
+ }
553
+ }
554
+ const slice = buffer.slice(offset, offset + length);
555
+ this._buffer.set(slice, position);
556
+ const bytesWritten = slice.byteLength;
557
+ this.stats.mtimeMs = Date.now();
558
+ if (isSynchronous(this.flag)) {
559
+ this.syncSync();
560
+ return bytesWritten;
561
+ }
562
+ this.position = position + bytesWritten;
563
+ return bytesWritten;
564
+ }
565
+
566
+ /**
567
+ * Read data from the file.
568
+ * @param buffer The buffer that the data will be
569
+ * written to.
570
+ * @param offset The offset within the buffer where writing will
571
+ * start.
572
+ * @param length An integer specifying the number of bytes to read.
573
+ * @param position An integer specifying where to begin reading from
574
+ * in the file. If position is null, data will be read from the current file
575
+ * position.
576
+ */
577
+ public async read<TBuffer extends ArrayBufferView>(
578
+ buffer: TBuffer,
579
+ offset: number = 0,
580
+ length: number = this.stats.size,
581
+ position: number = 0
582
+ ): Promise<{ bytesRead: number; buffer: TBuffer }> {
583
+ return { bytesRead: this.readSync(buffer, offset, length, position), buffer };
584
+ }
585
+
586
+ /**
587
+ * Read data from the file.
588
+ * @param buffer The buffer that the data will be
589
+ * written to.
590
+ * @param offset The offset within the buffer where writing will start.
591
+ * @param length An integer specifying the number of bytes to read.
592
+ * @param position An integer specifying where to begin reading from
593
+ * in the file. If position is null, data will be read from the current file
594
+ * position.
595
+ * @returns number of bytes written
596
+ */
597
+ public readSync(buffer: ArrayBufferView, offset: number = 0, length: number = this.stats.size, position: number = 0): number {
598
+ if (!isReadable(this.flag)) {
599
+ throw new ApiError(ErrorCode.EPERM, 'File not opened with a readable mode.');
600
+ }
601
+ position ??= this.position;
602
+ let end = position + length;
603
+ if (end > this.stats.size) {
604
+ end = position + Math.max(this.stats.size - position, 0);
605
+ }
606
+ this.stats.atimeMs = Date.now();
607
+ this._position = end;
608
+ const bytesRead = end - position;
609
+ if (bytesRead == 0) {
610
+ // No copy/read. Return immediatly for better performance
611
+ return bytesRead;
612
+ }
613
+ new Uint8Array(buffer.buffer).set(this._buffer.slice(position, end), offset);
614
+ return bytesRead;
615
+ }
616
+
617
+ /**
618
+ * Asynchronous `fchmod`.
619
+ * @param mode the mode
620
+ */
621
+ public async chmod(mode: number): Promise<void> {
622
+ this.chmodSync(mode);
623
+ }
624
+
625
+ /**
626
+ * Synchronous `fchmod`.
627
+ * @param mode
628
+ */
629
+ public chmodSync(mode: number): void {
630
+ this._dirty = true;
631
+ this.stats.chmod(mode);
632
+ this.syncSync();
633
+ }
634
+
635
+ /**
636
+ * Asynchronous `fchown`.
637
+ * @param uid
638
+ * @param gid
639
+ */
640
+ public async chown(uid: number, gid: number): Promise<void> {
641
+ this.chownSync(uid, gid);
642
+ }
643
+
644
+ /**
645
+ * Synchronous `fchown`.
646
+ * @param uid
647
+ * @param gid
648
+ */
649
+ public chownSync(uid: number, gid: number): void {
650
+ this._dirty = true;
651
+ this.stats.chown(uid, gid);
652
+ this.syncSync();
653
+ }
654
+
655
+ public async utimes(atime: Date, mtime: Date): Promise<void> {
656
+ this.utimesSync(atime, mtime);
657
+ }
658
+
659
+ public utimesSync(atime: Date, mtime: Date): void {
660
+ this._dirty = true;
661
+ this.stats.atime = atime;
662
+ this.stats.mtime = mtime;
663
+ this.syncSync();
664
+ }
665
+
666
+ protected isDirty(): boolean {
667
+ return this._dirty;
668
+ }
669
+
670
+ /**
671
+ * Resets the dirty bit. Should only be called after a sync has completed successfully.
672
+ */
673
+ protected resetDirty() {
674
+ this._dirty = false;
675
+ }
676
+
677
+ public _setType(type: FileType): Promise<void> {
678
+ this._dirty = true;
679
+ this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
680
+ return this.sync();
681
+ }
682
+
683
+ public _setTypeSync(type: FileType): void {
684
+ this._dirty = true;
685
+ this.stats.mode = (this.stats.mode & ~S_IFMT) | type;
686
+ this.syncSync();
687
+ }
688
+ }
689
+
690
+ /**
691
+ * For the filesystems which do not sync to anything..
692
+ */
693
+ export class NoSyncFile<T extends FileSystem> extends PreloadFile<T> {
694
+ constructor(_fs: T, _path: string, _flag: string, _stat: Stats, contents?: Uint8Array) {
695
+ super(_fs, _path, _flag, _stat, contents);
696
+ }
697
+ /**
698
+ * Asynchronous sync. Doesn't do anything, simply calls the cb.
699
+ */
700
+ public async sync(): Promise<void> {
701
+ return;
702
+ }
703
+ /**
704
+ * Synchronous sync. Doesn't do anything.
705
+ */
706
+ public syncSync(): void {
707
+ // NOP.
708
+ }
709
+ /**
710
+ * Asynchronous close. Doesn't do anything, simply calls the cb.
711
+ */
712
+ public async close(): Promise<void> {
713
+ return;
714
+ }
715
+ /**
716
+ * Synchronous close. Doesn't do anything.
717
+ */
718
+ public closeSync(): void {
719
+ // NOP.
720
+ }
721
+ }