@zenfs/core 0.18.0 → 1.0.0-rc.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.
@@ -54,7 +54,6 @@ import * as constants from './constants.js';
54
54
  import { Dir, Dirent } from './dir.js';
55
55
  import { dirname, join, parse } from './path.js';
56
56
  import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
57
- import { credentials } from '../credentials.js';
58
57
  import { ReadStream, WriteStream } from './streams.js';
59
58
  import { FSWatcher, emitChange } from './watchers.js';
60
59
  export * as constants from './constants.js';
@@ -213,7 +212,7 @@ export class FileHandle {
213
212
  }
214
213
  async stat(opts) {
215
214
  const stats = await this.file.stat();
216
- if (!stats.hasAccess(constants.R_OK, credentials)) {
215
+ if (!stats.hasAccess(constants.R_OK)) {
217
216
  throw ErrnoError.With('EACCES', this.file.path, 'stat');
218
217
  }
219
218
  return opts?.bigint ? new BigIntStats(stats) : stats;
@@ -357,7 +356,7 @@ export async function rename(oldPath, newPath) {
357
356
  newPath = normalizePath(newPath);
358
357
  const src = resolveMount(oldPath);
359
358
  const dst = resolveMount(newPath);
360
- if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK, credentials)) {
359
+ if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK)) {
361
360
  throw ErrnoError.With('EACCES', oldPath, 'rename');
362
361
  }
