@zenfs/core 2.7.5 → 2.7.7

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 (72) hide show
  1. package/dist/backends/backend.js +0 -2
  2. package/dist/backends/cow.d.ts +2 -2
  3. package/dist/backends/cow.js +6 -14
  4. package/dist/backends/passthrough.d.ts +0 -54
  5. package/dist/backends/passthrough.js +0 -54
  6. package/dist/backends/port.d.ts +2 -6
  7. package/dist/backends/port.js +3 -11
  8. package/dist/backends/store/fs.d.ts +0 -9
  9. package/dist/backends/store/fs.js +2 -24
  10. package/dist/backends/store/store.d.ts +1 -1
  11. package/dist/backends/store/store.js +2 -8
  12. package/dist/config.d.ts +7 -0
  13. package/dist/config.js +9 -3
  14. package/dist/internal/contexts.d.ts +1 -2
  15. package/dist/internal/credentials.d.ts +0 -3
  16. package/dist/internal/error.d.ts +5 -0
  17. package/dist/internal/error.js +10 -0
  18. package/dist/internal/file_index.js +0 -1
  19. package/dist/internal/index_fs.d.ts +2 -2
  20. package/dist/internal/index_fs.js +2 -2
  21. package/dist/internal/inode.d.ts +46 -43
  22. package/dist/internal/inode.js +75 -10
  23. package/dist/internal/rpc.d.ts +16 -7
  24. package/dist/internal/rpc.js +2 -11
  25. package/dist/mixins/async.js +22 -24
  26. package/dist/mixins/mutexed.d.ts +6 -41
  27. package/dist/mixins/mutexed.js +36 -109
  28. package/dist/mixins/sync.js +0 -2
  29. package/dist/node/async.js +2 -12
  30. package/dist/node/index.d.ts +1 -1
  31. package/dist/node/promises.d.ts +1 -6
  32. package/dist/node/promises.js +18 -22
  33. package/dist/node/stats.d.ts +25 -99
  34. package/dist/node/stats.js +22 -56
  35. package/dist/node/streams.d.ts +1 -37
  36. package/dist/node/sync.d.ts +1 -6
  37. package/dist/node/sync.js +16 -20
  38. package/dist/polyfills.js +25 -0
  39. package/dist/utils.d.ts +0 -4
  40. package/dist/vfs/acl.d.ts +1 -3
  41. package/dist/vfs/acl.js +1 -3
  42. package/dist/vfs/async.js +77 -69
  43. package/dist/vfs/config.d.ts +10 -1
  44. package/dist/vfs/config.js +5 -2
  45. package/dist/vfs/dir.d.ts +3 -0
  46. package/dist/vfs/dir.js +4 -1
  47. package/dist/vfs/file.d.ts +1 -8
  48. package/dist/vfs/file.js +11 -7
  49. package/dist/vfs/ioctl.js +2 -2
  50. package/dist/vfs/shared.d.ts +2 -0
  51. package/dist/vfs/shared.js +3 -4
  52. package/dist/vfs/sync.js +77 -61
  53. package/dist/vfs/vcache.d.ts +14 -14
  54. package/dist/vfs/vcache.js +65 -58
  55. package/dist/vfs/watchers.d.ts +1 -0
  56. package/dist/vfs/watchers.js +2 -4
  57. package/dist/vfs/xattr.js +18 -17
  58. package/package.json +2 -4
  59. package/scripts/make-index.js +1 -1
  60. package/tests/common/async.test.ts +35 -0
  61. package/tests/common/config.test.ts +1 -1
  62. package/tests/common/context.test.ts +1 -1
  63. package/tests/common/file_index.test.ts +1 -1
  64. package/tests/common/mounts.test.ts +12 -0
  65. package/tests/common/vcache.test.ts +13 -0
  66. package/tests/fs/concurrency.test.ts +3 -3
  67. package/tests/fs/links.test.ts +76 -0
  68. package/tests/fs/permissions.test.ts +232 -0
  69. package/tests/fs/stat.test.ts +1 -1
  70. package/tests/fs/streams.test.ts +1 -1
  71. package/tests/fs/xattr.test.ts +10 -0
  72. package/tests/common/mutex.test.ts +0 -61
