@zenfs/core 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/backends/fetch.js +5 -4
  2. package/dist/backends/index/fs.js +12 -5
  3. package/dist/backends/overlay.d.ts +1 -1
  4. package/dist/backends/overlay.js +9 -9
  5. package/dist/backends/port/fs.d.ts +22 -0
  6. package/dist/backends/port/fs.js +12 -1
  7. package/dist/backends/port/rpc.d.ts +2 -0
  8. package/dist/backends/port/rpc.js +13 -1
  9. package/dist/backends/store/fs.js +1 -1
  10. package/dist/browser.min.js +4 -4
  11. package/dist/browser.min.js.map +4 -4
  12. package/dist/config.d.ts +1 -2
  13. package/dist/config.js +2 -2
  14. package/dist/emulation/async.d.ts +4 -5
  15. package/dist/emulation/async.js +9 -9
  16. package/dist/emulation/dir.js +1 -1
  17. package/dist/emulation/promises.js +10 -5
  18. package/dist/emulation/sync.d.ts +1 -0
  19. package/dist/emulation/sync.js +1 -1
  20. package/dist/file.js +6 -6
  21. package/dist/filesystem.js +1 -1
  22. package/dist/inode.d.ts +1 -1
  23. package/dist/inode.js +54 -37
  24. package/dist/polyfills.js +1 -0
  25. package/dist/utils.d.ts +1 -1
  26. package/dist/utils.js +1 -1
  27. package/eslint.shared.js +53 -0
  28. package/package.json +16 -6
  29. package/src/backends/fetch.ts +6 -5
  30. package/src/backends/index/fs.ts +12 -5
  31. package/src/backends/index/index.ts +1 -1
  32. package/src/backends/overlay.ts +9 -9
  33. package/src/backends/port/fs.ts +18 -3
  34. package/src/backends/port/readme.md +7 -12
  35. package/src/backends/port/rpc.ts +16 -1
  36. package/src/backends/store/fs.ts +2 -2
  37. package/src/config.ts +3 -3
  38. package/src/emulation/async.ts +31 -18
  39. package/src/emulation/dir.ts +1 -1
  40. package/src/emulation/promises.ts +11 -6
  41. package/src/emulation/sync.ts +5 -4
  42. package/src/file.ts +6 -6
  43. package/src/filesystem.ts +2 -1
  44. package/src/inode.ts +56 -37
  45. package/src/polyfills.ts +1 -0
  46. package/src/utils.ts +5 -2
@@ -126,10 +126,10 @@ export class UnlockedOverlayFS extends FileSystem {
126
126
  return this._deleteLog;
127
127
  }
128
128
 
129
- public restoreDeletionLog(log: string, cred: Cred): void {
129
+ public async restoreDeletionLog(log: string, cred: Cred): Promise<void> {
130
130
  this._deleteLog = log;
131
131
  this._reparseDeletionLog();
132
- this.updateLog('', cred);
132
+ await this.updateLog('', cred);
133
133
  }
134
134
 
135
135
  public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
@@ -248,7 +248,7 @@ export class UnlockedOverlayFS extends FileSystem {
248
248
 
249
249
  // if it still exists add to the delete log
250
250
  if (await this.exists(path, cred)) {
251
- this.deletePath(path, cred);
251
+ await this.deletePath(path, cred);
252
252
  }
253
253
  }
254
254
 
@@ -265,7 +265,7 @@ export class UnlockedOverlayFS extends FileSystem {
265
265
 
266
266
  // if it still exists add to the delete log
267
267
  if (this.existsSync(path, cred)) {
268
- this.deletePath(path, cred);
268
+ void this.deletePath(path, cred);
269
269
  }
270
270
  }
271
271
 
@@ -282,7 +282,7 @@ export class UnlockedOverlayFS extends FileSystem {
282
282
  if ((await this.readdir(path, cred)).length > 0) {
283
283
  throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
284
284
  } else {
285
- this.deletePath(path, cred);
285
+ await this.deletePath(path, cred);
286
286
  }
287
287
  }
288
288
  }
@@ -300,7 +300,7 @@ export class UnlockedOverlayFS extends FileSystem {
300
300
  if (this.readdirSync(path, cred).length > 0) {
301
301
  throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
302
302
  } else {
303
- this.deletePath(path, cred);
303
+ void this.deletePath(path, cred);
304
304
  }
305
305
  }
