@zenfs/core 1.7.2 → 1.8.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.
Files changed (68) hide show
  1. package/dist/backends/backend.js +3 -4
  2. package/dist/backends/fetch.d.ts +17 -18
  3. package/dist/backends/fetch.js +95 -58
  4. package/dist/backends/index.d.ts +2 -1
  5. package/dist/backends/index.js +2 -1
  6. package/dist/backends/memory.d.ts +1 -1
  7. package/dist/backends/overlay.d.ts +8 -14
  8. package/dist/backends/overlay.js +38 -31
  9. package/dist/backends/passthrough.d.ts +8 -3
  10. package/dist/backends/passthrough.js +148 -4
  11. package/dist/backends/port/fs.d.ts +15 -49
  12. package/dist/backends/port/fs.js +28 -116
  13. package/dist/backends/port/rpc.d.ts +13 -6
  14. package/dist/backends/port/rpc.js +9 -7
  15. package/dist/backends/store/file_index.d.ts +38 -0
  16. package/dist/backends/store/file_index.js +76 -0
  17. package/dist/backends/store/fs.d.ts +39 -34
  18. package/dist/backends/store/fs.js +407 -238
  19. package/dist/backends/store/index_fs.d.ts +34 -0
  20. package/dist/backends/store/index_fs.js +67 -0
  21. package/dist/backends/store/inode.d.ts +26 -8
  22. package/dist/backends/store/inode.js +92 -91
  23. package/dist/backends/store/simple.d.ts +20 -20
  24. package/dist/backends/store/simple.js +3 -4
  25. package/dist/backends/store/store.d.ts +12 -12
  26. package/dist/backends/store/store.js +4 -6
  27. package/dist/devices.d.ts +44 -21
  28. package/dist/devices.js +110 -55
  29. package/dist/file.d.ts +111 -7
  30. package/dist/file.js +324 -92
  31. package/dist/filesystem.d.ts +44 -4
  32. package/dist/mixins/async.js +12 -6
  33. package/dist/mixins/mutexed.d.ts +8 -3
  34. package/dist/mixins/mutexed.js +57 -1
  35. package/dist/mixins/readonly.d.ts +17 -16
  36. package/dist/mixins/readonly.js +6 -0
  37. package/dist/mixins/sync.d.ts +1 -1
  38. package/dist/stats.d.ts +12 -6
  39. package/dist/stats.js +14 -6
  40. package/dist/utils.d.ts +23 -3
  41. package/dist/utils.js +58 -10
  42. package/dist/vfs/async.js +1 -1
  43. package/dist/vfs/constants.d.ts +2 -2
  44. package/dist/vfs/constants.js +2 -2
  45. package/dist/vfs/dir.js +3 -1
  46. package/dist/vfs/index.js +4 -1
  47. package/dist/vfs/promises.js +33 -13
  48. package/dist/vfs/shared.js +2 -0
  49. package/dist/vfs/sync.js +25 -13
  50. package/dist/vfs/types.d.ts +15 -0
  51. package/eslint.shared.js +1 -0
  52. package/package.json +2 -3
  53. package/readme.md +2 -2
  54. package/scripts/test.js +73 -11
  55. package/tests/common/mutex.test.ts +1 -1
  56. package/tests/fetch/run.sh +16 -0
  57. package/tests/fetch/server.ts +49 -0
  58. package/tests/fetch/setup.ts +13 -0
  59. package/tests/fs/read.test.ts +10 -10
  60. package/tests/fs/times.test.ts +2 -2
  61. package/tests/fs/write.test.ts +6 -11
  62. package/tests/setup/index.ts +38 -0
  63. package/tests/setup/port.ts +15 -0
  64. package/dist/backends/file_index.d.ts +0 -63
  65. package/dist/backends/file_index.js +0 -163
  66. package/tests/common/async.test.ts +0 -31
  67. package/tests/setup/cow+fetch.ts +0 -45
  68. /package/tests/fs/{appendFile.test.ts → append.test.ts} +0 -0
