@zenfs/core 0.13.0 → 0.14.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.
@@ -1,7 +1,11 @@
1
1
  import type { Cred } from '../cred.js';
2
2
  import type { File } from '../file.js';
3
- import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
3
+ import type { FileSystemMetadata } from '../filesystem.js';
4
+ import { FileSystem } from '../filesystem.js';
4
5
  import type { Stats } from '../stats.js';
6
+ export interface MutexLock extends PromiseWithResolvers<void> {
7
+ [Symbol.dispose](): void;
8
+ }
5
9
  /**
6
10
  * This class serializes access to an underlying async filesystem.
7
11
  * For example, on an OverlayFS instance with an async lower
@@ -14,8 +18,37 @@ import type { Stats } from '../stats.js';
14
18
  */
15
19
  export declare class LockedFS<FS extends FileSystem> implements FileSystem {
16
20
  readonly fs: FS;
17
- private mutex;
18
21
  constructor(fs: FS);
22
+ /**
23
+ * The current locks
24
+ */
25
+ private locks;
26
+ protected addLock(path: string): MutexLock;
27
+ /**
28
+ * Locks `path` asynchronously.
29
+ * If the path is currently locked, waits for it to be unlocked.
30
+ * @internal
31
+ */
32
+ lock(path: string): Promise<MutexLock>;
33
+ /**
34
+ * Locks `path` asynchronously.
35
+ * If the path is currently locked, an error will be thrown
36
+ * @internal
37
+ */
38
+ lockSync(path: string): MutexLock;
39
+ /**
40
+ * Unlocks a path
41
+ * @param path The path to lock
42
+ * @param noThrow If true, an error will not be thrown if the path is already unlocked
43
+ * @returns Whether the path was unlocked
44
+ * @internal
45
+ */
46
+ unlock(path: string, noThrow?: boolean): boolean;
47
+ /**
48
+ * Whether `path` is locked
49
+ * @internal
50
+ */
51
+ isLocked(path: string): boolean;
19
52
  ready(): Promise<void>;
20
53
  metadata(): FileSystemMetadata;
21
54
  rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
@@ -41,3 +74,18 @@ export declare class LockedFS<FS extends FileSystem> implements FileSystem {
41
74
  sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
42
75
  syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
43
76
  }
77
+ export declare const Locked: {
78
+ name: string;
79
+ options: {
80
+ fs: {
81
+ type: "object";
82
+ required: true;
83
+ description: string;
84
+ validator(fs: FileSystem): void;
85
+ };
86
+ };
87
+ isAvailable(): true;
88
+ create({ fs }: {
89
+ fs: FileSystem;
90
+ } & Partial<import("./backend.js").SharedConfig>): LockedFS<FileSystem>;
91
+ };
@@ -1,5 +1,50 @@
1
- import { ErrnoError } from '../error.js';
2
- import { Mutex } from '../mutex.js';
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ }
13
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
14
+ env.stack.push({ value: value, dispose: dispose, async: async });
15
+ }
16
+ else if (async) {
17
+ env.stack.push({ async: true });
18
+ }
19
+ return value;
20
+ };
21
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
22
+ return function (env) {
23
+ function fail(e) {
24
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
25
+ env.hasError = true;
26
+ }
27
+ function next() {
28
+ while (env.stack.length) {
29
+ var rec = env.stack.pop();
30
+ try {
31
+ var result = rec.dispose && rec.dispose.call(rec.value);
32
+ if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
33
+ }
34
+ catch (e) {
35
+ fail(e);
36
+ }
37
+ }
38
+ if (env.hasError) throw env.error;
39
+ }
40
+ return next();
41
+ };
42
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
43
+ var e = new Error(message);
44
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
45
+ });
46
+ import { Errno, ErrnoError } from '../error.js';
47
+ import { FileSystem } from '../filesystem.js';
3
48
  /**
4
49
  * This class serializes access to an underlying async filesystem.
5
50
  * For example, on an OverlayFS instance with an async lower
@@ -13,7 +58,70 @@ import { Mutex } from '../mutex.js';
13
58
  export class LockedFS {
14
59
  constructor(fs) {
15
60
  this.fs = fs;
16
- this.mutex = new Mutex();
61
+ /**
62
+ * The current locks
63
+ */
64
+ this.locks = new Map();
65
+ }
66
+ addLock(path) {
67
+ const lock = {
68
+ ...Promise.withResolvers(),
69
+ [Symbol.dispose]: () => {
70
+ this.unlock(path);
71
+ },
72
+ };
73
+ this.locks.set(path, lock);
74
+ return lock;
75
+ }
76
+ /**
77
+ * Locks `path` asynchronously.
78
+ * If the path is currently locked, waits for it to be unlocked.
79
+ * @internal
80
+ */
81
+ async lock(path) {
82
+ if (this.locks.has(path)) {
83
+ // Non-null assertion: we already checked locks has path
84
+ await this.locks.get(path).promise;
85
+ }
86
+ return this.addLock(path);
87
+ }
88
+ /**
89
+ * Locks `path` asynchronously.
90
+ * If the path is currently locked, an error will be thrown
91
+ * @internal
92
+ */
93
+ lockSync(path) {
94
+ if (this.locks.has(path)) {
95
+ // Non-null assertion: we already checked locks has path
96
+ throw ErrnoError.With('EBUSY', path, 'lockSync');
97
+ }
98
+ return this.addLock(path);
99
+ }
100
+ /**
101
+ * Unlocks a path
102
+ * @param path The path to lock
103
+ * @param noThrow If true, an error will not be thrown if the path is already unlocked
104
+ * @returns Whether the path was unlocked
105
+ * @internal
106
+ */
107
+ unlock(path, noThrow = false) {
108
+ if (!this.locks.has(path)) {
109
+ if (noThrow) {
110
+ return false;
111
+ }
112
+ throw new ErrnoError(Errno.EPERM, 'Can not unlock an already unlocked path', path);
113
+ }
114
+ // Non-null assertion: we already checked locks has path
115
+ this.locks.get(path).resolve();
116
+ this.locks.delete(path);
117
+ return true;
118
+ }
119
+ /**
120
+ * Whether `path` is locked
121
+ * @internal
122
+ */
123
+ isLocked(path) {
124
+ return this.locks.has(path);
17
125
  }
