@zenfs/core 1.8.8 → 1.9.0

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 (116) hide show
  1. package/dist/backends/backend.d.ts +1 -1
  2. package/dist/backends/backend.js +7 -4
  3. package/dist/backends/fetch.d.ts +23 -32
  4. package/dist/backends/fetch.js +94 -134
  5. package/dist/backends/index.d.ts +1 -4
  6. package/dist/backends/index.js +1 -4
  7. package/dist/backends/memory.d.ts +7 -5
  8. package/dist/backends/memory.js +6 -4
  9. package/dist/backends/overlay.d.ts +4 -5
  10. package/dist/backends/overlay.js +16 -20
  11. package/dist/backends/passthrough.d.ts +3 -3
  12. package/dist/backends/passthrough.js +4 -6
  13. package/dist/backends/port/fs.d.ts +4 -5
  14. package/dist/backends/port/fs.js +7 -12
  15. package/dist/backends/port/rpc.d.ts +1 -1
  16. package/dist/backends/port/rpc.js +15 -13
  17. package/dist/backends/store/fs.d.ts +51 -40
  18. package/dist/backends/store/fs.js +347 -241
  19. package/dist/backends/store/map.d.ts +41 -0
  20. package/dist/backends/store/map.js +45 -0
  21. package/dist/backends/store/simple.d.ts +10 -58
  22. package/dist/backends/store/simple.js +8 -115
  23. package/dist/backends/store/store.d.ts +111 -44
  24. package/dist/backends/store/store.js +230 -38
  25. package/dist/config.d.ts +7 -3
  26. package/dist/config.js +17 -14
  27. package/dist/context.d.ts +1 -1
  28. package/dist/context.js +1 -1
  29. package/dist/index.d.ts +1 -5
  30. package/dist/index.js +1 -5
  31. package/dist/{devices.d.ts → internal/devices.d.ts} +4 -4
  32. package/dist/{devices.js → internal/devices.js} +18 -14
  33. package/dist/{file.d.ts → internal/file.d.ts} +3 -2
  34. package/dist/{file.js → internal/file.js} +17 -12
  35. package/dist/{backends/store → internal}/file_index.d.ts +13 -3
  36. package/dist/{backends/store → internal}/file_index.js +28 -5
  37. package/dist/{filesystem.d.ts → internal/filesystem.d.ts} +99 -32
  38. package/dist/internal/filesystem.js +83 -0
  39. package/dist/internal/index.d.ts +9 -0
  40. package/dist/internal/index.js +9 -0
  41. package/dist/internal/index_fs.d.ts +56 -0
  42. package/dist/internal/index_fs.js +184 -0
  43. package/dist/{backends/store → internal}/inode.d.ts +6 -1
  44. package/dist/{backends/store → internal}/inode.js +14 -6
  45. package/dist/internal/log.d.ts +132 -0
  46. package/dist/internal/log.js +177 -0
  47. package/dist/mixins/async.d.ts +2 -2
  48. package/dist/mixins/async.js +19 -16
  49. package/dist/mixins/mutexed.d.ts +9 -3
  50. package/dist/mixins/mutexed.js +22 -3
  51. package/dist/mixins/readonly.d.ts +2 -2
  52. package/dist/mixins/readonly.js +4 -3
  53. package/dist/mixins/shared.d.ts +1 -1
  54. package/dist/mixins/sync.d.ts +2 -2
  55. package/dist/stats.d.ts +2 -3
  56. package/dist/stats.js +7 -5
  57. package/dist/utils.d.ts +2 -15
  58. package/dist/utils.js +10 -47
  59. package/dist/vfs/async.d.ts +2 -2
  60. package/dist/vfs/async.js +3 -3
  61. package/dist/vfs/dir.js +1 -1
  62. package/dist/vfs/promises.d.ts +6 -6
  63. package/dist/vfs/promises.js +54 -49
  64. package/dist/vfs/shared.d.ts +3 -3
  65. package/dist/vfs/shared.js +16 -10
  66. package/dist/vfs/streams.js +1 -1
  67. package/dist/vfs/sync.d.ts +1 -2
  68. package/dist/vfs/sync.js +14 -15
  69. package/dist/vfs/types.d.ts +1 -0
  70. package/dist/vfs/watchers.d.ts +5 -1
  71. package/dist/vfs/watchers.js +16 -19
  72. package/package.json +3 -3
  73. package/readme.md +12 -12
  74. package/scripts/test.js +15 -3
  75. package/tests/backend/fetch.test.ts +49 -0
  76. package/tests/backend/port.test.ts +130 -0
  77. package/tests/common/context.test.ts +9 -4
  78. package/tests/common.ts +21 -3
  79. package/tests/data/image.jpg +0 -0
  80. package/tests/data/utf8.txt +1 -0
  81. package/tests/fetch/config.js +40 -0
  82. package/tests/fetch/fetch.ts +20 -0
  83. package/tests/fetch/run.sh +3 -3
  84. package/tests/fetch/{server.ts → server.js} +15 -11
  85. package/tests/fs/directory.test.ts +1 -1
  86. package/tests/fs/errors.test.ts +1 -1
  87. package/tests/fs/links.test.ts +1 -1
  88. package/tests/fs/open.test.ts +1 -1
  89. package/tests/fs/permissions.test.ts +2 -3
  90. package/tests/fs/rename.test.ts +1 -1
  91. package/tests/fs/stat.test.ts +1 -1
  92. package/tests/fs/times.test.ts +1 -1
  93. package/tests/fs/watch.test.ts +21 -22
  94. package/tests/fs/writeFile.test.ts +8 -7
  95. package/tests/readme.md +3 -3
  96. package/tests/setup/_overlay.ts +7 -0
  97. package/tests/setup/context.ts +2 -2
  98. package/tests/setup/index.ts +3 -3
  99. package/tests/setup/memory.ts +2 -2
  100. package/tests/setup/port.ts +2 -2
  101. package/tests/setup.ts +25 -5
  102. package/tests/tsconfig.json +3 -2
  103. package/dist/backends/store/index_fs.d.ts +0 -34
  104. package/dist/backends/store/index_fs.js +0 -67
  105. package/dist/filesystem.js +0 -52
  106. package/tests/fetch/cow+fetch.ts +0 -13
  107. package/tests/port/channel.test.ts +0 -39
  108. package/tests/port/config.test.ts +0 -30
  109. package/tests/port/remote.test.ts +0 -32
  110. package/tests/port/timeout.test.ts +0 -48
  111. /package/dist/{credentials.d.ts → internal/credentials.d.ts} +0 -0
  112. /package/dist/{credentials.js → internal/credentials.js} +0 -0
  113. /package/dist/{error.d.ts → internal/error.d.ts} +0 -0
  114. /package/dist/{error.js → internal/error.js} +0 -0
  115. /package/tests/{port → backend}/config.worker.js +0 -0
  116. /package/tests/{port → backend}/remote.worker.js +0 -0
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type * as fs from 'node:fs';
2
2
  import type { ClassLike, OptionalTuple } from 'utilium';