@@ -20,8 +20,8 @@ export async function checkOptions(backend, options) {
20
20
  }
21
21
  throw new ErrnoError(Errno.EINVAL, 'Missing required option: ' + optName);
22
22
  }
23
- const isType = (value) => (typeof opt.type == 'function' ? value instanceof opt.type : typeof value === opt.type);
24
- if (Array.isArray(opt.type) ? !opt.type.some(isType) : !isType(value)) {
23
+ const isType = (type, _ = value) => (typeof type == 'function' ? value instanceof type : typeof value === type);
24
+ if (Array.isArray(opt.type) ? !opt.type.some(v => isType(v)) : !isType(opt.type)) {
25
25
  // The type of the value as a string
26
26
  const type = typeof value == 'object' && 'constructor' in value ? value.constructor.name : typeof value;
27
27
  // The expected type (as a string)
@@ -29,9 +29,8 @@ export async function checkOptions(backend, options) {
29
29
  const expected = Array.isArray(opt.type) ? `one of ${opt.type.map(name).join(', ')}` : name(opt.type);
30
30
  throw new ErrnoError(Errno.EINVAL, `Incorrect type for "${optName}": ${type} (expected ${expected})`);
31
31
  }
32
- if (opt.validator) {
32
+ if (opt.validator)
33
33
  await opt.validator(value);
34
- }
35
34
  // Otherwise: All good!
36
35
  }
37
36
  }
@@ -1,7 +1,6 @@
1
- import type { FileSystemMetadata } from '../filesystem.js';
2
- import type { Stats } from '../stats.js';
3
- import type { IndexData } from './file_index.js';
4
- import { IndexFS } from './file_index.js';
1
+ import { StoreFS } from './store/fs.js';
2
+ import { type IndexData } from './store/file_index.js';
3
+ import type { Store } from './store/store.js';
5
4
  /**
6
5
  * Configuration options for FetchFS.
7
6
  */
@@ -19,6 +18,11 @@ export interface FetchOptions {
19
18
  * Default: Fetch files relative to the index.
20
19
  */
21
20
  baseUrl?: string;
21
+ /**
22
+ * A store to use for caching content.
23
+ * Defaults to an in-memory store
24
+ */
25
+ cache?: Store;
22
26
  }
23
27
  /**
24
28
  * A simple filesystem backed by HTTP using the `fetch` API.
@@ -31,7 +35,7 @@ export interface FetchOptions {
31
35
  * "version": 1,
32
36
  * "entries": {
33
37
  * "/home": { ... },
34
- * "/home/jvilk": { ... },
38
+ * "/home/john": { ... },
35
39
  * "/home/james": { ... }
36
40
  * }
37
41
  * }
@@ -39,21 +43,12 @@ export interface FetchOptions {
39
43
  *
40
44
  * Each entry contains the stats associated with the file.
41
45
  */