18
126
  async ready() {
19
127
  await this.fs.ready();
@@ -25,129 +133,354 @@ export class LockedFS {
25
133
  };
26
134
  }
27
135
  async rename(oldPath, newPath, cred) {
28
- await this.mutex.lock(oldPath);
29
- await this.fs.rename(oldPath, newPath, cred);
30
- this.mutex.unlock(oldPath);
136
+ const env_1 = { stack: [], error: void 0, hasError: false };
137
+ try {
138
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
139
+ const _ = __addDisposableResource(env_1, await this.lock(oldPath), false);
140
+ await this.fs.rename(oldPath, newPath, cred);
141
+ }
142
+ catch (e_1) {
143
+ env_1.error = e_1;
144
+ env_1.hasError = true;
145
+ }
146
+ finally {
147
+ __disposeResources(env_1);
148
+ }
31
149
  }
32
150
  renameSync(oldPath, newPath, cred) {
33
- if (this.mutex.isLocked(oldPath)) {
34
- throw ErrnoError.With('EBUSY', oldPath, 'rename');
151
+ const env_2 = { stack: [], error: void 0, hasError: false };
152
+ try {
153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
154
+ const _ = __addDisposableResource(env_2, this.lockSync(oldPath), false);
155
+ return this.fs.renameSync(oldPath, newPath, cred);
156
+ }
157
+ catch (e_2) {
158
+ env_2.error = e_2;
159
+ env_2.hasError = true;
160
+ }
161
+ finally {
162
+ __disposeResources(env_2);
35
163
  }
36
- return this.fs.renameSync(oldPath, newPath, cred);
37
164
  }
38
165
  async stat(path, cred) {
39
- await this.mutex.lock(path);
40
- const stats = await this.fs.stat(path, cred);
41
- this.mutex.unlock(path);
42
- return stats;
166
+ const env_3 = { stack: [], error: void 0, hasError: false };
167
+ try {
168
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
169
+ const _ = __addDisposableResource(env_3, await this.lock(path), false);
170
+ return await this.fs.stat(path, cred);
171
+ }
172
+ catch (e_3) {
173
+ env_3.error = e_3;
174
+ env_3.hasError = true;
175
+ }
176
+ finally {
177
+ __disposeResources(env_3);
178
+ }
43
179
  }
44
180
  statSync(path, cred) {
45
- if (this.mutex.isLocked(path)) {
46
- throw ErrnoError.With('EBUSY', path, 'stat');
181
+ const env_4 = { stack: [], error: void 0, hasError: false };
182
+ try {
183
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
184
+ const _ = __addDisposableResource(env_4, this.lockSync(path), false);
185
+ return this.fs.statSync(path, cred);
186
+ }
187
+ catch (e_4) {
188
+ env_4.error = e_4;
189
+ env_4.hasError = true;
190
+ }
191
+ finally {
192
+ __disposeResources(env_4);
47
193
  }
48
- return this.fs.statSync(path, cred);
49
194
  }
50
195
  async openFile(path, flag, cred) {
51
- await this.mutex.lock(path);
52
- const fd = await this.fs.openFile(path, flag, cred);
53
- this.mutex.unlock(path);
54
- return fd;
196
+ const env_5 = { stack: [], error: void 0, hasError: false };
197
+ try {
198
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
199
+ const _ = __addDisposableResource(env_5, await this.lock(path), false);
200
+ return await this.fs.openFile(path, flag, cred);
201
+ }
202
+ catch (e_5) {
203
+ env_5.error = e_5;
204
+ env_5.hasError = true;
205
+ }
206
+ finally {
207
+ __disposeResources(env_5);
208
+ }
55
209
  }
56
210
  openFileSync(path, flag, cred) {
57
- if (this.mutex.isLocked(path)) {
58
- throw ErrnoError.With('EBUSY', path, 'openFile');
211
+ const env_6 = { stack: [], error: void 0, hasError: false };
212
+ try {
213
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
214
+ const _ = __addDisposableResource(env_6, this.lockSync(path), false);
215
+ return this.fs.openFileSync(path, flag, cred);
216
+ }
217
+ catch (e_6) {
218
+ env_6.error = e_6;
219
+ env_6.hasError = true;
220
+ }
221
+ finally {
222
+ __disposeResources(env_6);
59
223
  }
60
- return this.fs.openFileSync(path, flag, cred);
61
224
  }
62
225
  async createFile(path, flag, mode, cred) {
63
- await this.mutex.lock(path);
64
- const fd = await this.fs.createFile(path, flag, mode, cred);
65
- this.mutex.unlock(path);
66
- return fd;
226
+ const env_7 = { stack: [], error: void 0, hasError: false };
227
+ try {
228
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
229
+ const _ = __addDisposableResource(env_7, await this.lock(path), false);
230
+ return await this.fs.createFile(path, flag, mode, cred);
231
+ }
232
+ catch (e_7) {
233
+ env_7.error = e_7;
234
+ env_7.hasError = true;
235
+ }
236
+ finally {
237
+ __disposeResources(env_7);
238
+ }
67
239
  }
68
240
  createFileSync(path, flag, mode, cred) {
69
- if (this.mutex.isLocked(path)) {
70
- throw ErrnoError.With('EBUSY', path, 'createFile');
241
+ const env_8 = { stack: [], error: void 0, hasError: false };
242
+ try {
243
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
244
+ const _ = __addDisposableResource(env_8, this.lockSync(path), false);
245
+ return this.fs.createFileSync(path, flag, mode, cred);
246
+ }
247
+ catch (e_8) {
248
+ env_8.error = e_8;
249
+ env_8.hasError = true;
250
+ }
251
+ finally {
252
+ __disposeResources(env_8);
71
253
  }
72
- return this.fs.createFileSync(path, flag, mode, cred);
73
254
  }
74
255
  async unlink(path, cred) {
75
- await this.mutex.lock(path);
76
- await this.fs.unlink(path, cred);
77
- this.mutex.unlock(path);
256
+ const env_9 = { stack: [], error: void 0, hasError: false };
257
+ try {
258
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
259
+ const _ = __addDisposableResource(env_9, await this.lock(path), false);
260
+ await this.fs.unlink(path, cred);
261
+ }
262
+ catch (e_9) {
263
+ env_9.error = e_9;
264
+ env_9.hasError = true;
265
+ }
266
+ finally {
267
+ __disposeResources(env_9);
268
+ }
78
269
  }
79
270
  unlinkSync(path, cred) {
80
- if (this.mutex.isLocked(path)) {
81
- throw ErrnoError.With('EBUSY', path, 'unlink');
271
+ const env_10 = { stack: [], error: void 0, hasError: false };
272
+ try {
273
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
274
+ const _ = __addDisposableResource(env_10, this.lockSync(path), false);
275
+ return this.fs.unlinkSync(path, cred);
276
+ }
277
+ catch (e_10) {
278
+ env_10.error = e_10;
279
+ env_10.hasError = true;
280
+ }
281
+ finally {
282
+ __disposeResources(env_10);
82
283
  }
83
- return this.fs.unlinkSync(path, cred);
84
284
  }
85
285
  async rmdir(path, cred) {
86
- await this.mutex.lock(path);
87
- await this.fs.rmdir(path, cred);
88
- this.mutex.unlock(path);
286
+ const env_11 = { stack: [], error: void 0, hasError: false };
287
+ try {
288
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
289
+ const _ = __addDisposableResource(env_11, await this.lock(path), false);
290
+ await this.fs.rmdir(path, cred);
291
+ }
292
+ catch (e_11) {
293
+ env_11.error = e_11;
294
+ env_11.hasError = true;
295
+ }
296
+ finally {
297
+ __disposeResources(env_11);
298
+ }
89
299
  }
90
300
  rmdirSync(path, cred) {
91
- if (this.mutex.isLocked(path)) {
92
- throw ErrnoError.With('EBUSY', path, 'rmdir');
301
+ const env_12 = { stack: [], error: void 0, hasError: false };
302
+ try {
303
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
304
+ const _ = __addDisposableResource(env_12, this.lockSync(path), false);
305
+ return this.fs.rmdirSync(path, cred);
306
+ }
307
+ catch (e_12) {
308
+ env_12.error = e_12;
309
+ env_12.hasError = true;
310
+ }
311
+ finally {
312
+ __disposeResources(env_12);
93
313
  }
94
- return this.fs.rmdirSync(path, cred);
95
314
  }
96
315
  async mkdir(path, mode, cred) {
97
- await this.mutex.lock(path);
98
- await this.fs.mkdir(path, mode, cred);
99
- this.mutex.unlock(path);
316
+ const env_13 = { stack: [], error: void 0, hasError: false };
317
+ try {
318
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
319
+ const _ = __addDisposableResource(env_13, await this.lock(path), false);
320
+ await this.fs.mkdir(path, mode, cred);
321
+ }
322
+ catch (e_13) {
323
+ env_13.error = e_13;
324
+ env_13.hasError = true;
325
+ }
326
+ finally {
327
+ __disposeResources(env_13);
328
+ }
100
329
  }
101
330
  mkdirSync(path, mode, cred) {
102
- if (this.mutex.isLocked(path)) {
103
- throw ErrnoError.With('EBUSY', path, 'mkdir');
331
+ const env_14 = { stack: [], error: void 0, hasError: false };
332
+ try {
333
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
334
+ const _ = __addDisposableResource(env_14, this.lockSync(path), false);
335
+ return this.fs.mkdirSync(path, mode, cred);
336
+ }
337
+ catch (e_14) {
338
+ env_14.error = e_14;
339
+ env_14.hasError = true;
340
+ }
341
+ finally {
342
+ __disposeResources(env_14);
104
343
  }
105
- return this.fs.mkdirSync(path, mode, cred);
106
344
  }
107
345
  async readdir(path, cred) {
108
- await this.mutex.lock(path);
109
- const files = await this.fs.readdir(path, cred);
110
- this.mutex.unlock(path);
111
- return files;
346
+ const env_15 = { stack: [], error: void 0, hasError: false };
347
+ try {
348
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
349
+ const _ = __addDisposableResource(env_15, await this.lock(path), false);
350
+ return await this.fs.readdir(path, cred);
351
+ }
352
+ catch (e_15) {
353
+ env_15.error = e_15;
354
+ env_15.hasError = true;
355
+ }
356
+ finally {
357
+ __disposeResources(env_15);
358
+ }
112
359
  }
113
360
  readdirSync(path, cred) {
114
- if (this.mutex.isLocked(path)) {
115
- throw ErrnoError.With('EBUSY', path, 'readdir');
361
+ const env_16 = { stack: [], error: void 0, hasError: false };
362
+ try {
363
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
364
+ const _ = __addDisposableResource(env_16, this.lockSync(path), false);
365
+ return this.fs.readdirSync(path, cred);
366
+ }
367
+ catch (e_16) {
368
+ env_16.error = e_16;
369
+ env_16.hasError = true;
370
+ }
371
+ finally {
372
+ __disposeResources(env_16);
116
373
  }
117
- return this.fs.readdirSync(path, cred);
118
374
  }
119
375
  async exists(path, cred) {
120
- await this.mutex.lock(path);
121
- const exists = await this.fs.exists(path, cred);
122
- this.mutex.unlock(path);
123
- return exists;
376
+ const env_17 = { stack: [], error: void 0, hasError: false };
377
+ try {
378
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
379
+ const _ = __addDisposableResource(env_17, await this.lock(path), false);
380
+ return await this.fs.exists(path, cred);
381
+ }
382
+ catch (e_17) {
383
+ env_17.error = e_17;
384
+ env_17.hasError = true;
385
+ }
386
+ finally {
387
+ __disposeResources(env_17);
388
+ }
124
389
  }
125
390
  existsSync(path, cred) {
126
- if (this.mutex.isLocked(path)) {
127
- throw ErrnoError.With('EBUSY', path, 'exists');
391
+ const env_18 = { stack: [], error: void 0, hasError: false };
392
+ try {
393
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
394
+ const _ = __addDisposableResource(env_18, this.lockSync(path), false);
395
+ return this.fs.existsSync(path, cred);
396
+ }
397
+ catch (e_18) {
398
+ env_18.error = e_18;
399
+ env_18.hasError = true;
400
+ }
401
+ finally {
402
+ __disposeResources(env_18);
128
403
  }
129
- return this.fs.existsSync(path, cred);
130
404
  }
131
405
  async link(srcpath, dstpath, cred) {
132
- await this.mutex.lock(srcpath);
133
- await this.fs.link(srcpath, dstpath, cred);
134
- this.mutex.unlock(srcpath);
406
+ const env_19 = { stack: [], error: void 0, hasError: false };
407
+ try {
408
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
409
+ const _ = __addDisposableResource(env_19, await this.lock(srcpath), false);
410
+ await this.fs.link(srcpath, dstpath, cred);
411
+ }
412
+ catch (e_19) {
413
+ env_19.error = e_19;
414
+ env_19.hasError = true;
415
+ }
416
+ finally {
417
+ __disposeResources(env_19);
418
+ }
135
419
  }
136
420
  linkSync(srcpath, dstpath, cred) {
137
- if (this.mutex.isLocked(srcpath)) {
138
- throw ErrnoError.With('EBUSY', srcpath, 'link');
421
+ const env_20 = { stack: [], error: void 0, hasError: false };
422
+ try {
423
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
424
+ const _ = __addDisposableResource(env_20, this.lockSync(srcpath), false);
425
+ return this.fs.linkSync(srcpath, dstpath, cred);
426
+ }
427
+ catch (e_20) {
428
+ env_20.error = e_20;
429
+ env_20.hasError = true;
430
+ }
431
+ finally {
432
+ __disposeResources(env_20);
139
433
  }
140
- return this.fs.linkSync(srcpath, dstpath, cred);
141
434
  }
142
435
  async sync(path, data, stats) {
143
- await this.mutex.lock(path);
144
- await this.fs.sync(path, data, stats);
145
- this.mutex.unlock(path);
436
+ const env_21 = { stack: [], error: void 0, hasError: false };
437
+ try {
438
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
439
+ const _ = __addDisposableResource(env_21, await this.lock(path), false);
440
+ await this.fs.sync(path, data, stats);
441
+ }
442
+ catch (e_21) {
443
+ env_21.error = e_21;
444
+ env_21.hasError = true;
445
+ }
446
+ finally {
447
+ __disposeResources(env_21);
448
+ }
146
449
  }
147
450
  syncSync(path, data, stats) {
148
- if (this.mutex.isLocked(path)) {
149
- throw ErrnoError.With('EBUSY', path, 'sync');
451
+ const env_22 = { stack: [], error: void 0, hasError: false };
452
+ try {
453
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
454
+ const _ = __addDisposableResource(env_22, this.lockSync(path), false);
455
+ return this.fs.syncSync(path, data, stats);
456
+ }
457
+ catch (e_22) {
458
+ env_22.error = e_22;
459
+ env_22.hasError = true;
460
+ }
461
+ finally {
462
+ __disposeResources(env_22);
150
463
  }
151
- return this.fs.syncSync(path, data, stats);
152
464
  }
153
465
  }
466
+ export const Locked = {
467
+ name: 'Locked',
468
+ options: {
469
+ fs: {
470
+ type: 'object',
471
+ required: true,
472
+ description: '',
473
+ validator(fs) {
474
+ if (!(fs instanceof FileSystem)) {
475
+ throw new ErrnoError(Errno.EINVAL, 'fs passed to LockedFS must be a FileSystem');
476
+ }
477
+ },
478
+ },
479
+ },
480
+ isAvailable() {
481
+ return true;
482
+ },
483
+ create({ fs }) {
484
+ return new LockedFS(fs);
485
+ },
486
+ };
@@ -56,6 +56,9 @@ export class UnlockedOverlayFS extends FileSystem {
56
56
  async sync(path, data, stats) {
57
57
  const cred = stats.cred(0, 0);
58
58
  await this.createParentDirectories(path, cred);
59
+ if (!(await this._writable.exists(path, cred))) {
60
+ await this._writable.createFile(path, 'w', 0o644, cred);
61
+ }
59
62
  await this._writable.sync(path, data, stats);
60
63
  }
61
64
  syncSync(path, data, stats) {
@@ -123,7 +126,7 @@ export class UnlockedOverlayFS extends FileSystem {
123
126
  async stat(path, cred) {
124
127
  this.checkInitialized();
125
128
  try {
126
- return this._writable.stat(path, cred);
129
+ return await this._writable.stat(path, cred);
127
130
  }
128
131
  catch (e) {
129
132
  if (this._deletedFiles.has(path)) {
@@ -1,4 +1,5 @@
1
1
  import type { Ino } from '../../inode.js';
2
+ import '../../symbol-dispose.js';
2
3
  /**
3
4
  * Represents a key-value store.
4
5
  */
@@ -1,4 +1,5 @@
1
1
  import { ErrnoError } from '../../error.js';
2
+ import '../../symbol-dispose.js';
2
3
  /**
3
4
  * A transaction for a store.
4
5
  */