@@ -1,3 +1,4 @@
1
+ import { type ExceptionExtra } from 'kerium';
1
2
  import { BufferView } from 'utilium/buffer';
2
3
  import { Stats } from '../node/stats.js';
3
4
  import { type V_Context } from './contexts.js';
@@ -32,61 +33,39 @@ export interface InodeFields {
32
33
  data?: number;
33
34
  flags?: number;
34
35
  version?: number;
36
+ /** Extended attributes */
37
+ attributes?: Attributes;
35
38
  }
36
- /**
37
- * @category Internals
38
- * @internal
39
- */
40
- export interface InodeLike<T extends number | bigint = number> extends InodeFields {
41
- /**
42
- * Size of the item in bytes.
43
- * For directories/symlinks, this is normally the size of the struct that represents the item.
44
- */
39
+ export interface StatsLike<T extends number | bigint = number | bigint> {
40
+ /** Size of the item in bytes. For directories/symlinks, this may be the size of the struct that represents the item. */
45
41
  size: T;
46
- /**
47
- * Unix-style file mode (e.g. 0o644) that includes the item type
48
- */
42
+ /** Unix-style file mode (e.g. 0o644) that includes the item type */
49
43
  mode: T;
50
- /**
51
- * Time of last access, since epoch
52
- */
44
+ /** Time of last access, since epoch */
53
45
  atimeMs: T;
54
- /**
55
- * Time of last modification, since epoch
56
- */
46
+ /** Time of last modification, since epoch */
57
47
  mtimeMs: T;
58
- /**
59
- * Time of last time file status was changed, since epoch
60
- */
48
+ /** Time of last time file status was changed, since epoch */
61
49
  ctimeMs: T;
62
- /**
63
- * Time of file creation, since epoch
64
- */
50
+ /** Time of file creation, since epoch */
65
51
  birthtimeMs: T;
66
- /**
67
- * The id of the user that owns the file
68
- */
52
+ /** The id of the user that owns the file */
69
53
  uid: T;
70
- /**
71
- * The id of the group that owns the file
72
- */
54
+ /** The id of the group that owns the file */
73
55
  gid: T;
74
- /**
75
- * Inode number
76
- */
56
+ /** Inode number */
77
57
  ino: T;
78
- /**
79
- * Number of hard links
80
- */
58
+ /** Number of hard links */
81
59
  nlink: T;
82
- /**
83
- * Device ID for special files
84
- */
60
+ /** Device ID for special files */
85
61
  rdev: T;
86
- /**
87
- * Extended attributes
88
- */
89
- attributes?: Attributes;
62
+ }
63
+ /**
64
+ * @category Internals
65
+ * @internal
66
+ * @todo [breaking] switch fully to using bigints
67
+ */
68
+ export interface InodeLike<T extends number | bigint = number> extends InodeFields, StatsLike<T> {
90
69
  }
91
70
  /**
92
71
  * @internal @hidden
@@ -256,10 +235,34 @@ export declare function isFIFO(metadata: {
256
235
  * @internal
257
236
  */
258
237
  export declare function hasAccess($: V_Context, inode: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, access: number): boolean;
238
+ /**
239
+ * Throws if a given user may not search the directory `dir`
240
+ * @internal
241
+ */
242
+ export declare function assertSearchable($: V_Context, dir: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, extra?: ExceptionExtra): void;
243
+ /**
244
+ * Throws if a given user may not remove `entry` from the directory `parent`
245
+ * @internal
246
+ */
247
+ export declare function assertRemovable($: V_Context, parent: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, entry: Pick<InodeLike, 'uid'>, extra?: ExceptionExtra): void;
248
+ /**
249
+ * Throws if a given user is neither root nor the owner of `inode`
250
+ * @internal
251
+ */
252
+ export declare function assertOwner($: V_Context, inode: Pick<InodeLike, 'uid'>, extra?: ExceptionExtra): void;
253
+ /**
254
+ * Throws if a given user may not change the owner of `inode` to `uid` and `gid`, where `-1` leaves an ID unchanged
255
+ * @internal
256
+ */
257
+ export declare function assertCanChown($: V_Context, inode: Pick<InodeLike, 'uid' | 'gid'>, uid: number, gid: number, extra?: ExceptionExtra): void;
259
258
  /**
260
259
  * @hidden @internal
261
260
  *
262
261
  * @todo https://github.com/nodejs/node/pull/58836 support -1 for `*chown`
263
262
  */
264
263
  export declare function _chown(stats: Partial<InodeLike>, uid: number, gid: number): boolean;
264
+ /**
265
+ * @hidden @internal
266
+ */
267
+ export declare function _chmod($: V_Context, inode: Pick<InodeLike, 'mode' | 'gid'>, mode: number): void;
265
268
  export {};
@@ -33,7 +33,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
33
33
  return useValue ? value : void 0;
34
34
  };