42
- export declare class FetchFS extends IndexFS {
46
+ export declare class FetchFS extends StoreFS {
43
47
  readonly baseUrl: string;
44
- readonly requestInit?: RequestInit;
48
+ readonly requestInit?: RequestInit | undefined;
49
+ private indexData;
45
50
  ready(): Promise<void>;
46
- constructor({ index, baseUrl, requestInit }: FetchOptions);
47
- metadata(): FileSystemMetadata;
48
- /**
49
- * Preload the `path` into the index.
50
- */
51
- preload(path: string, buffer: Uint8Array): void;
52
- /**
53
- * @todo Be lazier about actually requesting the data?
54
- */
55
- protected getData(path: string, stats: Stats): Promise<Uint8Array>;
56
- protected getDataSync(path: string, stats: Stats): Uint8Array;
51
+ constructor(index?: IndexData | string, cache?: Store, baseUrl?: string, requestInit?: RequestInit | undefined);
57
52
  }
58
53
  declare const _Fetch: {
59
54
  readonly name: "Fetch";
@@ -70,6 +65,10 @@ declare const _Fetch: {
70
65
  readonly type: "object";
71
66
  readonly required: false;
72
67
  };
68
+ readonly cache: {
69
+ readonly type: "object";
70
+ readonly required: false;
71
+ };
73
72
  };
74
73
  readonly isAvailable: () => boolean;
75
74
  readonly create: (options: FetchOptions) => FetchFS;
@@ -1,5 +1,61 @@
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, inner;
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
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
1
53
  import { Errno, ErrnoError } from '../error.js';
2
- import { IndexFS } from './file_index.js';
54
+ import { S_IFREG } from '../vfs/constants.js';
55
+ import { InMemoryStore } from './memory.js';
56
+ import { StoreFS } from './store/fs.js';
57
+ import { Index } from './store/file_index.js';
58
+ import { normalizePath } from '../utils.js';
3
59
  async function fetchFile(path, type, init) {
4
60
  const response = await fetch(path, init).catch((e) => {
5
61
  throw new ErrnoError(Errno.EIO, e.message, path);
@@ -33,7 +89,7 @@ async function fetchFile(path, type, init) {
33
89
  * "version": 1,
34
90
  * "entries": {
35
91
  * "/home": { ... },
36
- * "/home/jvilk": { ... },
92
+ * "/home/john": { ... },
37
93
  * "/home/james": { ... }
38
94
  * }
39
95
  * }
@@ -41,68 +97,46 @@ async function fetchFile(path, type, init) {
41
97
  *
42
98
  * Each entry contains the stats associated with the file.
43
99
  */
44
- export class FetchFS extends IndexFS {
100
+ export class FetchFS extends StoreFS {
45
101
  async ready() {
46
- if (this._isInitialized) {
47
- return;
102
+ const env_1 = { stack: [], error: void 0, hasError: false };
103
+ try {
104
+ if (this._initialized)
105
+ return;
106
+ await super.ready();
107
+ const index = new Index();
108
+ index.fromJSON(await this.indexData);
109
+ await this.loadIndex(index);
110
+ if (this._disableSync)
111
+ return;
112
+ const tx = __addDisposableResource(env_1, this.store.transaction(), true);
113
+ // Iterate over all of the files and cache their contents
114
+ for (const [path, node] of index) {
115
+ if (!(node.mode & S_IFREG))
116
+ continue;
117
+ const content = await fetchFile(this.baseUrl + path, 'buffer', this.requestInit);
118
+ await tx.set(node.data, content);
119
+ }
120
+ await tx.commit();
48
121
  }
49
- await super.ready();
50
- if (this._disableSync) {
51
- return;
122
+ catch (e_1) {
123
+ env_1.error = e_1;
124
+ env_1.hasError = true;
52
125
  }
53
- /**
54
- * Iterate over all of the files and cache their contents
55
- */
56
- for (const [path, stats] of this.index.files()) {
57
- await this.getData(path, stats);
126
+ finally {
127
+ const result_1 = __disposeResources(env_1);
128
+ if (result_1)
129
+ await result_1;
58
130
  }
59
131
  }
60
- constructor({ index = 'index.json', baseUrl = '', requestInit }) {
61
- // prefix url must end in a directory separator.
62
- if (baseUrl.at(-1) != '/') {
63
- baseUrl += '/';
64
- }
65
- super(typeof index != 'string' ? index : fetchFile(index, 'json', requestInit));
132
+ constructor(index = 'index.json', cache = new InMemoryStore('fetch'), baseUrl = '', requestInit) {
133
+ super(cache);
66
134
  this.baseUrl = baseUrl;
67
135
  this.requestInit = requestInit;
68
- }
69
- metadata() {
70
- return {
71
- ...super.metadata(),
72
- name: FetchFS.name,
73
- readonly: true,
74
- };
75
- }
76
- /**
77
- * Preload the `path` into the index.
78
- */
79
- preload(path, buffer) {
80
- const stats = this.index.get(path);
81
- if (!stats) {
82
- throw ErrnoError.With('ENOENT', path, 'preload');
83
- }
84
- if (!stats.isFile()) {
85
- throw ErrnoError.With('EISDIR', path, 'preload');
86
- }
87
- stats.size = buffer.length;
88
- stats.fileData = buffer;
89
- }
90
- /**
91
- * @todo Be lazier about actually requesting the data?
92
- */
93
- async getData(path, stats) {
94
- if (stats.fileData) {
95
- return stats.fileData;
96
- }
97
- const data = await fetchFile(this.baseUrl + (path.startsWith('/') ? path.slice(1) : path), 'buffer', this.requestInit);
98
- stats.fileData = data;
99
- return data;
100
- }
101
- getDataSync(path, stats) {
102
- if (stats.fileData) {
103
- return stats.fileData;
104
- }
105
- throw new ErrnoError(Errno.ENODATA, '', path, 'getData');
136
+ // prefix url must end in a directory separator.
137
+ if (baseUrl.at(-1) == '/')
138
+ this.baseUrl = baseUrl.slice(0, -1);
139
+ this.indexData = typeof index != 'string' ? index : fetchFile(index, 'json', requestInit);
106
140
  }
107
141
  }
108
142
  const _Fetch = {
@@ -111,12 +145,15 @@ const _Fetch = {
111
145
  index: { type: ['string', 'object'], required: false },
112
146
  baseUrl: { type: 'string', required: false },
113
147
  requestInit: { type: 'object', required: false },
148
+ cache: { type: 'object', required: false },
114
149
  },
115
150
  isAvailable() {
116
151
  return typeof globalThis.fetch == 'function';
117
152
  },
118
153
  create(options) {
119
- return new FetchFS(options);
154
+ const url = new URL(options.baseUrl || '');
155
+ url.pathname = normalizePath(url.pathname);
156
+ return new FetchFS(options.index, options.cache, url.toString(), options.requestInit);
120
157
  },
121
158
  };
122
159
  export const Fetch = _Fetch;
@@ -1,11 +1,12 @@
1
1
  export * from './backend.js';
2
2
  export * from './fetch.js';
3
- export * from './file_index.js';
4
3
  export * from './memory.js';
5
4
  export * from './overlay.js';
6
5
  export * from './passthrough.js';
7
6
  export * from './port/fs.js';
8
7
  export * from './store/fs.js';
8
+ export * from './store/file_index.js';
9
+ export * from './store/index_fs.js';
9
10
  export * from './store/inode.js';
10
11
  export * from './store/simple.js';
11
12
  export * from './store/store.js';
@@ -1,11 +1,12 @@
1
1
  export * from './backend.js';
2
2
  export * from './fetch.js';
3
- export * from './file_index.js';
4
3
  export * from './memory.js';
5
4
  export * from './overlay.js';
6
5
  export * from './passthrough.js';
7
6
  export * from './port/fs.js';
8
7
  export * from './store/fs.js';
8
+ export * from './store/file_index.js';
9
+ export * from './store/index_fs.js';
9
10
  export * from './store/inode.js';
10
11
  export * from './store/simple.js';
11
12
  export * from './store/store.js';
@@ -3,7 +3,7 @@ import { SimpleTransaction, type SimpleSyncStore } from './store/simple.js';
3
3
  /**
4
4
  * A simple in-memory store
5
5
  */
6
- export declare class InMemoryStore extends Map<bigint, Uint8Array> implements SimpleSyncStore {
6
+ export declare class InMemoryStore extends Map<number, Uint8Array> implements SimpleSyncStore {
7
7
  name: string;
8
8
  constructor(name?: string);
9
9
  sync(): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import type { File } from '../file.js';
2
2
  import type { CreationOptions, FileSystemMetadata } from '../filesystem.js';
3
3
  import type { Stats } from '../stats.js';
4
+ import type { InodeLike } from './store/inode.js';
4
5
  import { FileSystem } from '../filesystem.js';
5
6
  /**
6
7
  * Configuration options for OverlayFS instances.
@@ -23,7 +24,7 @@ export interface OverlayOptions {
23
24
  *
24
25
  * @internal
25
26
  */
26
- export declare class UnmutexedOverlayFS extends FileSystem {
27
+ export declare class OverlayFS extends FileSystem {
27
28
  ready(): Promise<void>;
28
29
  readonly writable: FileSystem;
29
30
  readonly readable: FileSystem;
@@ -36,8 +37,12 @@ export declare class UnmutexedOverlayFS extends FileSystem {
36
37
  private _ready;
37
38
  constructor({ writable, readable }: OverlayOptions);
38
39
  metadata(): FileSystemMetadata;
39
- sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
40
- syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
40
+ sync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): Promise<void>;
41
+ syncSync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): void;
42
+ read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
43
+ readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
44
+ write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
45
+ writeSync(path: string, buffer: Uint8Array, offset: number): void;
41
46
  /**
42
47
  * Called once to load up metadata stored on the writable file system.
43
48
  * @internal
@@ -92,17 +97,6 @@ export declare class UnmutexedOverlayFS extends FileSystem {
92
97
  private copyToWritableSync;
93
98
  private copyToWritable;
94
99
  }
95
- declare const OverlayFS_base: {
96
- new (): import("../mixins/mutexed.js")._MutexedFS<UnmutexedOverlayFS>;
97
- } & (new (args_0: OverlayOptions) => import("../mixins/mutexed.js")._MutexedFS<UnmutexedOverlayFS>);
98
- /**
99
- * OverlayFS makes a read-only filesystem writable by storing writes on a second,
100
- * writable file system. Deletes are persisted via metadata stored on the writable
101
- * file system.
102
- * @internal
103
- */
104
- export declare class OverlayFS extends OverlayFS_base {
105
- }
106
100
  declare const _Overlay: {
107
101
  readonly name: "Overlay";
108
102
  readonly options: {
@@ -51,11 +51,10 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
53
  import { Errno, ErrnoError } from '../error.js';
54
- import { PreloadFile, parseFlag } from '../file.js';
54
+ import { LazyFile, parseFlag } from '../file.js';
55
55
  import { FileSystem } from '../filesystem.js';
56
- import { Mutexed } from '../mixins/mutexed.js';
57
- import { decodeUTF8, encodeUTF8 } from '../utils.js';
58
- import { dirname } from '../vfs/path.js';
56
+ import { canary, decodeUTF8, encodeUTF8 } from '../utils.js';
57
+ import { dirname, join } from '../vfs/path.js';
59
58
  /** @internal */
60
59
  const deletionLogPath = '/.deleted';
61
60
  /**
@@ -66,7 +65,7 @@ const deletionLogPath = '/.deleted';
66
65
  *
67
66
  * @internal
68
67
  */
69
- export class UnmutexedOverlayFS extends FileSystem {
68
+ export class OverlayFS extends FileSystem {
70
69
  async ready() {
71
70
  await this.readable.ready();
72
71
  await this.writable.ready();
@@ -97,15 +96,26 @@ export class UnmutexedOverlayFS extends FileSystem {
97
96
  }
98
97
  async sync(path, data, stats) {
99
98
  await this.copyForWrite(path);
100
- if (!(await this.writable.exists(path))) {
101
- await this.writable.createFile(path, 'w', 0o644, stats);
102
- }
103
99
  await this.writable.sync(path, data, stats);
104
100
  }
105
101
  syncSync(path, data, stats) {
106
102
  this.copyForWriteSync(path);
107
103
  this.writable.syncSync(path, data, stats);
108
104
  }
105
+ async read(path, buffer, offset, end) {
106
+ return (await this.writable.exists(path)) ? await this.writable.read(path, buffer, offset, end) : await this.readable.read(path, buffer, offset, end);
107
+ }
108
+ readSync(path, buffer, offset, end) {
109
+ return this.writable.existsSync(path) ? this.writable.readSync(path, buffer, offset, end) : this.readable.readSync(path, buffer, offset, end);
110
+ }
111
+ async write(path, buffer, offset) {
112
+ await this.copyForWrite(path);
113
+ return await this.writable.write(path, buffer, offset);
114
+ }
115
+ writeSync(path, buffer, offset) {
116
+ this.copyForWriteSync(path);
117
+ return this.writable.writeSync(path, buffer, offset);
118
+ }
109
119
  /**
110
120
  * Called once to load up metadata stored on the writable file system.
111
121
  * @internal
@@ -199,22 +209,15 @@ export class UnmutexedOverlayFS extends FileSystem {
199
209
  if (await this.writable.exists(path)) {
200
210
  return this.writable.openFile(path, flag);
201
211
  }
202
- // Create an OverlayFile.
203
- const file = await this.readable.openFile(path, parseFlag('r'));
204
- const stats = await file.stat();
205
- const { buffer } = await file.read(new Uint8Array(stats.size));
206
- return new PreloadFile(this, path, flag, stats, buffer);
212
+ const stats = await this.readable.stat(path);
213
+ return new LazyFile(this, path, flag, stats);
207
214
  }
208
215
  openFileSync(path, flag) {
209
216
  if (this.writable.existsSync(path)) {
210
217
  return this.writable.openFileSync(path, flag);
211
218
  }
212
- // Create an OverlayFile.
213
- const file = this.readable.openFileSync(path, parseFlag('r'));
214
- const stats = file.statSync();
215
- const data = new Uint8Array(stats.size);
216
- file.readSync(data);
217
- return new PreloadFile(this, path, flag, stats, data);
219
+ const stats = this.readable.statSync(path);
220
+ return new LazyFile(this, path, flag, stats);
218
221
  }
219
222
  async createFile(path, flag, mode, options) {
220
223
  this.checkInitialized();
@@ -421,10 +424,12 @@ export class UnmutexedOverlayFS extends FileSystem {
421
424
  createParentDirectoriesSync(path) {
422
425
  let parent = dirname(path);
423
426
  const toCreate = [];
427
+ const silence = canary(path);
424
428
  while (!this.writable.existsSync(parent)) {
425
429
  toCreate.push(parent);
426
430
  parent = dirname(parent);
427
431
  }
432
+ silence();
428
433
  for (const path of toCreate.reverse()) {
429
434
  const { uid, gid, mode } = this.statSync(path);
430
435
  this.writable.mkdirSync(path, mode, { uid, gid });
@@ -437,10 +442,12 @@ export class UnmutexedOverlayFS extends FileSystem {
437
442
  async createParentDirectories(path) {
438
443
  let parent = dirname(path);
439
444
  const toCreate = [];
445
+ const silence = canary(path);
440
446
  while (!(await this.writable.exists(parent))) {
441
447
  toCreate.push(parent);
442
448
  parent = dirname(parent);
443
449
  }
450
+ silence();
444
451
  for (const path of toCreate.reverse()) {
445
452
  const { uid, gid, mode } = await this.stat(path);
446
453
  await this.writable.mkdir(path, mode, { uid, gid });
@@ -453,7 +460,7 @@ export class UnmutexedOverlayFS extends FileSystem {
453
460
  */
454
461
  copyForWriteSync(path) {
455
462
  if (!this.existsSync(path)) {
456
- throw ErrnoError.With('ENOENT', path, 'copyForWrite');
463
+ throw ErrnoError.With('ENOENT', path, '[copyForWrite]');
457
464
  }
458
465
  if (!this.writable.existsSync(dirname(path))) {
459
466
  this.createParentDirectoriesSync(path);
@@ -464,7 +471,7 @@ export class UnmutexedOverlayFS extends FileSystem {
464
471
  }
465
472
  async copyForWrite(path) {
466
473
  if (!(await this.exists(path))) {
467
- throw ErrnoError.With('ENOENT', path, 'copyForWrite');
474
+ throw ErrnoError.With('ENOENT', path, '[copyForWrite]');
468
475
  }
469
476
  if (!(await this.writable.exists(dirname(path)))) {
470
477
  await this.createParentDirectories(path);
@@ -481,14 +488,18 @@ export class UnmutexedOverlayFS extends FileSystem {
481
488
  const env_1 = { stack: [], error: void 0, hasError: false };
482
489
  try {
483
490
  const stats = this.statSync(path);
491
+ stats.mode |= 0o222;
484
492
  if (stats.isDirectory()) {
485
493
  this.writable.mkdirSync(path, stats.mode, stats);
494
+ for (const k of this.readable.readdirSync(path)) {
495
+ this.copyToWritableSync(join(path, k));
496
+ }
486
497
  return;
487
498
  }
488
499
  const data = new Uint8Array(stats.size);
489
500
  const readable = __addDisposableResource(env_1, this.readable.openFileSync(path, 'r'), false);
490
501
  readable.readSync(data);
491
- const writable = __addDisposableResource(env_1, this.writable.createFileSync(path, 'w', stats.mode | 0o222, stats), false);
502
+ const writable = __addDisposableResource(env_1, this.writable.createFileSync(path, 'w', stats.mode, stats), false);
492
503
  writable.writeSync(data);
493
504
  }
494
505
  catch (e_1) {
@@ -503,14 +514,18 @@ export class UnmutexedOverlayFS extends FileSystem {
503
514
  const env_2 = { stack: [], error: void 0, hasError: false };
504
515
  try {
505
516
  const stats = await this.stat(path);
517
+ stats.mode |= 0o222;
506
518
  if (stats.isDirectory()) {
507
519
  await this.writable.mkdir(path, stats.mode, stats);
520
+ for (const k of await this.readable.readdir(path)) {
521
+ await this.copyToWritable(join(path, k));
522
+ }
508
523
  return;
509
524
  }
510
525
  const data = new Uint8Array(stats.size);
511
526
  const readable = __addDisposableResource(env_2, await this.readable.openFile(path, 'r'), true);
512
527
  await readable.read(data);
513
- const writable = __addDisposableResource(env_2, await this.writable.createFile(path, 'w', stats.mode | 0o222, stats), true);
528
+ const writable = __addDisposableResource(env_2, await this.writable.createFile(path, 'w', stats.mode, stats), true);
514
529
  await writable.write(data);
515
530
  }
516
531
  catch (e_2) {
@@ -524,14 +539,6 @@ export class UnmutexedOverlayFS extends FileSystem {
524
539
  }
525
540
  }
526
541
  }
527
- /**
528
- * OverlayFS makes a read-only filesystem writable by storing writes on a second,
529
- * writable file system. Deletes are persisted via metadata stored on the writable
530
- * file system.
531
- * @internal
532
- */
533
- export class OverlayFS extends Mutexed(UnmutexedOverlayFS) {
534
- }
535
542
  const _Overlay = {
536
543
  name: 'Overlay',
537
544
  options: {
@@ -1,7 +1,8 @@
1
1
  import type * as fs from 'node:fs';
2
+ import { File } from '../file.js';
2
3
  import { FileSystem } from '../filesystem.js';
3
4
  import { Stats } from '../stats.js';
4
- import { File } from '../file.js';
5
+ import type { InodeLike } from './store/inode.js';
5
6
  export type NodeFS = typeof fs;
6
7
  export interface PassthroughOptions {
7
8
  fs: NodeFS;
@@ -80,11 +81,11 @@ export declare class PassthroughFS extends FileSystem {
80
81
  /**
81
82
  * Synchronize data to the file system.
82
83
  */
83
- sync(path: string, data: Uint8Array, stats: Stats): Promise<void>;
84
+ sync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): Promise<void>;
84
85
  /**
85
86
  * Synchronize data to the file system synchronously.
86
87
  */
87
- syncSync(path: string, data: Uint8Array, stats: Stats): void;
88
+ syncSync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): void;
88
89
  /**
89
90
  * Create a hard link.
90
91
  */
@@ -93,6 +94,10 @@ export declare class PassthroughFS extends FileSystem {
93
94
  * Create a hard link synchronously.
94
95
  */
95
96
  linkSync(target: string, link: string): void;
97
+ read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
98
+ readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
99
+ write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
100
+ writeSync(path: string, buffer: Uint8Array, offset: number): void;
96
101
  }
97
102
  declare const _Passthrough: {
98
103
  readonly name: "Passthrough";