@zenfs/core 0.11.0 → 0.11.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.
@@ -16,7 +16,7 @@ import type { Stats } from '../stats.js';
16
16
  * @internal
17
17
  */
18
18
  export class LockedFS<FS extends FileSystem> implements FileSystem {
19
- private _mu: Mutex = new Mutex();
19
+ private mutex: Mutex = new Mutex();
20
20
 
21
21
  constructor(public readonly fs: FS) {}
22
22
 
@@ -32,148 +32,148 @@ export class LockedFS<FS extends FileSystem> implements FileSystem {
32
32
  }
33
33
 
34
34
  public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
35
- await this._mu.lock(oldPath);
35
+ await this.mutex.lock(oldPath);
36
36
  await this.fs.rename(oldPath, newPath, cred);
37
- this._mu.unlock(oldPath);
37
+ this.mutex.unlock(oldPath);
38
38
  }
39
39
 
40
40
  public renameSync(oldPath: string, newPath: string, cred: Cred): void {
41
- if (this._mu.isLocked(oldPath)) {
41
+ if (this.mutex.isLocked(oldPath)) {
42
42
  throw ErrnoError.With('EBUSY', oldPath, 'rename');
43
43
  }
44
44
  return this.fs.renameSync(oldPath, newPath, cred);
45
45
  }
46
46
 
47
47
  public async stat(path: string, cred: Cred): Promise<Stats> {
48
- await this._mu.lock(path);
48
+ await this.mutex.lock(path);
49
49
  const stats = await this.fs.stat(path, cred);
50
- this._mu.unlock(path);
50
+ this.mutex.unlock(path);
51
51
  return stats;
52
52
  }
53
53
 
54
54
  public statSync(path: string, cred: Cred): Stats {
55
- if (this._mu.isLocked(path)) {
55
+ if (this.mutex.isLocked(path)) {
56
56
  throw ErrnoError.With('EBUSY', path, 'stat');
57
57
  }
58
58
  return this.fs.statSync(path, cred);
59
59
  }
60
60
 
61
61
  public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
62
- await this._mu.lock(path);
62
+ await this.mutex.lock(path);
63
63
  const fd = await this.fs.openFile(path, flag, cred);
64
- this._mu.unlock(path);
64
+ this.mutex.unlock(path);
65
65
  return fd;
66
66
  }
67
67
 
68
68
  public openFileSync(path: string, flag: string, cred: Cred): File {
69
- if (this._mu.isLocked(path)) {
69
+ if (this.mutex.isLocked(path)) {
70
70
  throw ErrnoError.With('EBUSY', path, 'openFile');
71
71
  }
72
72
  return this.fs.openFileSync(path, flag, cred);
73
73
  }
74
74
 
75
75
  public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
76
- await this._mu.lock(path);
76
+ await this.mutex.lock(path);
77
77
  const fd = await this.fs.createFile(path, flag, mode, cred);
78
- this._mu.unlock(path);
78
+ this.mutex.unlock(path);
79
79
  return fd;
80
80
  }
81
81
 
82
82
  public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
83
- if (this._mu.isLocked(path)) {
83
+ if (this.mutex.isLocked(path)) {
84
84
  throw ErrnoError.With('EBUSY', path, 'createFile');
85
85
  }
86
86
  return this.fs.createFileSync(path, flag, mode, cred);
87
87
  }
88
88
 
89
89
  public async unlink(path: string, cred: Cred): Promise<void> {
90
- await this._mu.lock(path);
90
+ await this.mutex.lock(path);
91
91
  await this.fs.unlink(path, cred);
92
- this._mu.unlock(path);
92
+ this.mutex.unlock(path);
93
93
  }
94
94
 
95
95
  public unlinkSync(path: string, cred: Cred): void {
96
- if (this._mu.isLocked(path)) {
96
+ if (this.mutex.isLocked(path)) {
97
97
  throw ErrnoError.With('EBUSY', path, 'unlink');
98
98
  }
99
99
  return this.fs.unlinkSync(path, cred);
100
100
  }
101
101
 
102
102
  public async rmdir(path: string, cred: Cred): Promise<void> {
103
- await this._mu.lock(path);
103
+ await this.mutex.lock(path);
104
104
  await this.fs.rmdir(path, cred);
105
- this._mu.unlock(path);
105
+ this.mutex.unlock(path);
106
106
  }
107
107
 
108
108
  public rmdirSync(path: string, cred: Cred): void {
109
- if (this._mu.isLocked(path)) {
109
+ if (this.mutex.isLocked(path)) {
110
110
  throw ErrnoError.With('EBUSY', path, 'rmdir');
111
111
  }
112
112
  return this.fs.rmdirSync(path, cred);
113
113
  }
114
114
 
115
115
  public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
116
- await this._mu.lock(path);
116
+ await this.mutex.lock(path);
117
117
  await this.fs.mkdir(path, mode, cred);
118
- this._mu.unlock(path);
118
+ this.mutex.unlock(path);
119
119
  }
120
120
 
121
121
  public mkdirSync(path: string, mode: number, cred: Cred): void {
122
- if (this._mu.isLocked(path)) {
122
+ if (this.mutex.isLocked(path)) {
123
123
  throw ErrnoError.With('EBUSY', path, 'mkdir');
124
124
  }
125
125
  return this.fs.mkdirSync(path, mode, cred);
126
126
  }
127
127
 
128
128
  public async readdir(path: string, cred: Cred): Promise<string[]> {
129
- await this._mu.lock(path);
129
+ await this.mutex.lock(path);
130
130
  const files = await this.fs.readdir(path, cred);
131
- this._mu.unlock(path);
131
+ this.mutex.unlock(path);
132
132
  return files;
133
133
  }
134
134
 
135
135
  public readdirSync(path: string, cred: Cred): string[] {
136
- if (this._mu.isLocked(path)) {
136
+ if (this.mutex.isLocked(path)) {
137
137
  throw ErrnoError.With('EBUSY', path, 'readdir');
138
138
  }
139
139
  return this.fs.readdirSync(path, cred);
140
140
  }
141
141
 
142
142
  public async exists(path: string, cred: Cred): Promise<boolean> {
143
- await this._mu.lock(path);
143
+ await this.mutex.lock(path);
144
144
  const exists = await this.fs.exists(path, cred);
145
- this._mu.unlock(path);
145
+ this.mutex.unlock(path);
146
146
  return exists;
147
147
  }
148
148
 
149
149
  public existsSync(path: string, cred: Cred): boolean {
150
- if (this._mu.isLocked(path)) {
150
+ if (this.mutex.isLocked(path)) {
151
151
  throw ErrnoError.With('EBUSY', path, 'exists');
152
152
  }
153
153
  return this.fs.existsSync(path, cred);
154
154
  }
155
155
 
156
156
  public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
157
- await this._mu.lock(srcpath);
157
+ await this.mutex.lock(srcpath);
158
158
  await this.fs.link(srcpath, dstpath, cred);
159
- this._mu.unlock(srcpath);
159
+ this.mutex.unlock(srcpath);
160
160
  }
161
161
 
162
162
  public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
163
- if (this._mu.isLocked(srcpath)) {
163
+ if (this.mutex.isLocked(srcpath)) {
164
164
  throw ErrnoError.With('EBUSY', srcpath, 'link');
165
165
  }
166
166
  return this.fs.linkSync(srcpath, dstpath, cred);
167
167
  }
168
168
 
169
169
  public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
170
- await this._mu.lock(path);
170
+ await this.mutex.lock(path);
171
171
  await this.fs.sync(path, data, stats);
172
- this._mu.unlock(path);
172
+ this.mutex.unlock(path);
173
173
  }
174
174
 
175
175
  public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
176
- if (this._mu.isLocked(path)) {
176
+ if (this.mutex.isLocked(path)) {
177
177
  throw ErrnoError.With('EBUSY', path, 'sync');
178
178
  }
179
179
  return this.fs.syncSync(path, data, stats);
@@ -41,4 +41,4 @@ export const InMemory = {
41
41
  create({ name }: { name?: string }) {
42
42
  return new StoreFS(new InMemoryStore(name));
43
43
  },
44
- } as const satisfies Backend<StoreFS, { name?: string }>;
44
+ } as const satisfies Backend<StoreFS<InMemoryStore>, { name?: string }>;
@@ -63,7 +63,8 @@ export class PortFile extends File {
63
63
  }
64
64
 
65
65
  public async read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>> {
66
- return (await this.rpc('read', buffer, offset, length, position)) as FileReadResult<TBuffer>;
66
+ const result = await this.rpc('read', buffer, offset, length, position);
67
+ return result as FileReadResult<TBuffer>;
67
68
  }
68
69
 
69
70
  public readSync(): number {
@@ -85,10 +85,11 @@ export function request<const TRequest extends Request, TValue>(
85
85
  const id = Math.random().toString(16).slice(10);
86
86
  executors.set(id, { resolve, reject, fs });
87
87
  port.postMessage({ ...request, _zenfs: true, id, stack });
88
- setTimeout(() => {
88
+ const _ = setTimeout(() => {
89
89
  const error = new ErrnoError(Errno.EIO, 'RPC Failed');
90
90
  error.stack += stack;
91
91
  reject(error);
92
+ if (typeof _ == 'object') _.unref();
92
93
  }, timeout);
93
94
  });
94
95
  }
@@ -12,22 +12,22 @@ import type { Store, Transaction } from './store.js';
12
12
  const maxInodeAllocTries = 5;
13
13
 
14
14
  /**
15
- * A synchronous key-value file system. Uses a SyncStore to store the data.
15
+ * A file system which uses a key-value store.
16
16
  *
17
17
  * We use a unique ID for each node in the file system. The root node has a fixed ID.
18
18
  * @todo Introduce Node ID caching.
19
19
  * @todo Check modes.
20
20
  * @internal
21
21
  */
22
- export class StoreFS extends FileSystem {
23
- protected get store(): Store {
22
+ export class StoreFS<T extends Store = Store> extends FileSystem {
23
+ protected get store(): T {
24
24
  if (!this._store) {
25
25
  throw new ErrnoError(Errno.ENODATA, 'No store attached');
26
26
  }
27
27
  return this._store;
28
28
  }
29
29
 
30
- protected _store?: Store;
30
+ protected _store?: T;
31
31
 
32
32
  private _initialized: boolean = false;
33
33
 
@@ -40,7 +40,7 @@ export class StoreFS extends FileSystem {
40
40
  this._store = await this.$store;
41
41
  }
42
42
 
43
- constructor(private $store: Store | Promise<Store>) {
43
+ constructor(private $store: T | Promise<T>) {
44
44
  super();
45
45
 
46
46
  if (!($store instanceof Promise)) {
package/src/config.ts CHANGED
@@ -58,7 +58,7 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
58
58
  throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
59
59
  }
60
60
  checkOptions(backend, config);
61
- const mount = backend.create(config);
61
+ const mount = await backend.create(config);
62
62
  await mount.ready();
63
63
  return mount;
64
64
  }
@@ -144,8 +144,7 @@ export class FileHandle implements promises.FileHandle {
144
144
  }
145
145
 
146
146
  const { size } = await this.stat();
147
- const data = new Uint8Array(size);
148
- await this.file.read(data, 0, size, 0);
147
+ const { buffer: data } = await this.file.read(new Uint8Array(size), 0, size, 0);
149
148
  const buffer = Buffer.from(data);
150
149
  return options.encoding ? buffer.toString(options.encoding) : buffer;
151
150
  }
@@ -16,7 +16,7 @@ type FileSystemMethod = {
16
16
  : never;
17
17
  }[keyof FileSystem]; // https://stackoverflow.com/a/76335220/17637456
18
18
 
19
- function doOp<M extends FileSystemMethod, RT extends ReturnType<M>>(...[name, resolveSymlinks, path, ...args]: Parameters<M>): RT {
19
+ function wrap<M extends FileSystemMethod, RT extends ReturnType<M>>(...[name, resolveSymlinks, path, ...args]: Parameters<M>): RT {
20
20
  path = normalizePath(path!);
21
21
  const { fs, path: resolvedPath } = resolveMount(resolveSymlinks && existsSync(path) ? realpathSync(path) : path);
22
22
  try {
@@ -78,7 +78,7 @@ existsSync satisfies typeof fs.existsSync;
78
78
  export function statSync(path: fs.PathLike, options?: { bigint?: boolean }): Stats;
79
79
  export function statSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
80
80
  export function statSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
81
- const stats: Stats = doOp('statSync', true, path.toString(), cred);
81
+ const stats: Stats = wrap('statSync', true, path.toString(), cred);
82
82
  return options?.bigint ? new BigIntStats(stats) : stats;
83
83
  }
84
84
  statSync satisfies typeof fs.statSync;
@@ -92,7 +92,7 @@ statSync satisfies typeof fs.statSync;
92
92
  export function lstatSync(path: fs.PathLike, options?: { bigint?: boolean }): Stats;
93
93
  export function lstatSync(path: fs.PathLike, options: { bigint: true }): BigIntStats;
94
94
  export function lstatSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats {
95
- const stats: Stats = doOp('statSync', false, path.toString(), cred);
95
+ const stats: Stats = wrap('statSync', false, path.toString(), cred);
96
96
  return options?.bigint ? new BigIntStats(stats) : stats;
97
97
  }
98
98
  lstatSync satisfies typeof fs.lstatSync;
@@ -117,7 +117,7 @@ truncateSync satisfies typeof fs.truncateSync;
117
117
  * @param path
118
118
  */
119
119
  export function unlinkSync(path: fs.PathLike): void {
120
- return doOp('unlinkSync', false, path.toString(), cred);
120
+ return wrap('unlinkSync', false, path.toString(), cred);
121
121
  }
122
122
  unlinkSync satisfies typeof fs.unlinkSync;
123
123
 
@@ -128,17 +128,17 @@ function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | nul
128
128
  // Check if the path exists, and is a file.
129
129
  let stats: Stats;
130
130
  try {
131
- stats = doOp('statSync', resolveSymlinks, path, cred);
131
+ stats = wrap('statSync', resolveSymlinks, path, cred);
132
132
  } catch (e) {
133
133
  // File does not exist.
134
134
  switch (pathNotExistsAction(flag)) {
135
135
  case ActionType.CREATE:
136
136
  // Ensure parent exists.
137
- const parentStats: Stats = doOp('statSync', resolveSymlinks, dirname(path), cred);
137
+ const parentStats: Stats = wrap('statSync', resolveSymlinks, dirname(path), cred);
138
138
  if (!parentStats.isDirectory()) {
139
139
  throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
140
140
  }
141
- return doOp('createFileSync', resolveSymlinks, path, flag, mode, cred);
141
+ return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred);
142
142
  case ActionType.THROW:
143
143
  throw ErrnoError.With('ENOENT', path, '_open');
144
144
  default:
@@ -155,16 +155,16 @@ function _openSync(_path: fs.PathLike, _flag: fs.OpenMode, _mode?: fs.Mode | nul
155
155
  throw ErrnoError.With('EEXIST', path, '_open');
156
156
  case ActionType.TRUNCATE:
157
157
  // Delete file.
158
- doOp('unlinkSync', resolveSymlinks, path, cred);
158
+ wrap('unlinkSync', resolveSymlinks, path, cred);
159
159
  /*
160
160
  Create file. Use the same mode as the old file.
161
161
  Node itself modifies the ctime when this occurs, so this action
162
162
  will preserve that behavior if the underlying file system
163
163
  supports those properties.
164
164
  */
165
- return doOp('createFileSync', resolveSymlinks, path, flag, stats.mode, cred);
165
+ return wrap('createFileSync', resolveSymlinks, path, flag, stats.mode, cred);
166
166
  case ActionType.NOP:
167
- return doOp('openFileSync', resolveSymlinks, path, flag, cred);
167
+ return wrap('openFileSync', resolveSymlinks, path, flag, cred);
168
168
  default:
169
169
  throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
170
170
  }
@@ -230,21 +230,6 @@ export function readFileSync(path: fs.PathOrFileDescriptor, _options: fs.WriteFi
230
230
  }
231
231
  readFileSync satisfies typeof fs.readFileSync;
232
232
 
233
- /**
234
- * Synchronously writes data to a file, replacing the file
235
- * if it already exists.
236
- *
237
- * The encoding option is ignored if data is a buffer.
238
- */
239
- function _writeFileSync(fname: string, data: Uint8Array, flag: string, mode: number, resolveSymlinks: boolean): void {
240
- const file = _openSync(fname, flag, mode, resolveSymlinks);
241
- try {
242
- file.writeSync(data, 0, data.byteLength, 0);
243
- } finally {
244
- file.closeSync();
245
- }
246
- }
247
-
248
233
  /**
249
234
  * Synchronously writes data to a file, replacing the file if it already
250
235
  * exists.
@@ -272,22 +257,14 @@ export function writeFileSync(path: fs.PathOrFileDescriptor, data: FileContents,
272
257
  if (!encodedData) {
273
258
  throw new ErrnoError(Errno.EINVAL, 'Data not specified');
274
259
  }
275
- _writeFileSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), encodedData, options.flag, options.mode, true);
276
- }
277
- writeFileSync satisfies typeof fs.writeFileSync;
278
-
279
- /**
280
- * Synchronously append data to a file, creating the file if
281
- * it not yet exists.
282
- */
283
- function _appendFileSync(fname: string, data: Uint8Array, flag: string, mode: number, resolveSymlinks: boolean): void {
284
- const file = _openSync(fname, flag, mode, resolveSymlinks);
260
+ const file = _openSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), flag, options.mode, true);
285
261
  try {
286
- file.writeSync(data, 0, data.byteLength, null);
262
+ file.writeSync(encodedData, 0, encodedData.byteLength, 0);
287
263
  } finally {
288
264
  file.closeSync();
289
265
  }
290
266
  }
267
+ writeFileSync satisfies typeof fs.writeFileSync;
291
268
 
292
269
  /**
293
270
  * Asynchronously append data to a file, creating the file if it not yet
@@ -310,7 +287,12 @@ export function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileCont
310
287
  throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
311
288
  }
312
289
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
313
- _appendFileSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), encodedData, options.flag, options.mode, true);
290
+ const file = _openSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), flag, options.mode, true);
291
+ try {
292
+ file.writeSync(encodedData, 0, encodedData.byteLength, null);
293
+ } finally {
294
+ file.closeSync();
295
+ }
314
296
  }
315
297
  appendFileSync satisfies typeof fs.appendFileSync;
316
298
 
@@ -481,7 +463,7 @@ futimesSync satisfies typeof fs.futimesSync;
481
463
  * @param path
482
464
  */
483
465
  export function rmdirSync(path: fs.PathLike): void {
484
- return doOp('rmdirSync', true, path.toString(), cred);
466
+ return wrap('rmdirSync', true, path.toString(), cred);
485
467
  }
486
468
  rmdirSync satisfies typeof fs.rmdirSync;
487
469
 
@@ -497,7 +479,7 @@ export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirector
497
479
  export function mkdirSync(path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions | null): string | undefined | void {
498
480
  const mode: fs.Mode | undefined = typeof options == 'number' || typeof options == 'string' ? options : options?.mode;
499
481
  const recursive = typeof options == 'object' && options?.recursive;
500
- doOp('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred);
482
+ wrap('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred);
501
483
  }
502
484
  mkdirSync satisfies typeof fs.mkdirSync;
503
485
 
@@ -514,7 +496,7 @@ export function readdirSync(
514
496
  options?: { recursive?: boolean; encoding?: BufferEncoding | 'buffer' | null; withFileTypes?: boolean } | BufferEncoding | 'buffer' | null
515
497
  ): string[] | Dirent[] | Buffer[] {
516
498
  path = normalizePath(path);
517
- const entries: string[] = doOp('readdirSync', true, path, cred);
499
+ const entries: string[] = wrap('readdirSync', true, path, cred);
518
500
  for (const mount of mounts.keys()) {
519
501
  if (!mount.startsWith(path)) {
520
502
  continue;
@@ -549,7 +531,7 @@ readdirSync satisfies typeof fs.readdirSync;
549
531
  */
550
532
  export function linkSync(existing: fs.PathLike, newpath: fs.PathLike): void {
551
533
  newpath = normalizePath(newpath);
552
- return doOp('linkSync', false, existing.toString(), newpath.toString(), cred);
534
+ return wrap('linkSync', false, existing.toString(), newpath.toString(), cred);
553
535
  }
554
536
  linkSync satisfies typeof fs.linkSync;
555
537
 
package/src/filesystem.ts CHANGED
@@ -197,7 +197,7 @@ export abstract class FileSystem {
197
197
  /**
198
198
  * @internal
199
199
  */
200
- declare abstract class SyncFileSystem extends FileSystem {
200
+ declare abstract class SyncFS extends FileSystem {
201
201
  metadata(): FileSystemMetadata;
202
202
  ready(): Promise<void>;
203
203
  exists(path: string, cred: Cred): Promise<boolean>;
@@ -217,8 +217,8 @@ declare abstract class SyncFileSystem extends FileSystem {
217
217
  * Implements the asynchronous API in terms of the synchronous API.
218
218
  */
219
219
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
220
- export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFileSystem) & T {
221
- abstract class _SyncFileSystem extends FS implements SyncFileSystem {
220
+ export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFS) & T {
221
+ abstract class _SyncFS extends FS implements SyncFS {
222
222
  public async exists(path: string, cred: Cred): Promise<boolean> {
223
223
  return this.existsSync(path, cred);
224
224
  }
@@ -263,13 +263,13 @@ export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS:
263
263
  return this.syncSync(path, data, stats);
264
264
  }
265
265
  }
266
- return _SyncFileSystem;
266
+ return _SyncFS;
267
267
  }
268
268
 
269
269
  /**
270
270
  * @internal
271
271
  */
272
- declare abstract class AsyncFileSystem extends FileSystem {
272
+ declare abstract class AsyncFS extends FileSystem {
273
273
  /**
274
274
  * @hidden
275
275
  */
@@ -312,8 +312,8 @@ type AsyncOperation = {
312
312
  *
313
313
  */
314
314
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
315
- export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFileSystem) & T {
316
- abstract class _AsyncFileSystem extends FS implements AsyncFileSystem {
315
+ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFS) & T {
316
+ abstract class _AsyncFS extends FS implements AsyncFS {
317
317
  /**
318
318
  * Queue of pending asynchronous operations.
319
319
  */
@@ -359,18 +359,19 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
359
359
  }
360
360
 
361
361
  public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
362
- const file = this._sync.createFileSync(path, flag, mode, cred);
362
+ this._sync.createFileSync(path, flag, mode, cred);
363
363
  this.queue('createFile', path, flag, mode, cred);
364
+ return this.openFileSync(path, flag, cred);
365
+ }
366
+
367
+ public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
368
+ const file = this._sync.openFileSync(path, flag, cred);
364
369
  const stats = file.statSync();
365
370
  const buffer = new Uint8Array(stats.size);
366
371
  file.readSync(buffer);
367
372
  return new PreloadFile(this, path, flag, stats, buffer);
368
373
  }
369
374
 
370
- public openFileSync(path: string, flag: string, cred: Cred): File {
371
- return this._sync.openFileSync(path, flag, cred);
372
- }
373
-
374
375
  public unlinkSync(path: string, cred: Cred): void {
375
376
  this._sync.unlinkSync(path, cred);
376
377
  this.queue('unlink', path, cred);
@@ -455,13 +456,13 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
455
456
  }
456
457
  }
457
458
 
458
- return _AsyncFileSystem;
459
+ return _AsyncFS;
459
460
  }
460
461
 
461
462
  /**
462
463
  * @internal
463
464
  */
464
- declare abstract class ReadonlyFileSystem extends FileSystem {
465
+ declare abstract class ReadonlyFS extends FileSystem {
465
466
  metadata(): FileSystemMetadata;
466
467
  rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
467
468
  renameSync(oldPath: string, newPath: string, cred: Cred): void;
@@ -483,8 +484,8 @@ declare abstract class ReadonlyFileSystem extends FileSystem {
483
484
  * Implements the non-readonly methods to throw `EROFS`
484
485
  */
485
486
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
486
- export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFileSystem) & T {
487
- abstract class _ReadonlyFileSystem extends FS implements ReadonlyFileSystem {
487
+ export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFS) & T {
488
+ abstract class _ReadonlyFS extends FS implements ReadonlyFS {
488
489
  public metadata(): FileSystemMetadata {
489
490
  return { ...super.metadata(), readonly: true };
490
491
  }
@@ -546,5 +547,5 @@ export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(
546
547
  }
547
548
  /* eslint-enable @typescript-eslint/no-unused-vars */
548
549
  }
549
- return _ReadonlyFileSystem;
550
+ return _ReadonlyFS;
550
551
  }
package/src/index.ts CHANGED
@@ -6,6 +6,8 @@ export * from './backends/Index.js';
6
6
  export * from './backends/locked.js';
7
7
  export * from './backends/overlay.js';
8
8
  export * from './backends/store/fs.js';
9
+ export * from './backends/store/simple.js';
10
+ export * from './backends/store/store.js';
9
11
  export * from './backends/backend.js';
10
12
  export * from './config.js';
11
13
  export * from './cred.js';
package/src/mutex.ts CHANGED
@@ -5,24 +5,24 @@ import { ErrnoError, Errno } from './error.js';
5
5
  * @internal
6
6
  */
7
7
  export class Mutex {
8
- private _locks: Map<string, (() => void)[]> = new Map();
8
+ protected locks: Map<string, (() => void)[]> = new Map();
9
9
 
10
10
  public lock(path: string): Promise<void> {
11
11
  return new Promise(resolve => {
12
- if (this._locks.has(path)) {
13
- this._locks.get(path)!.push(resolve);
12
+ if (this.locks.has(path)) {
13
+ this.locks.get(path)!.push(resolve);
14
14
  } else {
15
- this._locks.set(path, [resolve]);
15
+ this.locks.set(path, [resolve]);
16
16
  }
17
17
  });
18
18
  }
19
19
 
20
20
  public unlock(path: string): void {
21
- if (!this._locks.has(path)) {
21
+ if (!this.locks.has(path)) {
22
22
  throw new ErrnoError(Errno.EPERM, 'Can not unlock an already unlocked path', path);
23
23
  }
24
24
 
25
- const next = this._locks.get(path)?.shift();
25
+ const next = this.locks.get(path)?.shift();
26
26
  /*
27
27
  don't unlock - we want to queue up next for the
28
28
  end of the current task execution, but we don't
@@ -32,23 +32,23 @@ export class Mutex {
32
32
  lock won't cause starvation.
33
33
  */
34
34
  if (next) {
35
- setTimeout(next, 0);
35
+ setTimeout(next);
36
36
  return;
37
37
  }
38
38
 
39
- this._locks.delete(path);
39
+ this.locks.delete(path);
40
40
  }
41
41
 
42
42
  public tryLock(path: string): boolean {
43
- if (this._locks.has(path)) {
43
+ if (this.locks.has(path)) {
44
44
  return false;
45
45
  }
46
46
 
47
- this._locks.set(path, []);
47
+ this.locks.set(path, []);
48
48
  return true;
49
49
  }
50
50
 
51
51
  public isLocked(path: string): boolean {
52
- return this._locks.has(path);
52
+ return this.locks.has(path);
53
53
  }
54
54
  }