3
- import { ErrnoError } from './error.js';
3
+ import { ErrnoError } from './internal/error.js';
4
4
  import type { AbsolutePath } from './vfs/path.js';
5
5
  declare global {
6
6
  function atob(data: string): string;
@@ -79,19 +79,6 @@ export declare function randomBigInt(): bigint;
79
79
  /**
80
80
  * Prevents infinite loops
81
81
  * @internal
82
+ * @deprecated Use `canary` from Utilium
82
83
  */
83
84
  export declare function canary(path?: string, syscall?: string): () => void;
84
- /**
85
- * A wrapper for throwing things.
86
- * Used in expressions.
87
- * @todo Remove once `throw` is allowed in expressions
88
- * @see https://github.com/tc39/proposal-throw-expressions
89
- * @internal @hidden
90
- */
91
- export declare function _throw(e: unknown): never;
92
- /**
93
- * Grows a buffer if it isn't large enough
94
- * @returns The original buffer if resized successfully, or a newly created buffer
95
- * @internal Not for external use!
96
- */
97
- export declare function growBuffer<T extends ArrayBufferLike | ArrayBufferView>(buffer: T, newByteLength: number): T;
package/dist/utils.js CHANGED
@@ -1,5 +1,5 @@
1
- import { randomHex } from 'utilium';
2
- import { Errno, ErrnoError } from './error.js';
1
+ import { Errno, ErrnoError } from './internal/error.js';
2
+ import { log_deprecated } from './internal/log.js';
3
3
  import { resolve } from './vfs/path.js';
4
4
  /**
5
5
  * Encodes a string into a buffer
@@ -122,74 +122,37 @@ export function normalizePath(p) {
122
122
  export function normalizeOptions(options, encoding = 'utf8', flag, mode = 0) {
123
123
  if (typeof options != 'object' || options === null) {
124
124
  return {
125
- encoding: typeof options == 'string' ? options : encoding !== null && encoding !== void 0 ? encoding : null,
125
+ encoding: typeof options == 'string' ? options : (encoding !== null && encoding !== void 0 ? encoding : null),
126
126
  flag,
127
127
  mode,
128
128
  };
129
129
  }
130
130
  return {
131
- encoding: typeof (options === null || options === void 0 ? void 0 : options.encoding) == 'string' ? options.encoding : encoding !== null && encoding !== void 0 ? encoding : null,
131
+ encoding: typeof (options === null || options === void 0 ? void 0 : options.encoding) == 'string' ? options.encoding : (encoding !== null && encoding !== void 0 ? encoding : null),
132
132
  flag: typeof (options === null || options === void 0 ? void 0 : options.flag) == 'string' ? options.flag : flag,
133
133
  mode: normalizeMode('mode' in options ? options === null || options === void 0 ? void 0 : options.mode : null, mode),
134
134
  };
135
135
  }
136
+ /* node:coverage disable */
137
+ import { randomHex } from 'utilium';
136
138
  /**
137
139
  * Generate a random ino
138
140
  * @internal @deprecated @hidden
139
141
  */
140
142
  export function randomBigInt() {
143
+ log_deprecated('randomBigInt');
141
144
  return BigInt('0x' + randomHex(8));
142
145
  }
143
146
  /**
144
147
  * Prevents infinite loops
145
148
  * @internal
149
+ * @deprecated Use `canary` from Utilium
146
150
  */
147
151
  export function canary(path, syscall) {
152
+ log_deprecated('canary');
148
153
  const timeout = setTimeout(() => {
149
154
  throw ErrnoError.With('EDEADLK', path, syscall);
150
155
  }, 5000);
151
156
  return () => clearTimeout(timeout);
152
157
  }
153
- /**
154
- * A wrapper for throwing things.
155
- * Used in expressions.
156
- * @todo Remove once `throw` is allowed in expressions
157
- * @see https://github.com/tc39/proposal-throw-expressions
158
- * @internal @hidden
159
- */
160
- export function _throw(e) {
161
- throw e;
162
- }
163
- /**
164
- * Grows a buffer if it isn't large enough
165
- * @returns The original buffer if resized successfully, or a newly created buffer
166
- * @internal Not for external use!
167
- */
168
- export function growBuffer(buffer, newByteLength) {
169
- if (buffer.byteLength >= newByteLength)
170
- return buffer;
171
- if (ArrayBuffer.isView(buffer)) {
172
- const newBuffer = growBuffer(buffer.buffer, newByteLength);
173
- return new buffer.constructor(newBuffer, buffer.byteOffset, newByteLength);
174
- }
175
- const isShared = typeof SharedArrayBuffer !== 'undefined' && buffer instanceof SharedArrayBuffer;
176
- // Note: If true, the buffer must be resizable/growable because of the first check.
177
- if (buffer.maxByteLength > newByteLength) {
178
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
179
- isShared ? buffer.grow(newByteLength) : buffer.resize(newByteLength);
180
- return buffer;
181
- }
182
- if (isShared) {
183
- const newBuffer = new SharedArrayBuffer(newByteLength);
184
- new Uint8Array(newBuffer).set(new Uint8Array(buffer));
185
- return newBuffer;
186
- }
187
- try {
188
- return buffer.transfer(newByteLength);
189
- }
190
- catch {
191
- const newBuffer = new ArrayBuffer(newByteLength);
192
- new Uint8Array(newBuffer).set(new Uint8Array(buffer));
193
- return newBuffer;
194
- }
195
- }
158
+ /* node:coverage enable */
@@ -1,11 +1,11 @@
1
1
  import type * as fs from 'node:fs';
2
2
  import type { V_Context } from '../context.js';
3
- import type { FileContents } from '../filesystem.js';
4
3
  import type { Stats } from '../stats.js';
5
4
  import type { Callback } from '../utils.js';
6
5
  import type { Dir, Dirent } from './dir.js';
6
+ import type { FileContents } from './types.js';
7
7
  import { Buffer } from 'buffer';
8
- import { ErrnoError } from '../error.js';
8
+ import { ErrnoError } from '../internal/error.js';
9
9
  import { BigIntStats } from '../stats.js';
10
10
  import * as promises from './promises.js';
11
11
  import { ReadStream, WriteStream } from './streams.js';
package/dist/vfs/async.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from 'buffer';
2
- import { Errno, ErrnoError } from '../error.js';
2
+ import { Errno, ErrnoError } from '../internal/error.js';
3
3
  import { BigIntStats } from '../stats.js';
4
4
  import { normalizeMode, normalizePath } from '../utils.js';
5
5
  import { R_OK } from './constants.js';
@@ -476,10 +476,10 @@ export function createWriteStream(path, options) {
476
476
  },
477
477
  destroy(error, callback) {
478
478
  callback(error);
479
- handle === null || handle === void 0 ? void 0 : handle.close().then(() => callback(error)).catch(callback);
479
+ void (handle === null || handle === void 0 ? void 0 : handle.close().then(() => callback(error)).catch(callback));
480
480
  },
481
481
  final(callback) {
482
- handle === null || handle === void 0 ? void 0 : handle.close().then(() => callback()).catch(callback);
482
+ void (handle === null || handle === void 0 ? void 0 : handle.close().then(() => callback()).catch(callback));
483
483
  },
484
484
  });