306
306
  }
@@ -379,9 +379,9 @@ export class UnlockedOverlayFS extends FileSystem {
379
379
  });
380
380
  }
381
381
 
382
- private deletePath(path: string, cred: Cred): void {
382
+ private async deletePath(path: string, cred: Cred): Promise<void> {
383
383
  this._deletedFiles.add(path);
384
- this.updateLog(`d${path}\n`, cred);
384
+ await this.updateLog(`d${path}\n`, cred);
385
385
  }
386
386
 
387
387
  private async updateLog(addition: string, cred: Cred) {
@@ -396,7 +396,7 @@ export class UnlockedOverlayFS extends FileSystem {
396
396
  await log.write(encode(this._deleteLog));
397
397
  if (this._deleteLogUpdateNeeded) {
398
398
  this._deleteLogUpdateNeeded = false;
399
- this.updateLog('', cred);
399
+ await this.updateLog('', cred);
400
400
  }
401
401
  } catch (e) {
402
402
  this._deleteLogError = e as ErrnoError;
@@ -7,8 +7,9 @@ import { File } from '../../file.js';
7
7
  import { Async, FileSystem, type FileSystemMetadata } from '../../filesystem.js';
8
8
  import { Stats, type FileType } from '../../stats.js';
9
9
  import { InMemory } from '../memory.js';
10
- import type { Backend } from '../backend.js';
10
+ import type { Backend, FilesystemOf } from '../backend.js';
11
11
  import * as RPC from './rpc.js';
12
+ import { type MountConfiguration, resolveMountConfig } from '../../config.js';
12
13
 
13
14
  type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;
14
15
  type FileMethod = keyof FileMethods;
@@ -223,9 +224,15 @@ let nextFd = 0;
223
224
 
224
225
  const descriptors: Map<number, File> = new Map();
225
226
 
226
- type FileOrFSRequest = FSRequest | FileRequest;
227
+ /**
228
+ * @internal
229
+ */
230
+ export type FileOrFSRequest = FSRequest | FileRequest;
227
231
 
228
- async function handleRequest(port: RPC.Port, fs: FileSystem, request: FileOrFSRequest): Promise<void> {
232
+ /**
233
+ * @internal
234
+ */
235
+ export async function handleRequest(port: RPC.Port, fs: FileSystem, request: FileOrFSRequest): Promise<void> {
229
236
  if (!RPC.isMessage(request)) {
230
237
  return;
231
238
  }
@@ -308,3 +315,11 @@ export const Port = {
308
315
  return new PortFS(options);
309
316
  },
310
317
  } satisfies Backend<PortFS, RPC.Options>;
318
+
319
+ export async function resolveRemoteMount<T extends Backend>(port: RPC.Port, config: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
320
+ const stopAndReplay = RPC.catchMessages(port);
321
+ const fs = await resolveMountConfig(config, _depth);
322
+ attachFS(port, fs);
323
+ stopAndReplay(fs);
324
+ return fs;
325
+ }
@@ -26,12 +26,10 @@ await configure({
26
26
  Worker:
27
27
 
28
28
  ```ts
29
- import { InMemory, resolveMountConfig } from '@zenfs/core';
30
- import { attachFS } from '@zenfs/port';
29
+ import { InMemory, resolveRemoteMount, attachFS } from '@zenfs/core';
31
30
  import { parentPort } from 'node:worker_threads';
32
31
 
33
- const tmpfs = await resolveMountConfig({ backend: InMemory, name: 'tmp' });
34
- attachFS(parentPort, tmpfs);
32
+ await resolveRemoteMount(parentPort, { backend: InMemory, name: 'tmp' });
35
33
  ```
36
34
 
37
35
  If you are using using web workers, you would use `self` instead of importing `parentPort` in the worker, and would not need to import `Worker` in the main thread.
@@ -39,21 +37,18 @@ If you are using using web workers, you would use `self` instead of importing `p
39
37
  #### Using with multiple ports on the same thread
40
38
 
41
39
  ```ts
42
- import { InMemory, fs, resolveMountConfig } from '@zenfs/core';
43
- import { Port, attachFS } from '@zenfs/port';
40
+ import { InMemory, fs, resolveMountConfig, resolveRemoteMount, Port } from '@zenfs/core';
44
41
  import { MessageChannel } from 'node:worker_threads';
45
42
 
46
- const { port1, port2 } = new MessageChannel();
43
+ const { port1: localPort, port2: remotePort } = new MessageChannel();
47
44
 
48
- const tmpfs = await resolveMountConfig({ backend: InMemory, name: 'tmp' });
49
- attachFS(port2, tmpfs);
50
- fs.mount('/port', await resolveMountConfig({ backend: Port, port: port1 }));
51
- console.log('/port');
45
+ fs.mount('/remote', await resolveRemoteMount(remotePort, { backend: InMemory, name: 'tmp' }));
46
+ fs.mount('/port', await resolveMountConfig({ backend: Port, port: localPort }));
52
47
 
53
48
  const content = 'FS is in a port';
54
49
 
55
50
  await fs.promises.writeFile('/port/test', content);
56
51
 
57
- fs.readFileSync('/tmp/test', 'utf8'); // FS is in a port
52
+ fs.readFileSync('/remote/test', 'utf8'); // FS is in a port
58
53
  await fs.promises.readFile('/port/test', 'utf8'); // FS is in a port
59
54
  ```
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { Errno, ErrnoError, type ErrnoErrorJSON } from '../../error.js';
3
- import { PortFile, type PortFS } from './fs.js';
3
+ import type { Backend, FilesystemOf } from '../backend.js';
4
+ import { handleRequest, PortFile, type PortFS } from './fs.js';
5
+ import type { FileOrFSRequest } from './fs.js';
4
6
 
5
7
  type _MessageEvent<T = any> = T | { data: T };
6
8
 
@@ -148,3 +150,16 @@ export function detach<T extends Message>(port: Port, handler: (message: T) => u
148
150
  handler('data' in message ? message.data : message);
149
151
  });
150
152
  }
153
+
154
+ export function catchMessages<T extends Backend>(port: Port): (fs: FilesystemOf<T>) => void {
155
+ const events: _MessageEvent[] = [];
156
+ const handler = events.push.bind(events);
157
+ attach(port, handler);
158
+ return function (fs: any) {
159
+ detach(port, handler);
160
+ for (const event of events) {
161
+ const request: FileOrFSRequest = 'data' in event ? event.data : event;
162
+ void handleRequest(port, fs, request);
163
+ }
164
+ };
165
+ }
@@ -535,7 +535,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
535
535
  */
536
536
  private async findINode(tx: Transaction, path: string, visited: Set<string> = new Set()): Promise<Inode> {
537
537
  const id = await this._findINode(tx, dirname(path), basename(path), visited);
538
- return this.getINode(tx, id!, path);
538
+ return this.getINode(tx, id, path);
539
539
  }
540
540
 
541
541
  /**
@@ -698,7 +698,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
698
698
  await tx.commit();
699
699
  return inode;
700
700
  } catch (e) {
701
- tx.abort();
701
+ await tx.abort();
702
702
  throw e;
703
703
  }
704
704
  }
package/src/config.ts CHANGED
@@ -55,9 +55,9 @@ export async function resolveMountConfig<T extends Backend>(config: MountConfigu
55
55
  const { backend } = config;
56
56
 
57
57
  if (!(await backend.isAvailable())) {
58
- throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
58
+ throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend.name);
59
59
  }
60
- checkOptions(backend, config);
60
+ await checkOptions(backend, config);
61
61
  const mount = (await backend.create(config)) as FilesystemOf<T>;
62
62
  if ('_disableSync' in mount) {
63
63
  type AsyncFS = InstanceType<ReturnType<typeof Async<new () => FilesystemOf<T>>>>;
@@ -67,7 +67,7 @@ export async function resolveMountConfig<T extends Backend>(config: MountConfigu
67
67
  return mount;
68
68
  }
69
69
 
70
- type ConfigMounts = { [K in AbsolutePath]: Backend };
70
+ export type ConfigMounts = { [K in AbsolutePath]: Backend };
71
71
 
72
72
  /**
73
73
  * Configuration
@@ -207,12 +207,18 @@ writeFile satisfies Omit<typeof fs.writeFile, '__promisify__'>;
207
207
  * @param callback
208
208
  */
209
209
  export function appendFile(filename: fs.PathLike, data: FileContents, cb?: Callback): void;
210
- export function appendFile(filename: fs.PathLike, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string }, cb?: Callback): void;
211
- export function appendFile(filename: fs.PathLike, data: FileContents, encoding?: string, cb?: Callback): void;
212
- export function appendFile(filename: fs.PathLike, data: FileContents, cbEncOpts?: any, cb: Callback = nop): void {
210
+ export function appendFile(filename: fs.PathLike, data: FileContents, options?: fs.EncodingOption & { mode?: fs.Mode; flag?: fs.OpenMode }, cb?: Callback): void;
211
+ export function appendFile(filename: fs.PathLike, data: FileContents, encoding?: BufferEncoding, cb?: Callback): void;
212
+ export function appendFile(
213
+ filename: fs.PathLike,
214
+ data: FileContents,
215
+ cbEncOpts?: (fs.EncodingOption & { mode?: fs.Mode; flag?: fs.OpenMode }) | Callback,
216
+ cb: Callback = nop
217
+ ): void {
218
+ const optionsOrEncoding = typeof cbEncOpts != 'function' ? cbEncOpts : undefined;
213
219
  cb = typeof cbEncOpts === 'function' ? cbEncOpts : cb;
214
220
  promises
215
- .appendFile(filename, data, typeof cbEncOpts === 'function' ? null : cbEncOpts)
221
+ .appendFile(filename, data, optionsOrEncoding)
216
222
  .then(() => cb())
217
223
  .catch(cb);
218
224
  }
@@ -259,7 +265,7 @@ close satisfies Omit<typeof fs.close, '__promisify__'>;
259
265
  */
260
266
  export function ftruncate(fd: number, cb?: Callback): void;
261
267
  export function ftruncate(fd: number, len?: number, cb?: Callback): void;
262
- export function ftruncate(fd: number, lenOrCB?: any, cb: Callback = nop): void {
268
+ export function ftruncate(fd: number, lenOrCB?: number | Callback, cb: Callback = nop): void {
263
269
  const length = typeof lenOrCB === 'number' ? lenOrCB : 0;
264
270
  cb = typeof lenOrCB === 'function' ? lenOrCB : cb;
265
271
  const file = fd2file(fd);
@@ -317,8 +323,15 @@ export function write(fd: number, buffer: Uint8Array, offset: number, length: nu
317
323
  export function write(fd: number, data: FileContents, cb?: Callback<[number, string]>): void;
318
324
  export function write(fd: number, data: FileContents, position?: number, cb?: Callback<[number, string]>): void;
319
325
  export function write(fd: number, data: FileContents, position: number | null, encoding: BufferEncoding, cb?: Callback<[number, string]>): void;
320
- export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?: any, cbPos?: any, cb: Callback<[number, Uint8Array]> | Callback<[number, string]> = nop): void {
321
- let buffer: Buffer, offset: number, length: number, position: number | undefined | null, encoding: BufferEncoding;
326
+ export function write(
327
+ fd: number,
328
+ data: FileContents,
329
+ cbPosOff?: number | Callback<[number, string]> | null,
330
+ cbLenEnc?: number | BufferEncoding | Callback<[number, string]>,
331
+ cbPosEnc?: number | BufferEncoding | Callback<[number, Uint8Array]> | Callback<[number, string]>,
332
+ cb: Callback<[number, Uint8Array]> | Callback<[number, string]> = nop
333
+ ): void {
334
+ let buffer: Buffer, offset: number | undefined, length: number | undefined, position: number | undefined | null, encoding: BufferEncoding;
322
335
  const handle = new promises.FileHandle(fd);
323
336
  if (typeof data === 'string') {
324
337
  // Signature 1: (fd, string, [position?, [encoding?]], cb?)
@@ -331,12 +344,12 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
331
344
  case 'number':
332
345
  // (fd, string, position, encoding?, cb?)
333
346
  position = cbPosOff;
334
- encoding = typeof cbLenEnc === 'string' ? (cbLenEnc as BufferEncoding) : 'utf8';
335
- cb = typeof cbPos === 'function' ? cbPos : cb;
347
+ encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
348
+ cb = typeof cbPosEnc === 'function' ? cbPosEnc : cb;
336
349
  break;
337
350
  default:
338
351
  // ...try to find the callback and get out of here!
339
- cb = typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPos === 'function' ? cbPos : cb;
352
+ cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb) as Callback<[number, Uint8Array | string]>;
340
353
  (cb as Callback<[number, Uint8Array | string]>)(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
341
354
  return;
342
355
  }
@@ -353,11 +366,11 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
353
366
  } else {
354
367
  // Signature 2: (fd, buffer, offset, length, position?, cb?)
355
368
  buffer = Buffer.from(data.buffer);
356
- offset = cbPosOff;
357
- length = cbLenEnc;
358
- position = typeof cbPos === 'number' ? cbPos : null;
359
- const _cb = typeof cbPos === 'function' ? cbPos : (cb as Callback<[number, Uint8Array]>);
360
- handle
369
+ offset = cbPosOff as number;
370
+ length = cbLenEnc as number;
371
+ position = typeof cbPosEnc === 'number' ? cbPosEnc : null;
372
+ const _cb = (typeof cbPosEnc === 'function' ? cbPosEnc : cb) as Callback<[number, Uint8Array]>;
373
+ void handle
361
374
  .write(buffer, offset, length, position)
362
375
  .then(({ bytesWritten }) => _cb(undefined, bytesWritten, buffer))
363
376
  .catch(_cb);
@@ -472,7 +485,7 @@ export function readdir(path: fs.PathLike, _options: { withFileTypes?: boolean }
472
485
  const options = typeof _options != 'function' ? _options : {};
473
486
  promises
474
487
  .readdir(path, options as object)
475
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
488
+
476
489
  .then(entries => cb(undefined, entries as any))
477
490
  .catch(cb);
478
491
  }
@@ -647,11 +660,11 @@ realpath satisfies Omit<typeof fs.realpath, '__promisify__' | 'native'>;
647
660
  */
648
661
  export function access(path: fs.PathLike, cb: Callback): void;
649
662
  export function access(path: fs.PathLike, mode: number, cb: Callback): void;
650
- export function access(path: fs.PathLike, cbMode: any, cb: Callback = nop): void {
663
+ export function access(path: fs.PathLike, cbMode: number | Callback, cb: Callback = nop): void {
651
664
  const mode = typeof cbMode === 'number' ? cbMode : R_OK;
652
665
  cb = typeof cbMode === 'function' ? cbMode : cb;
653
666
  promises
654
- .access(path, typeof cbMode === 'function' ? null : cbMode)
667
+ .access(path, mode)
655
668
  .then(() => cb())
656
669
  .catch(cb);
657
670
  }
@@ -103,7 +103,7 @@ export class Dir implements _Dir {
103
103
  return this._read();
104
104
  }
105
105
 
106
- this._read().then(value => cb(undefined, value));
106
+ void this._read().then(value => cb(undefined, value));
107
107
  }
108
108
 
109
109
  /**
@@ -164,7 +164,7 @@ export class FileHandle implements promises.FileHandle {
164
164
  */
165
165
  public readableWebStream(options: promises.ReadableWebStreamOptions = {}): TReadableStream<Uint8Array> {
166
166
  // Note: using an arrow function to preserve `this`
167
- const start = async ({ close, enqueue, error }: ReadableStreamController<Uint8Array>) => {
167
+ const start = async (controller: ReadableStreamController<Uint8Array>) => {
168
168
  try {
169
169
  const chunkSize = 64 * 1024,
170
170
  maxChunks = 1e7;
@@ -175,10 +175,10 @@ export class FileHandle implements promises.FileHandle {
175
175
  while (bytesRead > 0) {
176
176
  const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize, position);
177
177
  if (!result.bytesRead) {
178
- close();
178
+ controller.close();
179
179
  return;
180
180
  }
181
- enqueue(result.buffer.slice(0, result.bytesRead));
181
+ controller.enqueue(result.buffer.slice(0, result.bytesRead));
182
182
  position += result.bytesRead;
183
183
  if (++i >= maxChunks) {
184
184
  throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
@@ -186,11 +186,15 @@ export class FileHandle implements promises.FileHandle {
186
186
  bytesRead = result.bytesRead;
187
187
  }
188
188
  } catch (e) {
189
- error(e);
189
+ controller.error(e);
190
190
  }
191
191
  };
192
192
 
193
- return new (globalThis as any).ReadableStream({ start, type: options.type });
193
+ const _gt = globalThis;
194
+ if (!('ReadableStream' in _gt)) {
195
+ throw new ErrnoError(Errno.ENOSYS, 'ReadableStream is missing on globalThis');
196
+ }
197
+ return new (_gt as { ReadableStream: new (...args: unknown[]) => TReadableStream<Uint8Array> }).ReadableStream({ start, type: options.type });
194
198
  }
195
199
 
196
200
  public readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface {
@@ -326,6 +330,7 @@ export class FileHandle implements promises.FileHandle {
326
330
  highWaterMark: options?.highWaterMark || 64 * 1024,
327
331
  encoding: options!.encoding!,
328
332
 
333
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
329
334
  read: async (size: number) => {
330
335
  try {
331
336
  const result = await this.read(new Uint8Array(size), 0, size, this.file.position);
@@ -573,7 +578,7 @@ export async function writeFile(
573
578
  _options?: (fs.ObjectEncodingOptions & { mode?: fs.Mode; flag?: fs.OpenMode; flush?: boolean }) | BufferEncoding | null
574
579
  ): Promise<void> {
575
580
  const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
576
- await using handle = path instanceof FileHandle ? path : await open(path.toString(), options.flag, options.mode);
581
+ await using handle = path instanceof FileHandle ? path : await open((path as fs.PathLike).toString(), options.flag, options.mode);
577
582
 
578
583
  const _data = typeof data == 'string' ? data : data;
579
584
  if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
@@ -219,7 +219,7 @@ export function readFileSync(path: fs.PathOrFileDescriptor, _options: fs.WriteFi
219
219
  if (!isReadable(flag)) {
220
220
  throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading.');
221
221
  }
222
- const data: Buffer = Buffer.from(_readFileSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), options.flag, true));
222
+ const data: Buffer = Buffer.from(_readFileSync(typeof path == 'number' ? fd2file(path).path : path.toString(), options.flag, true));
223
223
  return options.encoding ? data.toString(options.encoding) : data;
224
224
  }
225
225
  readFileSync satisfies typeof fs.readFileSync;
@@ -251,7 +251,7 @@ export function writeFileSync(path: fs.PathOrFileDescriptor, data: FileContents,
251
251
  if (!encodedData) {
252
252
  throw new ErrnoError(Errno.EINVAL, 'Data not specified');
253
253
  }
254
- using file = _openSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), flag, options.mode, true);
254
+ using file = _openSync(typeof path == 'number' ? fd2file(path).path : path.toString(), flag, options.mode, true);
255
255
  file.writeSync(encodedData, 0, encodedData.byteLength, 0);
256
256
  }
257
257
  writeFileSync satisfies typeof fs.writeFileSync;
@@ -277,7 +277,7 @@ export function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileCont
277
277
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
278
278
  }
279
279
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
280
- using file = _openSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), flag, options.mode, true);
280
+ using file = _openSync(typeof filename == 'number' ? fd2file(filename).path : filename.toString(), flag, options.mode, true);
281
281
  file.writeSync(encodedData, 0, encodedData.byteLength);
282
282
  }
283
283
  appendFileSync satisfies typeof fs.appendFileSync;
@@ -587,6 +587,7 @@ symlinkSync satisfies typeof fs.symlinkSync;
587
587
  */
588
588
  export function readlinkSync(path: fs.PathLike, options?: fs.BufferEncodingOption): Buffer;
589
589
  export function readlinkSync(path: fs.PathLike, options: fs.EncodingOption | BufferEncoding): string;
590
+ export function readlinkSync(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Buffer | string;
590
591
  export function readlinkSync(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Buffer | string {
591
592
  const value: Buffer = Buffer.from(_readFileSync(path.toString(), 'r', false));
592
593
  const encoding = typeof options == 'object' ? options?.encoding : options;
@@ -697,7 +698,7 @@ export function realpathSync(path: fs.PathLike, options?: fs.EncodingOption | fs
697
698
  return lpath;
698
699
  }
699
700
 
700
- return realpathSync(mountPoint + readlinkSync(lpath));
701
+ return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
701
702
  } catch (e) {
702
703
  throw fixError(e as Error, { [resolvedPath]: lpath });
703
704
  }
package/src/file.ts CHANGED
@@ -429,8 +429,8 @@ export class PreloadFile<FS extends FileSystem> extends File {
429
429
  /**
430
430
  * Asynchronous `stat`.
431
431
  */
432
- public async stat(): Promise<Stats> {
433
- return new Stats(this.stats);
432
+ public stat(): Promise<Stats> {
433
+ return Promise.resolve(new Stats(this.stats));
434
434
  }
435
435
 
436
436
  /**
@@ -673,8 +673,8 @@ export class NoSyncFile<T extends FileSystem> extends PreloadFile<T> {
673
673
  /**
674
674
  * Asynchronous sync. Doesn't do anything, simply calls the cb.
675
675
  */
676
- public async sync(): Promise<void> {
677
- return;
676
+ public sync(): Promise<void> {
677
+ return Promise.resolve();
678
678
  }
679
679
  /**
680
680
  * Synchronous sync. Doesn't do anything.
@@ -685,8 +685,8 @@ export class NoSyncFile<T extends FileSystem> extends PreloadFile<T> {
685
685
  /**
686
686
  * Asynchronous close. Doesn't do anything, simply calls the cb.
687
687
  */
688
- public async close(): Promise<void> {
689
- return;
688
+ public close(): Promise<void> {
689
+ return Promise.resolve();
690
690
  }
691
691
  /**
692
692
  * Synchronous close. Doesn't do anything.
package/src/filesystem.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/require-await */
1
2
  import type { ExtractProperties } from 'utilium';
2
3
  import { rootCred, type Cred } from './cred.js';
3
4
  import { join } from './emulation/path.js';
@@ -490,7 +491,7 @@ export function Async<T extends typeof FileSystem>(
490
491
  */
491
492
  private queue(...op: AsyncOperation) {
492
493
  this._queue.push(op);
493
- this._next();
494
+ void this._next();
494
495
  }
495
496
  }
496
497
 
package/src/inode.ts CHANGED
@@ -16,7 +16,7 @@ export const size_max = 2 ** 32 - 1;
16
16
  * Room inode
17
17
  * @hidden
18
18
  */
19
- export const rootIno = 0n as const;
19
+ export const rootIno = 0n;
20
20
 
21
21
  /**
22
22
  * Generates a random 32 bit integer, then converts to a hex string
@@ -36,19 +36,38 @@ export function randomIno(): Ino {
36
36
  /**
37
37
  * Offsets for inode members
38
38
  */
39
- enum Offset {
40
- ino = 0,
41
- size = 8, // offsets with a 64-bit size
42
- mode = 12, // 16
43
- nlink = 14, // 18
44
- uid = 18, // 22
45
- gid = 22, // 26
46
- atime = 26, // 30
47
- birthtime = 34, // 38
48
- mtime = 42, // 46
49
- ctime = 50, // 54
50
- end = 58, // 62
51
- }
39
+ const offsets = {
40
+ ino: 0,
41
+ size: 8,
42
+ mode: 12,
43
+ nlink: 14,
44
+ uid: 18,
45
+ gid: 22,
46
+ atime: 26,
47
+ birthtime: 34,
48
+ mtime: 42,
49
+ ctime: 50,
50
+ end: 58,
51
+ };
52
+
53
+ /**
54
+ * Offsets for a 64-bit inode's members
55
+ * Currently unused
56
+ */
57
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
+ const offsets_64 = {
59
+ ino: 0,
60
+ size: 8,
61
+ mode: 16,
62
+ nlink: 18,
63
+ uid: 22,
64
+ gid: 26,
65
+ atime: 30,
66
+ birthtime: 38,
67
+ mtime: 46,
68
+ ctime: 54,
69
+ end: 62,
70
+ };
52
71
 
53
72
  /**
54
73
  * Generic inode definition that can easily be serialized.
@@ -64,9 +83,9 @@ export class Inode implements StatsLike {
64
83
 
65
84
  constructor(buffer?: ArrayBufferLike) {
66
85
  const setDefaults = !buffer;
67
- buffer ??= new ArrayBuffer(Offset.end);
68
- if (buffer?.byteLength < Offset.end) {
69
- throw new RangeError(`Can not create an inode from a buffer less than ${Offset.end} bytes`);
86
+ buffer ??= new ArrayBuffer(offsets.end);
87
+ if (buffer?.byteLength < offsets.end) {
88
+ throw new RangeError(`Can not create an inode from a buffer less than ${offsets.end} bytes`);
70
89
  }
71
90
  this.view = new DataView(buffer);
72
91
  this.buffer = buffer;
@@ -87,83 +106,83 @@ export class Inode implements StatsLike {
87
106
  }
88
107
 
89
108
  public get ino(): Ino {
90
- return this.view.getBigUint64(Offset.ino, true);
109
+ return this.view.getBigUint64(offsets.ino, true);
91
110
  }
92
111
 
93
112
  public set ino(value: Ino) {
94
- this.view.setBigUint64(Offset.ino, value, true);
113
+ this.view.setBigUint64(offsets.ino, value, true);
95
114
  }
96
115
 
97
116
  public get size(): number {
98
- return this.view.getUint32(Offset.size, true);
117
+ return this.view.getUint32(offsets.size, true);
99
118
  }
100
119
 
101
120
  public set size(value: number) {
102
- this.view.setUint32(Offset.size, value, true);
121
+ this.view.setUint32(offsets.size, value, true);
103
122
  }
104
123
 
105
124
  public get mode(): number {
106
- return this.view.getUint16(Offset.mode, true);
125
+ return this.view.getUint16(offsets.mode, true);
107
126
  }
108
127
 
109
128
  public set mode(value: number) {
110
- this.view.setUint16(Offset.mode, value, true);
129
+ this.view.setUint16(offsets.mode, value, true);
111
130
  }
112
131
 
113
132
  public get nlink(): number {
114
- return this.view.getUint32(Offset.nlink, true);
133
+ return this.view.getUint32(offsets.nlink, true);
115
134
  }
116
135
 
117
136
  public set nlink(value: number) {
118
- this.view.setUint32(Offset.nlink, value, true);
137
+ this.view.setUint32(offsets.nlink, value, true);
119
138
  }
120
139
 
121
140
  public get uid(): number {
122
- return this.view.getUint32(Offset.uid, true);
141
+ return this.view.getUint32(offsets.uid, true);
123
142
  }
124
143
 
125
144
  public set uid(value: number) {
126
- this.view.setUint32(Offset.uid, value, true);
145
+ this.view.setUint32(offsets.uid, value, true);
127
146
  }
128
147
 
129
148
  public get gid(): number {
130
- return this.view.getUint32(Offset.gid, true);
149
+ return this.view.getUint32(offsets.gid, true);
131
150
  }
132
151
 
133
152
  public set gid(value: number) {
134
- this.view.setUint32(Offset.gid, value, true);
153
+ this.view.setUint32(offsets.gid, value, true);
135
154
  }
136
155
 
137
156
  public get atimeMs(): number {
138
- return this.view.getFloat64(Offset.atime, true);
157
+ return this.view.getFloat64(offsets.atime, true);
139
158
  }
140
159
 
141
160
  public set atimeMs(value: number) {
142
- this.view.setFloat64(Offset.atime, value, true);
161
+ this.view.setFloat64(offsets.atime, value, true);
143
162
  }
144
163
 
145
164
  public get birthtimeMs(): number {
146
- return this.view.getFloat64(Offset.birthtime, true);
165
+ return this.view.getFloat64(offsets.birthtime, true);
147
166
  }
148
167
 
149
168
  public set birthtimeMs(value: number) {
150
- this.view.setFloat64(Offset.birthtime, value, true);
169
+ this.view.setFloat64(offsets.birthtime, value, true);
151
170
  }
152
171
 
153
172
  public get mtimeMs(): number {
154
- return this.view.getFloat64(Offset.mtime, true);
173
+ return this.view.getFloat64(offsets.mtime, true);
155
174
  }
156
175
 
157
176
  public set mtimeMs(value: number) {
158
- this.view.setFloat64(Offset.mtime, value, true);
177
+ this.view.setFloat64(offsets.mtime, value, true);
159
178
  }
160
179
 
161
180
  public get ctimeMs(): number {
162
- return this.view.getFloat64(Offset.ctime, true);
181
+ return this.view.getFloat64(offsets.ctime, true);
163
182
  }
164
183
 
165
184
  public set ctimeMs(value: number) {
166
- this.view.setFloat64(Offset.ctime, value, true);
185
+ this.view.setFloat64(offsets.ctime, value, true);
167
186
  }
168
187
 
169
188
  /**
package/src/polyfills.ts CHANGED
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line @typescript-eslint/unbound-method
1
2
  Promise.withResolvers ??= function <T>(): PromiseWithResolvers<T> {
2
3
  let _resolve: ((value: T | PromiseLike<T>) => void) | undefined,
3
4
  // eslint-disable-next-line @typescript-eslint/no-explicit-any