363
362
  try {
@@ -396,7 +395,7 @@ export async function stat(path, options) {
396
395
  const { fs, path: resolved } = resolveMount((await exists(path)) ? await realpath(path) : path);
397
396
  try {
398
397
  const stats = await fs.stat(resolved);
399
- if (!stats.hasAccess(constants.R_OK, credentials)) {
398
+ if (!stats.hasAccess(constants.R_OK)) {
400
399
  throw ErrnoError.With('EACCES', path, 'stat');
401
400
  }
402
401
  return options?.bigint ? new BigIntStats(stats) : stats;
@@ -449,7 +448,7 @@ export async function unlink(path) {
449
448
  path = normalizePath(path);
450
449
  const { fs, path: resolved } = resolveMount(path);
451
450
  try {
452
- if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
451
+ if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
453
452
  throw ErrnoError.With('EACCES', resolved, 'unlink');
454
453
  }
455
454
  await fs.unlink(resolved);
@@ -476,7 +475,7 @@ async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
476
475
  }
477
476
  // Create the file
478
477
  const parentStats = await fs.stat(dirname(resolved));
479
- if (!parentStats.hasAccess(constants.W_OK, credentials)) {
478
+ if (!parentStats.hasAccess(constants.W_OK)) {
480
479
  throw ErrnoError.With('EACCES', dirname(path), '_open');
481
480
  }
482
481
  if (!parentStats.isDirectory()) {
@@ -484,7 +483,7 @@ async function _open(path, _flag, _mode = 0o644, resolveSymlinks) {
484
483
  }
485
484
  return new FileHandle(await fs.createFile(resolved, flag, mode));
486
485
  }
487
- if (!stats.hasAccess(flagToMode(flag), credentials)) {
486
+ if (!stats.hasAccess(flagToMode(flag))) {
488
487
  throw ErrnoError.With('EACCES', path, '_open');
489
488
  }
490
489
  if (isExclusive(flag)) {
@@ -610,7 +609,7 @@ export async function rmdir(path) {
610
609
  path = (await exists(path)) ? await realpath(path) : path;
611
610
  const { fs, path: resolved } = resolveMount(path);
612
611
  try {
613
- if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
612
+ if (!(await fs.stat(resolved)).hasAccess(constants.W_OK)) {
614
613
  throw ErrnoError.With('EACCES', resolved, 'rmdir');
615
614
  }
616
615
  await fs.rmdir(resolved);
@@ -630,7 +629,7 @@ export async function mkdir(path, options) {
630
629
  const errorPaths = { [resolved]: path };
631
630
  try {
632
631
  if (!options?.recursive) {
633
- if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK, credentials)) {
632
+ if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK)) {
634
633
  throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');
635
634
  }
636
635
  await fs.mkdir(resolved, mode);
@@ -643,7 +642,7 @@ export async function mkdir(path, options) {
643
642
  errorPaths[dir] = origDir;
644
643
  }
645
644
  for (const dir of dirs) {
646
- if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK, credentials)) {
645
+ if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK)) {
647
646
  throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');
648
647
  }
649
648
  await fs.mkdir(dir, mode);
@@ -658,7 +657,7 @@ export async function mkdir(path, options) {
658
657
  mkdir;
659
658
  export async function readdir(path, options) {
660
659
  path = normalizePath(path);
661
- if (!(await stat(path)).hasAccess(constants.R_OK, credentials)) {
660
+ if (!(await stat(path)).hasAccess(constants.R_OK)) {
662
661
  throw ErrnoError.With('EACCES', path, 'readdir');
663
662
  }
664
663
  path = (await exists(path)) ? await realpath(path) : path;
@@ -695,11 +694,11 @@ readdir;
695
694
  */
696
695
  export async function link(targetPath, linkPath) {
697
696
  targetPath = normalizePath(targetPath);
698
- if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK, credentials)) {
697
+ if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK)) {
699
698
  throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
700
699
  }
701
700
  linkPath = normalizePath(linkPath);
702
- if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK, credentials)) {
701
+ if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK)) {
703
702
  throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
704
703
  }
705
704
  const { fs, path } = resolveMount(targetPath);
@@ -708,7 +707,7 @@ export async function link(targetPath, linkPath) {
708
707
  throw ErrnoError.With('EXDEV', linkPath, 'link');
709
708
  }
710
709
  try {
711
- if (!(await fs.stat(path)).hasAccess(constants.W_OK, credentials)) {
710
+ if (!(await fs.stat(path)).hasAccess(constants.W_OK)) {
712
711
  throw ErrnoError.With('EACCES', path, 'link');
713
712
  }
714
713
  return await fs.link(path, link.path);
@@ -938,7 +937,7 @@ watch;
938
937
  */
939
938
  export async function access(path, mode = constants.F_OK) {
940
939
  const stats = await stat(path);
941
- if (!stats.hasAccess(mode, credentials)) {
940
+ if (!stats.hasAccess(mode)) {
942
941
  throw new ErrnoError(Errno.EACCES);
943
942
  }
944
943
  }
@@ -52,7 +52,6 @@ import * as constants from './constants.js';
52
52
  import { Dir, Dirent } from './dir.js';
53
53
  import { dirname, join, parse } from './path.js';
54
54
  import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
55
- import { credentials } from '../credentials.js';
56
55
  import { emitChange } from './watchers.js';
57
56
  /**
58
57
  * Synchronous rename.
@@ -64,7 +63,7 @@ export function renameSync(oldPath, newPath) {
64
63
  newPath = normalizePath(newPath);
65
64
  const oldMount = resolveMount(oldPath);
66
65
  const newMount = resolveMount(newPath);
67
- if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK, credentials)) {
66
+ if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK)) {
68
67
  throw ErrnoError.With('EACCES', oldPath, 'rename');
69
68
  }
70
69
  try {
@@ -105,7 +104,7 @@ export function statSync(path, options) {
105
104
  const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
106
105
  try {
107
106
  const stats = fs.statSync(resolved);
108
- if (!stats.hasAccess(constants.R_OK, credentials)) {
107
+ if (!stats.hasAccess(constants.R_OK)) {
109
108
  throw ErrnoError.With('EACCES', path, 'stat');
110
109
  }
111
110
  return options?.bigint ? new BigIntStats(stats) : stats;
@@ -159,7 +158,7 @@ export function unlinkSync(path) {
159
158
  path = normalizePath(path);
160
159
  const { fs, path: resolved } = resolveMount(path);
161
160
  try {
162
- if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
161
+ if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
163
162
  throw ErrnoError.With('EACCES', resolved, 'unlink');
164
163
  }
165
164
  fs.unlinkSync(resolved);
@@ -181,7 +180,7 @@ function _openSync(path, _flag, _mode, resolveSymlinks = true) {
181
180
  }
182
181
  // Create the file
183
182
  const parentStats = fs.statSync(dirname(resolved));
184
- if (!parentStats.hasAccess(constants.W_OK, credentials)) {
183
+ if (!parentStats.hasAccess(constants.W_OK)) {
185
184
  throw ErrnoError.With('EACCES', dirname(path), '_open');
186
185
  }
187
186
  if (!parentStats.isDirectory()) {
@@ -190,7 +189,7 @@ function _openSync(path, _flag, _mode, resolveSymlinks = true) {
190
189
  return fs.createFileSync(resolved, flag, mode);
191
190
  }
192
191
  const stats = fs.statSync(resolved);
193
- if (!stats.hasAccess(mode, credentials) || !stats.hasAccess(flagToMode(flag), credentials)) {
192
+ if (!stats.hasAccess(mode) || !stats.hasAccess(flagToMode(flag))) {
194
193
  throw ErrnoError.With('EACCES', path, '_open');
195
194
  }
196
195
  if (isExclusive(flag)) {
@@ -440,7 +439,7 @@ export function rmdirSync(path) {
440
439
  path = normalizePath(path);
441
440
  const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
442
441
  try {
443
- if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
442
+ if (!fs.statSync(resolved).hasAccess(constants.W_OK)) {
444
443
  throw ErrnoError.With('EACCES', resolved, 'rmdir');
445
444
  }
446
445
  fs.rmdirSync(resolved);
@@ -460,7 +459,7 @@ export function mkdirSync(path, options) {
460
459
  const errorPaths = { [resolved]: path };
461
460
  try {
462
461
  if (!options?.recursive) {
463
- if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK, credentials)) {
462
+ if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK)) {
464
463
  throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');
465
464
  }
466
465
  return fs.mkdirSync(resolved, mode);
@@ -471,7 +470,7 @@ export function mkdirSync(path, options) {
471
470
  errorPaths[dir] = original;
472
471
  }
473
472
  for (const dir of dirs) {
474
- if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK, credentials)) {
473
+ if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK)) {
475
474
  throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');
476
475
  }
477
476
  fs.mkdirSync(dir, mode);
@@ -488,7 +487,7 @@ export function readdirSync(path, options) {
488
487
  path = normalizePath(path);
489
488
  const { fs, path: resolved } = resolveMount(existsSync(path) ? realpathSync(path) : path);
490
489
  let entries;
491
- if (!statSync(path).hasAccess(constants.R_OK, credentials)) {
490
+ if (!statSync(path).hasAccess(constants.R_OK)) {
492
491
  throw ErrnoError.With('EACCES', path, 'readdir');
493
492
  }
494
493
  try {
@@ -527,11 +526,11 @@ readdirSync;
527
526
  */
528
527
  export function linkSync(targetPath, linkPath) {
529
528
  targetPath = normalizePath(targetPath);
530
- if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK, credentials)) {
529
+ if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK)) {
531
530
  throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
532
531
  }
533
532
  linkPath = normalizePath(linkPath);
534
- if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK, credentials)) {
533
+ if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK)) {
535
534
  throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
536
535
  }
537
536
  const { fs, path } = resolveMount(targetPath);
@@ -540,7 +539,7 @@ export function linkSync(targetPath, linkPath) {
540
539
  throw ErrnoError.With('EXDEV', linkPath, 'link');
541
540
  }
542
541
  try {
543
- if (!fs.statSync(path).hasAccess(constants.W_OK, credentials)) {
542
+ if (!fs.statSync(path).hasAccess(constants.W_OK)) {
544
543
  throw ErrnoError.With('EACCES', path, 'link');
545
544
  }
546
545
  return fs.linkSync(path, linkPath);
@@ -672,7 +671,7 @@ realpathSync;
672
671
  */
673
672
  export function accessSync(path, mode = 0o600) {
674
673
  const stats = statSync(path);
675
- if (!stats.hasAccess(mode, credentials)) {
674
+ if (!stats.hasAccess(mode)) {
676
675
  throw new ErrnoError(Errno.EACCES);
677
676
  }
678
677
  }
package/dist/file.d.ts CHANGED
@@ -37,13 +37,28 @@ export declare function isSynchronous(flag: string): boolean;
37
37
  export declare function isExclusive(flag: string): boolean;
38
38
  export declare abstract class File {
39
39
  /**
40
- * Get the current file position.
40
+ * @internal
41
+ * The file system that created the file
41
42
  */
42
- abstract position: number;
43
+ fs: FileSystem;
44
+ /**
45
+ * The path to the file
46
+ */
47
+ readonly path: string;
48
+ constructor(
49
+ /**
50
+ * @internal
51
+ * The file system that created the file
52
+ */
53
+ fs: FileSystem,
43
54
  /**
44
55
  * The path to the file
45
56
  */
46
- abstract readonly path: string;
57
+ path: string);
58
+ /**
59
+ * Get the current file position.
60
+ */
61
+ abstract position: number;
47
62
  /**
48
63
  * Asynchronous `stat`.
49
64
  */
@@ -183,12 +198,9 @@ export declare abstract class File {
183
198
  export declare class PreloadFile<FS extends FileSystem> extends File {
184
199
  /**
185
200
  * The file system that created the file.
201
+ * @internal
186
202
  */
187
- protected fs: FS;
188
- /**
189
- * Path to the file
190
- */
191
- readonly path: string;
203
+ fs: FS;
192
204
  readonly flag: string;
193
205
  readonly stats: Stats;
194
206
  protected _buffer: Uint8Array;
@@ -219,12 +231,9 @@ export declare class PreloadFile<FS extends FileSystem> extends File {
219
231
  constructor(
220
232
  /**
221
233
  * The file system that created the file.
234
+ * @internal
222
235
  */
223
- fs: FS,
224
- /**
225
- * Path to the file
226
- */
227
- path: string, flag: string, stats: Stats, _buffer?: Uint8Array);
236
+ fs: FS, path: string, flag: string, stats: Stats, _buffer?: Uint8Array);
228
237
  /**
229
238
  * Get the underlying buffer for this file. Mutating not recommended and will mess up dirty tracking.
230
239
  */
package/dist/file.js CHANGED
@@ -105,6 +105,19 @@ export function isExclusive(flag) {
105
105
  return flag.indexOf('x') !== -1;
106
106
  }
107
107
  export class File {
108
+ constructor(
109
+ /**
110
+ * @internal
111
+ * The file system that created the file
112
+ */
113
+ fs,
114
+ /**
115
+ * The path to the file
116
+ */
117
+ path) {
118
+ this.fs = fs;
119
+ this.path = path;
120
+ }
108
121
  [Symbol.asyncDispose]() {
109
122
  return this.close();
110
123
  }
@@ -149,15 +162,11 @@ export class PreloadFile extends File {
149
162
  constructor(
150
163
  /**
151
164
  * The file system that created the file.
165
+ * @internal
152
166
  */
153
- fs,
154
- /**
155
- * Path to the file
156
- */
157
- path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))) {
158
- super();
167
+ fs, path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))) {
168
+ super(fs, path);
159
169
  this.fs = fs;
160
- this.path = path;
161
170
  this.flag = flag;
162
171
  this.stats = stats;
163
172
  this._buffer = _buffer;
@@ -1,17 +1,76 @@
1
- import type { FileSystem } from '../filesystem.js';
1
+ import type { File } from '../file.js';
2
+ import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
2
3
  import '../polyfills.js';
3
- import type { Mixin } from './shared.js';
4
+ import type { Stats } from '../stats.js';
5
+ import type { Concrete } from '../utils.js';
4
6
  export declare class MutexLock {
5
- readonly path: string;
6
7
  protected readonly previous?: MutexLock | undefined;
7
8
  protected current: PromiseWithResolvers<void>;
8
9
  protected _isLocked: boolean;
9
10
  get isLocked(): boolean;
10
- constructor(path: string, previous?: MutexLock | undefined);
11
+ constructor(previous?: MutexLock | undefined);
11
12
  done(): Promise<void>;
12
13
  unlock(): void;
13
14
  [Symbol.dispose](): void;
14
15
  }
16
+ /**
17
+ * @hidden
18
+ */
19
+ export declare class __MutexedFS<T extends FileSystem> implements FileSystem {
20
+ /**
21
+ * @internal
22
+ */
23
+ _fs: T;
24
+ ready(): Promise<void>;
25
+ metadata(): FileSystemMetadata;
26
+ /**
27
+ * The current locks
28
+ */
29
+ private currentLock?;
30
+ /**
31
+ * Adds a lock for a path
32
+ */
33
+ protected addLock(): MutexLock;
34
+ /**
35
+ * Locks `path` asynchronously.
36
+ * If the path is currently locked, waits for it to be unlocked.
37
+ * @internal
38
+ */
39
+ lock(path: string, syscall: string): Promise<MutexLock>;
40
+ /**
41
+ * Locks `path` asynchronously.
42
+ * If the path is currently locked, an error will be thrown
43
+ * @internal
44
+ */
45
+ lockSync(path: string, syscall: string): MutexLock;
46
+ /**
47
+ * Whether `path` is locked
48
+ * @internal
49
+ */
50
+ get isLocked(): boolean;
51
+ rename(oldPath: string, newPath: string): Promise<void>;
52
+ renameSync(oldPath: string, newPath: string): void;
53
+ stat(path: string): Promise<Stats>;
54
+ statSync(path: string): Stats;
55
+ openFile(path: string, flag: string): Promise<File>;
56
+ openFileSync(path: string, flag: string): File;
57
+ createFile(path: string, flag: string, mode: number): Promise<File>;
58
+ createFileSync(path: string, flag: string, mode: number): File;
59
+ unlink(path: string): Promise<void>;
60
+ unlinkSync(path: string): void;
61
+ rmdir(path: string): Promise<void>;
62
+ rmdirSync(path: string): void;
63
+ mkdir(path: string, mode: number): Promise<void>;
64
+ mkdirSync(path: string, mode: number): void;
65
+ readdir(path: string): Promise<string[]>;
66
+ readdirSync(path: string): string[];
67
+ exists(path: string): Promise<boolean>;
68
+ existsSync(path: string): boolean;
69
+ link(srcpath: string, dstpath: string): Promise<void>;
70
+ linkSync(srcpath: string, dstpath: string): void;
71
+ sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
72
+ syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
73
+ }
15
74
  /**
16
75
  * This serializes access to an underlying async filesystem.
17
76
  * For example, on an OverlayFS instance with an async lower
@@ -21,13 +80,16 @@ export declare class MutexLock {
21
80
  * to avoid having to reason about the correctness of
22
81
  * multiple requests interleaving.
23
82
  *
24
- * Note: `@ts-expect-error 2513` is needed because `FS` is not properly detected as being concrete
83
+ * Note:
84
+ * Instead of extending the passed class, `MutexedFS` stores it internally.
85
+ * This is to avoid a deadlock caused when a mathod calls another one
86
+ * The problem is discussed extensivly in [#78](https://github.com/zen-fs/core/issues/78)
87
+ * Instead of extending `FileSystem`,
88
+ * `MutexedFS` implements it in order to make sure all of the methods are passed through
25
89
  *
26
90
  * @todo Change `using _` to `using void` pending https://github.com/tc39/proposal-discard-binding
27
91
  * @internal
28
92
  */
29
- export declare function Mutexed<T extends new (...args: any[]) => FileSystem>(FS: T): Mixin<T, {
30
- lock(path: string, syscall: string): Promise<MutexLock>;
31
- lockSync(path: string): MutexLock;
32
- isLocked(path: string): boolean;
33
- }>;
93
+ export declare function Mutexed<const T extends Concrete<typeof FileSystem>>(FS: T): typeof __MutexedFS<InstanceType<T>> & {
94
+ new (...args: ConstructorParameters<T>): __MutexedFS<InstanceType<T>>;
95
+ };