485
485
  stream.path = path.toString();
package/dist/vfs/dir.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Errno, ErrnoError } from '../error.js';
1
+ import { Errno, ErrnoError } from '../internal/error.js';
2
2
  import { basename } from './path.js';
3
3
  import { readdir } from './promises.js';
4
4
  import { readdirSync } from './sync.js';
@@ -4,10 +4,9 @@ import type { Stream } from 'node:stream';
4
4
  import type { ReadableStream as TReadableStream } from 'node:stream/web';
5
5
  import type { Interface as ReadlineInterface } from 'readline';
6
6
  import type { V_Context } from '../context.js';
7
- import type { File } from '../file.js';
8
- import type { FileContents } from '../filesystem.js';
7
+ import type { File } from '../internal/file.js';
9
8
  import type { Stats } from '../stats.js';
10
- import type { InternalOptions, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
9
+ import type { FileContents, InternalOptions, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
11
10
  import { Buffer } from 'buffer';
12
11
  import '../polyfills.js';
13
12
  import { BigIntStats } from '../stats.js';
@@ -26,6 +25,7 @@ export declare class FileHandle implements promises.FileHandle {
26
25
  */
27
26
  readonly file: File;
28
27
  constructor(fdOrFile: number | File, context?: V_Context | undefined);
28
+ private _emitChange;
29
29
  /**
30
30
  * Asynchronous fchown(2) - Change ownership of a file.
31
31
  */
@@ -296,9 +296,9 @@ export declare function lutimes(this: V_Context, path: fs.PathLike, atime: fs.Ti
296
296
  */
297
297
  export declare function realpath(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
298
298
  export declare function realpath(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
299
- export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterable<promises.FileChangeInfo<string>>;
300
- export declare function watch(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterable<promises.FileChangeInfo<Buffer>>;
301
- export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterable<promises.FileChangeInfo<string>> | AsyncIterable<promises.FileChangeInfo<Buffer>>;
299
+ export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterableIterator<promises.FileChangeInfo<string>>;
300
+ export declare function watch(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterableIterator<promises.FileChangeInfo<Buffer>>;
301
+ export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterableIterator<promises.FileChangeInfo<string>> | AsyncIterableIterator<promises.FileChangeInfo<Buffer>>;
302
302
  export declare function access(this: V_Context, path: fs.PathLike, mode?: number): Promise<void>;
303
303
  /**
304
304
  * Asynchronous `rm`. Removes files or directories (recursively).
@@ -51,9 +51,9 @@ 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 { Buffer } from 'buffer';
54
- import { credentials } from '../credentials.js';
55
- import { Errno, ErrnoError } from '../error.js';
56
- import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
54
+ import { credentials } from '../internal/credentials.js';
55
+ import { Errno, ErrnoError } from '../internal/error.js';
56
+ import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../internal/file.js';
57
57
  import '../polyfills.js';
58
58
  import { BigIntStats } from '../stats.js';
59
59
  import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
@@ -73,12 +73,16 @@ export class FileHandle {
73
73
  this.fd = isFile ? file2fd(fdOrFile) : fdOrFile;
74
74
  this.file = isFile ? fdOrFile : fd2file(fdOrFile);
75
75
  }
76
+ _emitChange() {
77
+ var _a, _b, _c;
78
+ emitChange(this.context, 'change', this.file.path.slice((_c = (_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.root) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0));
79
+ }
76
80
  /**
77
81
  * Asynchronous fchown(2) - Change ownership of a file.
78
82
  */
79
83
  async chown(uid, gid) {
80
84
  await this.file.chown(uid, gid);
81
- emitChange('change', this.file.path);
85
+ this._emitChange();
82
86
  }
83
87
  /**
84
88
  * Asynchronous fchmod(2) - Change permissions of a file.
@@ -90,7 +94,7 @@ export class FileHandle {
90
94
  throw new ErrnoError(Errno.EINVAL, 'Invalid mode.');
91
95
  }
92
96
  await this.file.chmod(numMode);
93
- emitChange('change', this.file.path);
97
+ this._emitChange();
94
98
  }
95
99
  /**
96
100
  * Asynchronous fdatasync(2) - synchronize a file's in-core state with storage device.
@@ -114,7 +118,7 @@ export class FileHandle {
114
118
  throw new ErrnoError(Errno.EINVAL);
115
119
  }
116
120
  await this.file.truncate(length);
117
- emitChange('change', this.file.path);
121
+ this._emitChange();
118
122
  }
119
123
  /**
120
124
  * Asynchronously change file timestamps of the file.
@@ -123,7 +127,7 @@ export class FileHandle {
123
127
  */
124
128
  async utimes(atime, mtime) {
125
129
  await this.file.utimes(normalizeTime(atime), normalizeTime(mtime));
126
- emitChange('change', this.file.path);
130
+ this._emitChange();
127
131
  }
128
132
  /**
129
133
  * Asynchronously append data to a file, creating the file if it does not exist. The underlying file will _not_ be closed automatically.
@@ -145,7 +149,7 @@ export class FileHandle {
145
149
  }
146
150
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
147
151
  await this.file.write(encodedData, 0, encodedData.length);
148
- emitChange('change', this.file.path);
152
+ this._emitChange();
149
153
  }
150
154
  async read(buffer, offset, length, position) {
151
155
  if (typeof offset == 'object' && offset != null) {
@@ -262,7 +266,7 @@ export class FileHandle {
262
266
  }
263
267
  position !== null && position !== void 0 ? position : (position = this.file.position);
264
268
  const bytesWritten = await this.file.write(buffer, offset, length, position);
265
- emitChange('change', this.file.path);
269
+ this._emitChange();
266
270
  return { buffer: data, bytesWritten };
267
271
  }
268
272
  /**
@@ -286,7 +290,7 @@ export class FileHandle {
286
290
  }
287
291
  const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : data;
288
292
  await this.file.write(encodedData, 0, encodedData.length, 0);
289
- emitChange('change', this.file.path);
293
+ this._emitChange();
290
294
  }
291
295
  /**
292
296
  * Asynchronous close(2) - close a `FileHandle`.
@@ -378,13 +382,13 @@ export async function rename(oldPath, newPath) {
378
382
  try {
379
383
  if (src.mountPoint == dst.mountPoint) {
380
384
  await src.fs.rename(src.path, dst.path);
381
- emitChange('rename', oldPath.toString());
382
- emitChange('change', newPath.toString());
385
+ emitChange(this, 'rename', oldPath.toString());
386
+ emitChange(this, 'change', newPath.toString());
383
387
  return;
384
388
  }
385
389
  await writeFile.call(this, newPath, await readFile(oldPath));
386
390
  await unlink.call(this, oldPath);
387
- emitChange('rename', oldPath.toString());
391
+ emitChange(this, 'rename', oldPath.toString());
388
392
  }
389
393
  catch (e) {
390
394
  throw fixError(e, { [src.path]: oldPath, [dst.path]: newPath });
@@ -459,7 +463,7 @@ export async function unlink(path) {
459
463
  throw ErrnoError.With('EACCES', resolved, 'unlink');
460
464
  }
461
465
  await fs.unlink(resolved);
462
- emitChange('rename', path.toString());
466
+ emitChange(this, 'rename', path.toString());
463
467
  }
464
468
  catch (e) {
465
469
  throw fixError(e, { [resolved]: path });
@@ -470,8 +474,7 @@ unlink;
470
474
  * Manually apply setuid/setgid.
471
475
  */
472
476
  async function applySetId(file, uid, gid) {
473
- var _a;
474
- if ((_a = file.fs.metadata().features) === null || _a === void 0 ? void 0 : _a.includes('setid'))
477
+ if (file.fs.attributes.has('setid'))
475
478
  return;
476
479
  const parent = await file.fs.stat(dirname(file.path));
477
480
  await file.chown(parent.mode & constants.S_ISUID ? parent.uid : uid, // manually apply setuid/setgid
@@ -633,7 +636,7 @@ export async function rmdir(path) {
633
636
  throw ErrnoError.With('EACCES', resolved, 'rmdir');
634
637
  }
635
638
  await fs.rmdir(resolved);
636
- emitChange('rename', path.toString());
639
+ emitChange(this, 'rename', path.toString());
637
640
  }
638
641
  catch (e) {
639
642
  throw fixError(e, { [resolved]: path });
@@ -655,7 +658,7 @@ export async function mkdir(path, options) {
655
658
  }
656
659
  await fs.mkdir(resolved, mode, { uid, gid });
657
660
  await applySetId(await fs.openFile(resolved, 'r+'), uid, gid);
658
- emitChange('rename', path.toString());
661
+ emitChange(this, 'rename', path.toString());
659
662
  return;
660
663
  }
661
664
  const dirs = [];
@@ -669,7 +672,7 @@ export async function mkdir(path, options) {
669
672
  }
670
673
  await fs.mkdir(dir, mode, { uid, gid });
671
674
  await applySetId(await fs.openFile(dir, 'r+'), uid, gid);
672
- emitChange('rename', dir);
675
+ emitChange(this, 'rename', dir);
673
676
  }
674
677
  return root.length == 1 ? dirs[0] : (_b = dirs[0]) === null || _b === void 0 ? void 0 : _b.slice(root.length);
675
678
  }
@@ -984,36 +987,38 @@ export async function realpath(path, options) {
984
987
  }
985
988
  realpath;
986
989
  export function watch(filename, options = {}) {
987
- const context = this;
990
+ const watcher = new FSWatcher(this, filename.toString(), typeof options !== 'string' ? options : { encoding: options });
991
+ // A queue to hold change events, since we need to resolve them in the async iterator
992
+ const eventQueue = [];
993
+ let done = false;
994
+ watcher.on('change', (eventType, filename) => {
995
+ var _a;
996
+ (_a = eventQueue.shift()) === null || _a === void 0 ? void 0 : _a({ value: { eventType, filename }, done: false });
997
+ });
998
+ function cleanup() {
999
+ done = true;
1000
+ watcher.close();
1001
+ for (const resolve of eventQueue) {
1002
+ resolve({ value: null, done });
1003
+ }
1004
+ eventQueue.length = 0; // Clear the queue
1005
+ return Promise.resolve({ value: null, done: true });
1006
+ }
988
1007
  return {
1008
+ async next() {
1009
+ if (done)
1010
+ return Promise.resolve({ value: null, done });
1011
+ const { promise, resolve } = Promise.withResolvers();
1012
+ eventQueue.push(resolve);
1013
+ return promise;
1014
+ },
1015
+ return: cleanup,
1016
+ throw: cleanup,
1017
+ async [Symbol.asyncDispose]() {
1018
+ await cleanup();
1019
+ },
989
1020
  [Symbol.asyncIterator]() {
990
- const watcher = new FSWatcher(context, filename.toString(), typeof options !== 'string' ? options : { encoding: options });
991
- // A queue to hold change events, since we need to resolve them in the async iterator
992
- const eventQueue = [];
993
- watcher.on('change', (eventType, filename) => {
994
- var _a;
995
- (_a = eventQueue.shift()) === null || _a === void 0 ? void 0 : _a({ value: { eventType, filename }, done: false });
996
- });
997
- function cleanup() {
998
- watcher.close();
999
- for (const resolve of eventQueue) {
1000
- resolve({ value: null, done: true });
1001
- }
1002
- eventQueue.length = 0; // Clear the queue
1003
- return Promise.resolve({ value: null, done: true });
1004
- }
1005
- return {
1006
- async next() {
1007
- const { promise, resolve } = Promise.withResolvers();
1008
- eventQueue.push(resolve);
1009
- return promise;
1010
- },
1011
- return: cleanup,
1012
- throw: cleanup,
1013
- [Symbol.asyncDispose]() {
1014
- return Promise.resolve();
1015
- },
1016
- };
1021
+ return this;
1017
1022
  },
1018
1023
  };
1019
1024
  }
@@ -1093,7 +1098,7 @@ export async function copyFile(src, dest, mode) {
1093
1098
  throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');
1094
1099
  }
1095
1100
  await writeFile.call(this, dest, await readFile.call(this, src));
1096
- emitChange('rename', dest.toString());
1101
+ emitChange(this, 'rename', dest.toString());
1097
1102
  }
1098
1103
  copyFile;
1099
1104
  /**
@@ -1187,7 +1192,7 @@ export function glob(pattern, opt) {
1187
1192
  if (exclude((withFileTypes ? entry : fullPath)))
1188
1193
  continue;
1189
1194
  /**
1190
- * @todo it the pattern.source check correct?
1195
+ * @todo is the pattern.source check correct?
1191
1196
  */
1192
1197
  if ((await stat(fullPath)).isDirectory() && regexPatterns.some(pattern => pattern.source.includes('.*'))) {
1193
1198
  yield* recursiveList(fullPath);
@@ -1,8 +1,8 @@
1
1
  import type * as fs from 'node:fs';
2
- import type { File } from '../file.js';
3
- import type { FileSystem } from '../filesystem.js';
2
+ import type { File } from '../internal/file.js';
3
+ import type { FileSystem } from '../internal/filesystem.js';
4
4
  import { type BoundContext, type V_Context } from '../context.js';
5
- import { ErrnoError } from '../error.js';
5
+ import { ErrnoError } from '../internal/error.js';
6
6
  import { type AbsolutePath } from './path.js';
7
7
  /**
8
8
  * @internal @hidden
@@ -1,11 +1,13 @@
1
1
  // Utilities and shared data
2
2
  import { InMemory } from '../backends/memory.js';
3
3
  import { bindContext } from '../context.js';
4
- import { Errno, ErrnoError } from '../error.js';
4
+ import { Errno, ErrnoError } from '../internal/error.js';
5
+ import { alert, debug, err, info, log_deprecated, notice, warn } from '../internal/log.js';
5
6
  import { normalizePath } from '../utils.js';
6
7
  import { paths as pathCache } from './cache.js';
7
8
  import { size_max } from './constants.js';
8
9
  import { join, resolve } from './path.js';
10
+ import { ZenFsType } from '../stats.js';
9
11
  // descriptors
10
12
  /**
11
13
  * @internal @hidden
@@ -41,29 +43,32 @@ mount('/', InMemory.create({ name: 'root' }));
41
43
  * @internal
42
44
  */
43
45
  export function mount(mountPoint, fs) {
44
- if (mountPoint[0] !== '/') {
46
+ if (mountPoint[0] != '/')
45
47
  mountPoint = '/' + mountPoint;
46
- }
47
48
  mountPoint = resolve(mountPoint);
48
49
  if (mounts.has(mountPoint)) {
49
- throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use.');
50
+ throw err(new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use.'));
50
51
  }
52
+ fs._mountPoint = mountPoint;
51
53
  mounts.set(mountPoint, fs);
54
+ info(`Mounted ${fs.name} on ${mountPoint}`);
55
+ debug(`${fs.name} attributes: ${[...fs.attributes].map(([k, v]) => (v !== undefined && v !== null ? k + '=' + v : v)).join(', ')}`);
52
56
  pathCache.clear();
53
57
  }
54
58
  /**
55
59
  * Unmounts the file system at `mountPoint`.
56
60
  */
57
61
  export function umount(mountPoint) {
58
- if (mountPoint[0] !== '/') {
62
+ if (mountPoint[0] != '/')
59
63
  mountPoint = '/' + mountPoint;
60
- }
61
64
  mountPoint = resolve(mountPoint);
62
65
  if (!mounts.has(mountPoint)) {
63
- throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already unmounted.');
66
+ warn(mountPoint + ' is already unmounted.');
67
+ return;
64
68
  }
65
69
  mounts.delete(mountPoint);
66
70
  pathCache.clear();
71
+ notice('Unmounted ' + mountPoint);
67
72
  }
68
73
  /**
69
74
  * Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
@@ -83,7 +88,7 @@ export function resolveMount(path, ctx) {
83
88
  return { fs, path, mountPoint, root };
84
89
  }
85
90
  }
86
- throw new ErrnoError(Errno.EIO, 'No file system');
91
+ throw alert(new ErrnoError(Errno.EIO, 'No file system', path));
87
92
  }
88
93
  /**
89
94
  * Reverse maps the paths in text from the mounted FileSystem to the global path
@@ -118,6 +123,7 @@ export function fixError(e, paths) {
118
123
  * @internal @deprecated
119
124
  */
120
125
  export function mountObject(mounts) {
126
+ log_deprecated('mountObject');
121
127
  if ('/' in mounts) {
122
128
  umount('/');
123
129
  }
@@ -130,10 +136,10 @@ export function mountObject(mounts) {
130
136
  * @internal @hidden
131
137
  */
132
138
  export function _statfs(fs, bigint) {
133
- const md = fs.metadata();
139
+ const md = fs.usage();
134
140
  const bs = md.blockSize || 4096;
135
141
  return {
136
- type: (bigint ? BigInt : Number)(md.type),
142
+ type: (bigint ? BigInt : Number)(fs.id),
137
143
  bsize: (bigint ? BigInt : Number)(bs),
138
144
  ffree: (bigint ? BigInt : Number)(md.freeNodes || size_max),
139
145
  files: (bigint ? BigInt : Number)(md.totalNodes || size_max),
@@ -1,5 +1,5 @@
1
1
  import { Readable, Writable } from 'readable-stream';
2
- import { Errno, ErrnoError } from '../error.js';
2
+ import { Errno, ErrnoError } from '../internal/error.js';
3
3
  export class ReadStream extends Readable {
4
4
  close(callback = () => null) {
5
5
  try {
@@ -1,8 +1,7 @@
1
1
  import type * as fs from 'node:fs';
2
2
  import type { V_Context } from '../context.js';
3
- import type { FileContents } from '../filesystem.js';
4
3
  import type { Stats } from '../stats.js';
5
- import type { InternalOptions, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
4
+ import type { FileContents, InternalOptions, NullEnc, ReaddirOptions, ReaddirOptsI, ReaddirOptsU } from './types.js';
6
5
  import { Buffer } from 'buffer';
7
6
  import { BigIntStats } from '../stats.js';
8
7
  import { Dir, Dirent } from './dir.js';
package/dist/vfs/sync.js CHANGED
@@ -51,9 +51,9 @@ 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 { Buffer } from 'buffer';
54
- import { credentials } from '../credentials.js';
55
- import { Errno, ErrnoError } from '../error.js';
56
- import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
54
+ import { credentials } from '../internal/credentials.js';
55
+ import { Errno, ErrnoError } from '../internal/error.js';
56
+ import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../internal/file.js';
57
57
  import { BigIntStats } from '../stats.js';
58
58
  import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
59
59
  import * as cache from './cache.js';
@@ -74,13 +74,13 @@ export function renameSync(oldPath, newPath) {
74
74
  try {
75
75
  if (oldMount === newMount) {
76
76
  oldMount.fs.renameSync(oldMount.path, newMount.path);
77
- emitChange('rename', oldPath.toString());
78
- emitChange('change', newPath.toString());
77
+ emitChange(this, 'rename', oldPath.toString());
78
+ emitChange(this, 'change', newPath.toString());
79
79
  return;
80
80
  }
81
81
  writeFileSync.call(this, newPath, readFileSync(oldPath));
82
82
  unlinkSync.call(this, oldPath);
83
- emitChange('rename', oldPath.toString());
83
+ emitChange(this, 'rename', oldPath.toString());
84
84
  }
85
85
  catch (e) {
86
86
  throw fixError(e, { [oldMount.path]: oldPath, [newMount.path]: newPath });
@@ -158,7 +158,7 @@ export function unlinkSync(path) {
158
158
  throw ErrnoError.With('EACCES', resolved, 'unlink');
159
159
  }
160
160
  fs.unlinkSync(resolved);
161
- emitChange('rename', path.toString());
161
+ emitChange(this, 'rename', path.toString());
162
162
  }
163
163
  catch (e) {
164
164
  throw fixError(e, { [resolved]: path });
@@ -169,8 +169,7 @@ unlinkSync;
169
169
  * Manually apply setuid/setgid.
170
170
  */
171
171
  function applySetId(file, uid, gid) {
172
- var _a;
173
- if ((_a = file.fs.metadata().features) === null || _a === void 0 ? void 0 : _a.includes('setid'))
172
+ if (file.fs.attributes.has('setid'))
174
173
  return;
175
174
  const parent = file.fs.statSync(dirname(file.path));
176
175
  file.chownSync(parent.mode & constants.S_ISUID ? parent.uid : uid, // manually apply setuid/setgid
@@ -287,7 +286,7 @@ export function writeFileSync(path, data, _options = {}) {
287
286
  preserveSymlinks: true,
288
287
  }), false);
289
288
  file.writeSync(encodedData, 0, encodedData.byteLength, 0);
290
- emitChange('change', path.toString());
289
+ emitChange(this, 'change', path.toString());
291
290
  }
292
291
  catch (e_3) {
293
292
  env_3.error = e_3;
@@ -378,7 +377,7 @@ export function writeSync(fd, data, posOrOff, lenOrEnc, pos) {
378
377
  const file = fd2file(fd);
379
378
  position !== null && position !== void 0 ? position : (position = file.position);
380
379
  const bytesWritten = file.writeSync(buffer, offset, length, position);
381
- emitChange('change', file.path);
380
+ emitChange(this, 'change', file.path);
382
381
  return bytesWritten;
383
382
  }
384
383
  writeSync;
@@ -435,7 +434,7 @@ export function rmdirSync(path) {
435
434
  throw ErrnoError.With('EACCES', resolved, 'rmdir');
436
435
  }
437
436
  fs.rmdirSync(resolved);
438
- emitChange('rename', path.toString());
437
+ emitChange(this, 'rename', path.toString());
439
438
  }
440
439
  catch (e) {
441
440
  throw fixError(e, { [resolved]: path });
@@ -470,7 +469,7 @@ export function mkdirSync(path, options) {
470
469
  }
471
470
  fs.mkdirSync(dir, mode, { uid, gid });
472
471
  applySetId(fs.openFileSync(dir, 'r+'), uid, gid);
473
- emitChange('rename', dir);
472
+ emitChange(this, 'rename', dir);
474
473
  }
475
474
  return root.length == 1 ? dirs[0] : (_b = dirs[0]) === null || _b === void 0 ? void 0 : _b.slice(root.length);
476
475
  }
@@ -747,7 +746,7 @@ export function copyFileSync(source, destination, flags) {
747
746
  throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', destination, 'copyFile');
748
747
  }
749
748
  writeFileSync.call(this, destination, readFileSync(source));
750
- emitChange('rename', destination.toString());
749
+ emitChange(this, 'rename', destination.toString());
751
750
  }
752
751
  copyFileSync;
753
752
  /**
@@ -868,7 +867,7 @@ export function globSync(pattern, options = {}) {
868
867
  if (exclude((withFileTypes ? entry : fullPath)))
869
868
  continue;
870
869
  /**
871
- * @todo it the pattern.source check correct?
870
+ * @todo is the pattern.source check correct?
872
871
  */
873
872
  if (statSync(fullPath).isDirectory() && regexPatterns.some(pattern => pattern.source.includes('.*'))) {
874
873
  recursiveList(fullPath);
@@ -1,4 +1,5 @@
1
1
  import type * as fs from 'node:fs';
2
+ export type FileContents = ArrayBufferView | string;
2
3
  /**
3
4
  * Options used for caching, among other things.
4
5
  * @internal @hidden *UNSTABLE*