35
35
  // SPDX-License-Identifier: LGPL-3.0-or-later
36
- import { withErrno } from 'kerium';
36
+ import { UV, withErrno } from 'kerium';
37
37
  import { crit, warn } from 'kerium/log';
38
38
  import { sizeof } from 'memium';
39
39
  import { $from, field, struct, types as t } from 'memium/decorators';
@@ -41,6 +41,7 @@ import { decodeUTF8, encodeUTF8, pick } from 'utilium';
41
41
  import { BufferView } from 'utilium/buffer';
42
42
  import * as c from '../constants.js';
43
43
  import { Stats } from '../node/stats.js';
44
+ import { checkAccess } from '../vfs/config.js';
44
45
  import { contextOf } from './contexts.js';
45
46
  /**
46
47
  * Root inode
@@ -654,10 +655,10 @@ export function hasAccess($, inode, access) {
654
655
  const { credentials } = contextOf($);
655
656
  if (isSymbolicLink(inode))
656
657
  return true;
657
- if (credentials.euid === 0 || credentials.egid === 0)
658
+ if (credentials.euid === 0)
658
659
  return !(access & c.X_OK) || isDirectory(inode) || !!(inode.mode & (c.S_IXUSR | c.S_IXGRP | c.S_IXOTH));
659
660
  let perm = 0;
660
- if (credentials.uid === inode.uid) {
661
+ if (credentials.euid === inode.uid) {
661
662
  if (inode.mode & c.S_IRUSR)
662
663
  perm |= c.R_OK;
663
664
  if (inode.mode & c.S_IWUSR)
@@ -665,7 +666,7 @@ export function hasAccess($, inode, access) {
665
666
  if (inode.mode & c.S_IXUSR)
666
667
  perm |= c.X_OK;
667
668
  }
668
- if (credentials.gid === inode.gid || credentials.groups.includes(Number(inode.gid))) {
669
+ else if (credentials.egid === inode.gid || credentials.groups.includes(Number(inode.gid))) {
669
670
  if (inode.mode & c.S_IRGRP)
670
671
  perm |= c.R_OK;
671
672
  if (inode.mode & c.S_IWGRP)
@@ -673,14 +674,64 @@ export function hasAccess($, inode, access) {
673
674
  if (inode.mode & c.S_IXGRP)
674
675
  perm |= c.X_OK;
675
676
  }
676
- if (inode.mode & c.S_IROTH)
677
- perm |= c.R_OK;
678
- if (inode.mode & c.S_IWOTH)
679
- perm |= c.W_OK;
680
- if (inode.mode & c.S_IXOTH)
681
- perm |= c.X_OK;
677
+ else {
678
+ if (inode.mode & c.S_IROTH)
679
+ perm |= c.R_OK;
680
+ if (inode.mode & c.S_IWOTH)
681
+ perm |= c.W_OK;
682
+ if (inode.mode & c.S_IXOTH)
683
+ perm |= c.X_OK;
684
+ }
682
685
  return (perm & access) === access;
683
686
  }
687
+ /**
688
+ * Throws if a given user may not search the directory `dir`
689
+ * @internal
690
+ */
691
+ export function assertSearchable($, dir, extra) {
692
+ if (checkAccess && !hasAccess($, dir, c.X_OK))
693
+ throw UV('EACCES', extra);
694
+ }
695
+ /**
696
+ * Throws if a given user may not remove `entry` from the directory `parent`
697
+ * @internal
698
+ */
699
+ export function assertRemovable($, parent, entry, extra) {
700
+ if (!checkAccess)
701
+ return;
702
+ if (!hasAccess($, parent, c.W_OK | c.X_OK))
703
+ throw UV('EACCES', extra);
704
+ const { euid } = contextOf($).credentials;
705
+ if (parent.mode & c.S_ISVTX && euid !== 0 && euid !== entry.uid && euid !== parent.uid)
706
+ throw UV('EPERM', extra);
707
+ }
708
+ /**
709
+ * Throws if a given user is neither root nor the owner of `inode`
710
+ * @internal
711
+ */
712
+ export function assertOwner($, inode, extra) {
713
+ if (!checkAccess)
714
+ return;
715
+ const { euid } = contextOf($).credentials;
716
+ if (euid !== 0 && euid !== inode.uid)
717
+ throw UV('EPERM', extra);
718
+ }
719
+ /**
720
+ * Throws if a given user may not change the owner of `inode` to `uid` and `gid`, where `-1` leaves an ID unchanged
721
+ * @internal
722
+ */
723
+ export function assertCanChown($, inode, uid, gid, extra) {
724
+ if (!checkAccess)
725
+ return;
726
+ const { euid, egid, groups } = contextOf($).credentials;
727
+ if (euid === 0)
728
+ return;
729
+ const isOwner = euid === inode.uid;
730
+ if (uid !== -1 && !(isOwner && uid === inode.uid))
731
+ throw UV('EPERM', extra);
732
+ if (gid !== -1 && !(isOwner && (gid === inode.gid || gid === egid || groups.includes(gid))))
733
+ throw UV('EPERM', extra);
734
+ }
684
735
  /**
685
736
  * @hidden @internal
686
737
  *
@@ -696,5 +747,19 @@ export function _chown(stats, uid, gid) {
696
747
  stats.gid = gid;
697
748
  else
698
749
  valid = false;
750
+ if (stats.mode !== undefined && !isDirectory({ mode: stats.mode })) {
751
+ stats.mode &= ~c.S_ISUID;
752
+ if (stats.mode & c.S_IXGRP)
753
+ stats.mode &= ~c.S_ISGID;
754
+ }
699
755
  return valid;
700
756
  }
757
+ /**
758
+ * @hidden @internal
759
+ */
760
+ export function _chmod($, inode, mode) {
761
+ const { euid, egid, groups } = contextOf($).credentials;
762
+ if (checkAccess && euid !== 0 && egid !== inode.gid && !groups.includes(inode.gid))
763
+ mode &= ~c.S_ISGID;
764
+ inode.mode = (inode.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
765
+ }
@@ -100,15 +100,24 @@ export interface Message {
100
100
  method: Method;
101
101
  stack: string;
102
102
  }
103
- export interface Request<TMethod extends Method = Method> extends Message {
103
+ export interface RequestInit<TMethod extends Method = Method> {
104
104
  method: TMethod;
105
105
  args: Parameters<Methods[TMethod]>;
106
106
  }
107
- export interface Response<TMethod extends Method = Method> extends Message {
108
- error?: WithOptional<ExceptionJSON, 'code' | 'errno'>;
109
- method: TMethod;
110
- value: ReturnType<Methods[TMethod]>;
111
- }
107
+ export type Request<TMethod extends Method = Method> = {
108
+ [K in Method]: Message & RequestInit<K>;
109
+ }[TMethod];
110
+ export type Response<TMethod extends Method = Method> = {
111
+ [K in Method]: Message & {
112
+ error?: WithOptional<ExceptionJSON, 'code' | 'errno'>;
113
+ method: K;
114
+ /**
115
+ * This is undefined if an error occurs, and we check it at runtime.
116
+ * We don't do the type stuff because Typescript gets confused
117
+ */
118
+ value: ReturnType<Methods[K]>;
119
+ };
120
+ }[TMethod];
112
121
  /**
113
122
  * Encode a RPC message as a string using JSON.
114
123
  * This is only done when structured cloning is not available.
@@ -130,7 +139,7 @@ export interface Executor extends PromiseWithResolvers<any> {
130
139
  fs: PortFS;
131
140
  timeout: ReturnType<typeof setTimeout>;
132
141
  }
133
- export declare function request<const TRequest extends Request, TValue>(request: Omit<TRequest, 'id' | 'stack' | '_zenfs'>, { port, timeout: ms, fs }: Partial<Options> & {
142
+ export declare function request<const Init extends RequestInit, TValue>(request: Init, { port, timeout: ms, fs }: Partial<Options> & {
134
143
  fs: PortFS;
135
144
  }): Promise<TValue>;
136
145
  export declare function handleResponse<const TMethod extends Method>(response: Response<TMethod>): void;
@@ -160,11 +160,6 @@ export function request(request, { port, timeout: ms = 1000, fs }) {
160
160
  port.send({ ...request, _zenfs: true, id, stack });
161
161
  return promise;
162
162
  }
163
- // Why Typescript, WHY does the type need to be asserted even when the method is explicitly checked?
164
- function __requestMethod(req) { }
165
- function __responseMethod(res, ...t) {
166
- return t.includes(res.method);
167
- }
168
163
  export function handleResponse(response) {
169
164
  if (!isMessage(response))
170
165
  return;
@@ -182,7 +177,7 @@ export function handleResponse(response) {
182
177
  return;
183
178
  }
184
179
  disposeExecutors(response.id);
185
- resolve(__responseMethod(response, 'stat', 'createFile', 'mkdir') ? new Inode(response.value) : response.value);
180
+ resolve(['stat', 'createFile', 'mkdir'].includes(response.method) ? new Inode(response.value) : response.value);
186
181
  return;
187
182
  }
188
183
  export function attach(port, handler) {
@@ -214,11 +209,9 @@ export async function handleRequest(port, fs, request) {
214
209
  if (!isMessage(request))
215
210
  return;
216
211
  let value, error;
217
- const transferList = [];
218
212
  try {
219
213
  switch (request.method) {
220
214
  case 'read': {
221
- __requestMethod(request);
222
215
  const [path, buffer, start, end] = request.args;
223
216
  await fs.read(path, buffer, start, end);
224
217
  value = buffer;
@@ -227,7 +220,6 @@ export async function handleRequest(port, fs, request) {
227
220
  case 'stat':
228
221
  case 'createFile':
229
222
  case 'mkdir': {
230
- __requestMethod(request);
231
223
  // @ts-expect-error 2556
232
224
  const md = await fs[request.method](...request.args);
233
225
  const inode = md instanceof Inode ? md : new Inode(md);
@@ -235,7 +227,6 @@ export async function handleRequest(port, fs, request) {
235
227
  break;
236
228
  }
237
229
  case 'touch': {
238
- __requestMethod(request);
239
230
  const [path, metadata] = request.args;
240
231
  await fs.touch(path, new Inode(metadata));
241
232
  value = undefined;
@@ -249,5 +240,5 @@ export async function handleRequest(port, fs, request) {
249
240
  catch (e) {
250
241
  error = e instanceof Exception ? e.toJSON() : pick(e, 'message', 'stack');
251
242
  }
252
- port.send({ _zenfs: true, ...pick(request, 'id', 'method', 'stack'), error, value }, transferList);
243
+ port.send({ _zenfs: true, ...pick(request, 'id', 'method', 'stack'), error, value });
253
244
  }
@@ -57,6 +57,12 @@ import { StoreFS } from '../backends/store/fs.js';
57
57
  import { isDirectory } from '../internal/inode.js';
58
58
  import { join } from '../path.js';
59
59
  import { _asyncFSKeys } from './shared.js';
60
+ /**
61
+ * Whether the stack of `error` has a patched method or `key`'s sync method below the innermost patched method.
62
+ */
63
+ function isInLoop(key, error = new Error()) {
64
+ return new RegExp(`__patched__[^]*(__patched__|\\b${key}Sync\\b)`).test(error.stack ?? '');
65
+ }
60
66
  /**
61
67
  * Async() implements synchronous methods on an asynchronous file system
62
68
  *
@@ -82,6 +88,8 @@ export function Async(FS) {
82
88
  return this.sync();
83
89
  }
84
90
  _promise = Promise.resolve();
91
+ /** The async methods before patching, which do not update the sync cache */
92
+ _unpatched = {};
85
93
  _async(thunk) {
86
94
  this._promise = this._promise.finally(() => thunk());
87
95
  }
@@ -132,7 +140,7 @@ export function Async(FS) {
132
140
  renameSync(oldPath, newPath) {
133
141
  this.checkSync();
134
142
  this._sync.renameSync(oldPath, newPath);
135
- this._async(() => this.rename(oldPath, newPath));
143
+ this._async(() => this._unpatched.rename(oldPath, newPath));
136
144
  }
137
145
  statSync(path) {
138
146
  this.checkSync();
@@ -141,28 +149,28 @@ export function Async(FS) {
141
149
  touchSync(path, metadata) {
142
150
  this.checkSync();
143
151
  this._sync.touchSync(path, metadata);
144
- this._async(() => this.touch(path, metadata));
152
+ this._async(() => this._unpatched.touch(path, metadata));
145
153
  }
146
154
  createFileSync(path, options) {
147
155
  this.checkSync();
148
156
  const result = this._sync.createFileSync(path, options);
149
- this._async(() => this.createFile(path, options));
157
+ this._async(() => this._unpatched.createFile(path, options));
150
158
  return result;
151
159
  }
152
160
  unlinkSync(path) {
153
161
  this.checkSync();
154
162
  this._sync.unlinkSync(path);
155
- this._async(() => this.unlink(path));
163
+ this._async(() => this._unpatched.unlink(path));
156
164
  }
157
165
  rmdirSync(path) {
158
166
  this.checkSync();
159
167
  this._sync.rmdirSync(path);
160
- this._async(() => this.rmdir(path));
168
+ this._async(() => this._unpatched.rmdir(path));
161
169
  }
162
170
  mkdirSync(path, options) {
163
171
  this.checkSync();
164
172
  const result = this._sync.mkdirSync(path, options);
165
- this._async(() => this.mkdir(path, options));
173
+ this._async(() => this._unpatched.mkdir(path, options));
166
174
  return result;
167
175
  }
168
176
  readdirSync(path) {
@@ -172,7 +180,7 @@ export function Async(FS) {
172
180
  linkSync(srcpath, dstpath) {
173
181
  this.checkSync();
174
182
  this._sync.linkSync(srcpath, dstpath);
175
- this._async(() => this.link(srcpath, dstpath));
183
+ this._async(() => this._unpatched.link(srcpath, dstpath));
176
184
  }
177
185
  async sync() {
178
186
  if (!this.attributes.has('no_async_preload') && this._sync)
@@ -194,7 +202,7 @@ export function Async(FS) {
194
202
  writeSync(path, buffer, offset) {
195
203
  this.checkSync();
196
204
  this._sync.writeSync(path, buffer, offset);
197
- this._async(() => this.write(path, buffer, offset));
205
+ this._async(() => this._unpatched.write(path, buffer, offset));
198
206
  }
199
207
  ioctlSync(context, command, ...args) {
200
208
  this.checkSync();
@@ -261,22 +269,9 @@ export function Async(FS) {
261
269
  for (const key of toPatch) {
262
270
  // TS does not narrow the union based on the key
263
271
  const originalMethod = this[key].bind(this);
264
- function isInLoop(depth, error) {
265
- if (!error) {
266
- error = new Error();
267
- Error.captureStackTrace(error, isInLoop);
268
- }
269
- if (!error.stack)
270
- return false;
271
- const stack = error.stack.split('\n').slice(depth).join('\n');
272
- // From the async queue
273
- return (stack.includes(`at <computed> [as ${key}]`)
274
- || stack.includes(`at async <computed> [as ${key}]`)
275
- || stack.includes(`${key}Sync `));
276
- }
277
- this[key] = async (...args) => {
272
+ const fn = async function __patched__(...args) {
278
273
  const result = await originalMethod(...args);
279
- if (isInLoop(2))
274
+ if (isInLoop(key))
280
275
  return result;
281
276
  if (!this._isInitialized) {
282
277
  this._skippedCacheUpdates++;
@@ -287,13 +282,16 @@ export function Async(FS) {
287
282
  this._sync?.[`${key}Sync`]?.(...args);
288
283
  }
289
284
  catch (e) {
290
- if (isInLoop(3, e))
285
+ if (isInLoop(key, e))
291
286
  return result;
292
287
  e.message += ' (Out of sync!)';
293
288
  throw err(e);
294
289
  }
295
290
  return result;
296
291
  };
292
+ Object.defineProperty(fn, 'name', { get: () => key });
293
+ this._unpatched[key] = originalMethod;
294
+ this[key] = fn;
297
295
  }
298
296
  debug(`Async: patched ${toPatch.length} methods`);
299
297
  }
@@ -1,28 +1,17 @@
1
+ import { RwLockable } from 'kerium/locks';
1
2
  import type { UUID } from 'node:crypto';
2
3
  import type { Concrete } from 'utilium';
3
4
  import type { CreationOptions, FileSystem, StreamOptions, UsageInfo } from '../internal/filesystem.js';
4
5
  import type { InodeLike } from '../internal/inode.js';
5
6
  import type { IoctlContext } from '../internal/ioctl.js';
6
7
  import '../polyfills.js';
7
- /**
8
- * @category Internals
9
- * @internal
10
- */
11
- export declare class MutexLock {
12
- protected readonly previous?: MutexLock | undefined;
13
- protected current: PromiseWithResolvers<void>;
14
- protected _isLocked: boolean;
15
- get isLocked(): boolean;
16
- constructor(previous?: MutexLock | undefined);
17
- done(): Promise<void>;
18
- unlock(): void;
19
- [Symbol.dispose](): void;
20
- }
21
8
  /**
22
9
  * @hidden
23
10
  * @category Internals
11
+ * @deprecated This is no longer needed and will be removed
12
+ * @todo [breaking] Remove
24
13
  */
25
- export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
14
+ export declare class _MutexedFS<T extends FileSystem> extends RwLockable implements FileSystem {
26
15
  /**
27
16
  * @internal
28
17
  */
@@ -38,31 +27,6 @@ export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
38
27
  ready(): Promise<void>;
39
28
  readySync(): void;
40
29
  usage(): UsageInfo;
41
- /**
42
- * The current locks
43
- */
44
- private currentLock?;
45
- /**
46
- * Adds a lock for a path
47
- */
48
- protected addLock(): MutexLock;
49
- /**
50
- * Locks `path` asynchronously.
51
- * If the path is currently locked, waits for it to be unlocked.
52
- * @internal
53
- */
54
- lock(timeout?: number): Promise<MutexLock>;
55
- /**
56
- * Locks `path` asynchronously.
57
- * If the path is currently locked, an error will be thrown
58
- * @internal
59
- */
60
- lockSync(): MutexLock;
61
- /**
62
- * Whether `path` is locked
63
- * @internal
64
- */
65
- get isLocked(): boolean;
66
30
  rename(oldPath: string, newPath: string): Promise<void>;
67
31
  renameSync(oldPath: string, newPath: string): void;
68
32
  stat(path: string): Promise<InodeLike>;
@@ -110,9 +74,10 @@ export declare class _MutexedFS<T extends FileSystem> implements FileSystem {
110
74
  * Instead of extending `FileSystem`,
111
75
  * `MutexedFS` implements it in order to make sure all of the methods are passed through
112
76
  *
113
- * @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
114
77
  * @category Internals
115
78
  * @internal
79
+ * @deprecated This is no longer needed and will be removed
80
+ * @todo [breaking] Remove
116
81
  */
117
82
  export declare function Mutexed<const T extends Concrete<typeof FileSystem>>(FS: T): typeof _MutexedFS<InstanceType<T>> & {
118
83
  new (...args: ConstructorParameters<T>): _MutexedFS<InstanceType<T>>;