@zenfs/core 1.3.3 → 1.3.5

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.
@@ -4,7 +4,7 @@ import { type MountConfiguration } from '../../config.js';
4
4
  import { File } from '../../file.js';
5
5
  import { FileSystem, type FileSystemMetadata } from '../../filesystem.js';
6
6
  import { Async } from '../../mixins/async.js';
7
- import { Stats, type FileType } from '../../stats.js';
7
+ import { Stats } from '../../stats.js';
8
8
  import type { Backend, FilesystemOf } from '../backend.js';
9
9
  import * as RPC from './rpc.js';
10
10
  type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;
@@ -37,7 +37,6 @@ export declare class PortFile extends File {
37
37
  chmodSync(): void;
38
38
  utimes(atime: Date, mtime: Date): Promise<void>;
39
39
  utimesSync(): void;
40
- _setType(type: FileType): Promise<void>;
41
40
  _setTypeSync(): void;
42
41
  close(): Promise<void>;
43
42
  closeSync(): void;
@@ -67,9 +67,6 @@ export class PortFile extends File {
67
67
  utimesSync() {
68
68
  this._throwNoSync('utimes');
69
69
  }
70
- _setType(type) {
71
- return this.rpc('_setType', type);
72
- }
73
70
  _setTypeSync() {
74
71
  this._throwNoSync('_setType');
75
72
  }
package/dist/config.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
2
- import type { AbsolutePath } from './emulation/path.js';
3
2
  /**
4
3
  * Configuration for a specific mount point
5
4
  */
@@ -9,8 +8,11 @@ export type MountConfiguration<T extends Backend> = FilesystemOf<T> | BackendCon
9
8
  * @see MountConfiguration
10
9
  */
11
10
  export declare function resolveMountConfig<T extends Backend>(configuration: MountConfiguration<T>, _depth?: number): Promise<FilesystemOf<T>>;
11
+ /**
12
+ * An object mapping mount points to backends
13
+ */
12
14
  export interface ConfigMounts {
13
- [K: AbsolutePath]: Backend;
15
+ [K: string]: Backend;
14
16
  }
15
17
  /**
16
18
  * Configuration
@@ -20,7 +22,7 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
20
22
  * An object mapping mount points to mount configuration
21
23
  */
22
24
  mounts: {
23
- [K in keyof T & AbsolutePath]: MountConfiguration<T[K]>;
25
+ [K in keyof T]: MountConfiguration<T[K]>;
24
26
  };
25
27
  /**
26
28
  * The uid to use
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
2
- import { credentials } from './credentials.js';
2
+ import { useCredentials } from './credentials.js';
3
3
  import { DeviceFS } from './devices.js';
4
4
  import * as cache from './emulation/cache.js';
5
5
  import { config } from './emulation/config.js';
@@ -87,7 +87,7 @@ async function mount(path, mount) {
87
87
  export async function configure(configuration) {
88
88
  const uid = 'uid' in configuration ? configuration.uid || 0 : 0;
89
89
  const gid = 'gid' in configuration ? configuration.gid || 0 : 0;
90
- Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
90
+ useCredentials({ uid, gid });
91
91
  cache.stats.isEnabled = configuration.cacheStats ?? false;
92
92
  cache.paths.isEnabled = configuration.cachePaths ?? false;
93
93
  config.checkAccess = !configuration.disableAccessChecks;
@@ -96,10 +96,8 @@ export async function configure(configuration) {
96
96
  if (configuration.mounts) {
97
97
  const toMount = [];
98
98
  let unmountRoot = false;
99
- for (const [point, mountConfig] of Object.entries(configuration.mounts)) {
100
- if (!point.startsWith('/')) {
101
- throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
102
- }
99
+ for (const [_point, mountConfig] of Object.entries(configuration.mounts)) {
100
+ const point = _point.startsWith('/') ? _point : '/' + _point;
103
101
  if (isBackendConfig(mountConfig)) {
104
102
  mountConfig.disableAsyncCache ?? (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);
105
103
  }
@@ -10,9 +10,21 @@ export interface Credentials {
10
10
  sgid: number;
11
11
  euid: number;
12
12
  egid: number;
13
+ /**
14
+ * List of group IDs.
15
+ */
16
+ groups: number[];
13
17
  }
14
18
  export declare const credentials: Credentials;
19
+ export interface CredentialInit {
20
+ uid: number;
21
+ gid: number;
22
+ suid?: number;
23
+ sgid?: number;
24
+ euid?: number;
25
+ egid?: number;
26
+ }
15
27
  /**
16
- * @deprecated
28
+ * Uses credentials from the provided uid and gid.
17
29
  */
18
- export declare const rootCredentials: Credentials;
30
+ export declare function useCredentials(source: CredentialInit): void;
@@ -5,15 +5,17 @@ export const credentials = {
5
5
  sgid: 0,
6
6
  euid: 0,
7
7
  egid: 0,
8
+ groups: [],
8
9
  };
9
10
  /**
10
- * @deprecated
11
+ * Uses credentials from the provided uid and gid.
11
12
  */
12
- export const rootCredentials = {
13
- uid: 0,
14
- gid: 0,
15
- suid: 0,
16
- sgid: 0,
17
- euid: 0,
18
- egid: 0,
19
- };
13
+ export function useCredentials(source) {
14
+ Object.assign(credentials, {
15
+ suid: source.uid,
16
+ sgid: source.gid,
17
+ euid: source.uid,
18
+ egid: source.gid,
19
+ ...source,
20
+ });
21
+ }
@@ -7,26 +7,30 @@ export declare class Cache<T> {
7
7
  isEnabled: boolean;
8
8
  protected sync: Map<string, T>;
9
9
  protected async: Map<string, Promise<T>>;
10
+ /**
11
+ * Whether the data exists in the cache
12
+ */
13
+ has(path: string): boolean;
10
14
  /**
11
15
  * Gets data from the cache, if is exists and the cache is enabled.
12
16
  */
13
- getSync(path: string): T | undefined;
17
+ get(path: string): T | undefined;
14
18
  /**
15
19
  * Adds data if the cache is enabled
16
20
  */
17
- setSync(path: string, value: T): void;
21
+ set(path: string, value: T): void;
18
22
  /**
19
- * Clears the cache if it is enabled
23
+ * Whether the data exists in the cache
20
24
  */
21
- clearSync(): void;
25
+ hasAsync(path: string): boolean;
22
26
  /**
23
27
  * Gets data from the cache, if it exists and the cache is enabled.
24
28
  */
25
- get(path: string): Promise<T> | undefined;
29
+ getAsync(path: string): Promise<T> | undefined;
26
30
  /**
27
31
  * Adds data if the cache is enabled
28
32
  */
29
- set(path: string, value: Promise<T>): void;
33
+ setAsync(path: string, value: Promise<T>): void;
30
34
  /**
31
35
  * Clears the cache if it is enabled
32
36
  */
@@ -9,10 +9,16 @@ export class Cache {
9
9
  this.sync = new Map();
10
10
  this.async = new Map();
11
11
  }
12
+ /**
13
+ * Whether the data exists in the cache
14
+ */
15
+ has(path) {
16
+ return this.isEnabled && this.sync.has(path);
17
+ }
12
18
  /**
13
19
  * Gets data from the cache, if is exists and the cache is enabled.
14
20
  */
15
- getSync(path) {
21
+ get(path) {
16
22
  if (!this.isEnabled)
17
23
  return;
18
24
  return this.sync.get(path);
@@ -20,24 +26,22 @@ export class Cache {
20
26
  /**
21
27
  * Adds data if the cache is enabled
22
28
  */
23
- setSync(path, value) {
29
+ set(path, value) {
24
30
  if (!this.isEnabled)
25
31
  return;
26
32
  this.sync.set(path, value);
27
33
  this.async.set(path, Promise.resolve(value));
28
34
  }
29
35
  /**
30
- * Clears the cache if it is enabled
36
+ * Whether the data exists in the cache
31
37
  */
32
- clearSync() {
33
- if (!this.isEnabled)
34
- return;
35
- this.sync.clear();
38
+ hasAsync(path) {
39
+ return this.isEnabled && this.async.has(path);
36
40
  }
37
41
  /**
38
42
  * Gets data from the cache, if it exists and the cache is enabled.
39
43
  */
40
- get(path) {
44
+ getAsync(path) {
41
45
  if (!this.isEnabled)
42
46
  return;
43
47
  return this.async.get(path);
@@ -45,7 +49,7 @@ export class Cache {
45
49
  /**
46
50
  * Adds data if the cache is enabled
47
51
  */
48
- set(path, value) {
52
+ setAsync(path, value) {
49
53
  if (!this.isEnabled)
50
54
  return;
51
55
  this.async.set(path, value);
@@ -57,6 +61,7 @@ export class Cache {
57
61
  clear() {
58
62
  if (!this.isEnabled)
59
63
  return;
64
+ this.sync.clear();
60
65
  this.async.clear();
61
66
  }
62
67
  }
@@ -56,7 +56,7 @@ import * as cache from './cache.js';
56
56
  import { config } from './config.js';
57
57
  import * as constants from './constants.js';
58
58
  import { Dir, Dirent } from './dir.js';
59
- import { dirname, join, parse } from './path.js';
59
+ import { dirname, join, parse, resolve } from './path.js';
60
60
  import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
61
61
  import { ReadStream, WriteStream } from './streams.js';
62
62
  import { FSWatcher, emitChange } from './watchers.js';
@@ -360,6 +360,7 @@ export async function rename(oldPath, newPath) {
360
360
  if (src.mountPoint == dst.mountPoint) {
361
361
  await src.fs.rename(src.path, dst.path);
362
362
  emitChange('rename', oldPath.toString());
363
+ emitChange('change', newPath.toString());
363
364
  return;
364
365
  }
365
366
  await writeFile(newPath, await readFile(oldPath));
@@ -435,7 +436,7 @@ export async function unlink(path) {
435
436
  path = normalizePath(path);
436
437
  const { fs, path: resolved } = resolveMount(path);
437
438
  try {
438
- if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
439
+ if (config.checkAccess && !(await (cache.stats.getAsync(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
439
440
  throw ErrnoError.With('EACCES', resolved, 'unlink');
440
441
  }
441
442
  await fs.unlink(resolved);
@@ -583,7 +584,7 @@ export async function rmdir(path) {
583
584
  path = await realpath(path);
584
585
  const { fs, path: resolved } = resolveMount(path);
585
586
  try {
586
- const stats = await (cache.stats.get(path) || fs.stat(resolved));
587
+ const stats = await (cache.stats.getAsync(path) || fs.stat(resolved));
587
588
  if (!stats) {
588
589
  throw ErrnoError.With('ENOENT', path, 'rmdir');
589
590
  }
@@ -642,8 +643,8 @@ export async function readdir(path, options) {
642
643
  throw fixError(e, { [resolved]: path });
643
644
  };
644
645
  const { fs, path: resolved } = resolveMount(path);
645
- const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
646
- cache.stats.set(path, _stats);
646
+ const _stats = cache.stats.getAsync(path) || fs.stat(resolved).catch(handleError);
647
+ cache.stats.setAsync(path, _stats);
647
648
  const stats = await _stats;
648
649
  if (!stats) {
649
650
  throw ErrnoError.With('ENOENT', path, 'readdir');
@@ -659,8 +660,8 @@ export async function readdir(path, options) {
659
660
  const addEntry = async (entry) => {
660
661
  let entryStats;
661
662
  if (options?.recursive || options?.withFileTypes) {
662
- const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
663
- cache.stats.set(join(path, entry), _entryStats);
663
+ const _entryStats = cache.stats.getAsync(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
664
+ cache.stats.setAsync(join(path, entry), _entryStats);
664
665
  entryStats = await _entryStats;
665
666
  }
666
667
  if (options?.withFileTypes) {
@@ -737,7 +738,7 @@ export async function symlink(target, path, type = 'file') {
737
738
  }
738
739
  const handle = __addDisposableResource(env_5, await _open(path, 'w+', 0o644, false), true);
739
740
  await handle.writeFile(target.toString());
740
- await handle.file._setType(constants.S_IFLNK);
741
+ await handle.file.chmod(constants.S_IFLNK);
741
742
  }
742
743
  catch (e_5) {
743
744
  env_5.error = e_5;
@@ -880,17 +881,23 @@ export async function lutimes(path, atime, mtime) {
880
881
  lutimes;
881
882
  export async function realpath(path, options) {
882
883
  path = normalizePath(path);
884
+ if (cache.paths.hasAsync(path))
885
+ return cache.paths.getAsync(path);
883
886
  const { base, dir } = parse(path);
884
- const lpath = join(dir == '/' ? '/' : await (cache.paths.get(dir) || realpath(dir)), base);
885
- const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
887
+ const realDir = dir == '/' ? '/' : await (cache.paths.getAsync(dir) || realpath(dir));
888
+ const lpath = join(realDir, base);
889
+ const { fs, path: resolvedPath } = resolveMount(lpath);
886
890
  try {
887
- const _stats = cache.stats.get(lpath) || fs.stat(resolvedPath);
888
- cache.stats.set(lpath, _stats);
891
+ const _stats = cache.stats.getAsync(lpath) || fs.stat(resolvedPath);
892
+ cache.stats.setAsync(lpath, _stats);
889
893
  if (!(await _stats).isSymbolicLink()) {
894
+ cache.paths.set(path, lpath);
890
895
  return lpath;
891
896
  }
892
- const target = mountPoint + (await readlink(lpath));
893
- return await (cache.paths.get(target) || realpath(target));
897
+ const target = resolve(realDir, await readlink(lpath));
898
+ const real = cache.paths.getAsync(target) || realpath(target);
899
+ cache.paths.setAsync(path, real);
900
+ return await real;
894
901
  }
895
902
  catch (e) {
896
903
  if (e.code == 'ENOENT') {
@@ -945,7 +952,7 @@ access;
945
952
  */
946
953
  export async function rm(path, options) {
947
954
  path = normalizePath(path);
948
- const stats = await (cache.stats.get(path) ||
955
+ const stats = await (cache.stats.getAsync(path) ||
949
956
  stat(path).catch((error) => {
950
957
  if (error.code == 'ENOENT' && options?.force)
951
958
  return undefined;
@@ -954,7 +961,7 @@ export async function rm(path, options) {
954
961
  if (!stats) {
955
962
  return;
956
963
  }
957
- cache.stats.setSync(path, stats);
964
+ cache.stats.set(path, stats);
958
965
  switch (stats.mode & constants.S_IFMT) {
959
966
  case constants.S_IFDIR:
960
967
  if (options?.recursive) {
@@ -966,10 +973,10 @@ export async function rm(path, options) {
966
973
  break;
967
974
  case constants.S_IFREG:
968
975
  case constants.S_IFLNK:
969
- await unlink(path);
970
- break;
971
976
  case constants.S_IFBLK:
972
977
  case constants.S_IFCHR:
978
+ await unlink(path);
979
+ break;
973
980
  case constants.S_IFIFO:
974
981
  case constants.S_IFSOCK:
975
982
  default:
@@ -4,6 +4,7 @@ import { Errno, ErrnoError } from '../error.js';
4
4
  import { normalizePath } from '../utils.js';
5
5
  import { resolve } from './path.js';
6
6
  import { size_max } from './constants.js';
7
+ import { paths as pathCache } from './cache.js';
7
8
  // descriptors
8
9
  export const fdMap = new Map();
9
10
  let nextFd = 100;
@@ -37,6 +38,7 @@ export function mount(mountPoint, fs) {
37
38
  throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already in use.');
38
39
  }
39
40
  mounts.set(mountPoint, fs);
41
+ pathCache.clear();
40
42
  }
41
43
  /**
42
44
  * Unmounts the file system at `mountPoint`.
@@ -50,6 +52,7 @@ export function umount(mountPoint) {
50
52
  throw new ErrnoError(Errno.EINVAL, 'Mount point ' + mountPoint + ' is already unmounted.');
51
53
  }
52
54
  mounts.delete(mountPoint);
55
+ pathCache.clear();
53
56
  }
54
57
  /**
55
58
  * Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
@@ -54,7 +54,7 @@ import * as cache from './cache.js';
54
54
  import { config } from './config.js';
55
55
  import * as constants from './constants.js';
56
56
  import { Dir, Dirent } from './dir.js';
57
- import { dirname, join, parse } from './path.js';
57
+ import { dirname, join, parse, resolve } from './path.js';
58
58
  import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
59
59
  import { emitChange } from './watchers.js';
60
60
  export function renameSync(oldPath, newPath) {
@@ -69,6 +69,7 @@ export function renameSync(oldPath, newPath) {
69
69
  if (oldMount === newMount) {
70
70
  oldMount.fs.renameSync(oldMount.path, newMount.path);
71
71
  emitChange('rename', oldPath.toString());
72
+ emitChange('change', newPath.toString());
72
73
  return;
73
74
  }
74
75
  writeFileSync(newPath, readFileSync(oldPath));
@@ -147,7 +148,7 @@ export function unlinkSync(path) {
147
148
  path = normalizePath(path);
148
149
  const { fs, path: resolved } = resolveMount(path);
149
150
  try {
150
- if (config.checkAccess && !(cache.stats.getSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
151
+ if (config.checkAccess && !(cache.stats.get(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
151
152
  throw ErrnoError.With('EACCES', resolved, 'unlink');
152
153
  }
153
154
  fs.unlinkSync(resolved);
@@ -393,7 +394,7 @@ export function rmdirSync(path) {
393
394
  path = normalizePath(path);
394
395
  const { fs, path: resolved } = resolveMount(realpathSync(path));
395
396
  try {
396
- const stats = cache.stats.getSync(path) || fs.statSync(resolved);
397
+ const stats = cache.stats.get(path) || fs.statSync(resolved);
397
398
  if (!stats.isDirectory()) {
398
399
  throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
399
400
  }
@@ -446,8 +447,8 @@ export function readdirSync(path, options) {
446
447
  const { fs, path: resolved } = resolveMount(realpathSync(path));
447
448
  let entries;
448
449
  try {
449
- const stats = cache.stats.getSync(path) || fs.statSync(resolved);
450
- cache.stats.setSync(path, stats);
450
+ const stats = cache.stats.get(path) || fs.statSync(resolved);
451
+ cache.stats.set(path, stats);
451
452
  if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {
452
453
  throw ErrnoError.With('EACCES', resolved, 'readdir');
453
454
  }
@@ -462,8 +463,8 @@ export function readdirSync(path, options) {
462
463
  // Iterate over entries and handle recursive case if needed
463
464
  const values = [];
464
465
  for (const entry of entries) {
465
- const entryStat = cache.stats.getSync(join(path, entry)) || fs.statSync(join(resolved, entry));
466
- cache.stats.setSync(join(path, entry), entryStat);
466
+ const entryStat = cache.stats.get(join(path, entry)) || fs.statSync(join(resolved, entry));
467
+ cache.stats.set(join(path, entry), entryStat);
467
468
  if (options?.withFileTypes) {
468
469
  values.push(new Dirent(entry, entryStat));
469
470
  }
@@ -489,7 +490,7 @@ export function readdirSync(path, options) {
489
490
  }
490
491
  }
491
492
  if (!options?._isIndirect) {
492
- cache.stats.clearSync();
493
+ cache.stats.clear();
493
494
  }
494
495
  return values;
495
496
  }
@@ -535,7 +536,7 @@ export function symlinkSync(target, path, type = 'file') {
535
536
  }
536
537
  writeFileSync(path, target.toString());
537
538
  const file = _openSync(path, 'r+', 0o644, false);
538
- file._setTypeSync(constants.S_IFLNK);
539
+ file.chmodSync(constants.S_IFLNK);
539
540
  }
540
541
  symlinkSync;
541
542
  export function readlinkSync(path, options) {
@@ -592,16 +593,23 @@ export function lutimesSync(path, atime, mtime) {
592
593
  lutimesSync;
593
594
  export function realpathSync(path, options) {
594
595
  path = normalizePath(path);
596
+ if (cache.paths.has(path))
597
+ return cache.paths.get(path);
595
598
  const { base, dir } = parse(path);
596
- const lpath = join(dir == '/' ? '/' : cache.paths.getSync(dir) || realpathSync(dir), base);
597
- const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
599
+ const realDir = dir == '/' ? '/' : cache.paths.get(dir) || realpathSync(dir);
600
+ const lpath = join(realDir, base);
601
+ const { fs, path: resolvedPath } = resolveMount(lpath);
598
602
  try {
599
- const stats = fs.statSync(resolvedPath);
603
+ const stats = cache.stats.get(lpath) || fs.statSync(resolvedPath);
604
+ cache.stats.set(lpath, stats);
600
605
  if (!stats.isSymbolicLink()) {
606
+ cache.paths.set(path, lpath);
601
607
  return lpath;
602
608
  }
603
- const target = mountPoint + readlinkSync(lpath, options).toString();
604
- return cache.paths.getSync(target) || realpathSync(target);
609
+ const target = resolve(realDir, readlinkSync(lpath, options).toString());
610
+ const real = cache.paths.get(target) || realpathSync(target);
611
+ cache.paths.set(path, real);
612
+ return real;
605
613
  }
606
614
  catch (e) {
607
615
  if (e.code == 'ENOENT') {
@@ -627,7 +635,7 @@ export function rmSync(path, options) {
627
635
  path = normalizePath(path);
628
636
  let stats;
629
637
  try {
630
- stats = cache.stats.getSync(path) || statSync(path);
638
+ stats = cache.stats.get(path) || statSync(path);
631
639
  }
632
640
  catch (error) {
633
641
  if (error.code != 'ENOENT' || !options?.force)
@@ -636,7 +644,7 @@ export function rmSync(path, options) {
636
644
  if (!stats) {
637
645
  return;
638
646
  }
639
- cache.stats.setSync(path, stats);
647
+ cache.stats.set(path, stats);
640
648
  switch (stats.mode & constants.S_IFMT) {
641
649
  case constants.S_IFDIR:
642
650
  if (options?.recursive) {
@@ -648,18 +656,18 @@ export function rmSync(path, options) {
648
656
  break;
649
657
  case constants.S_IFREG:
650
658
  case constants.S_IFLNK:
651
- unlinkSync(path);
652
- break;
653
659
  case constants.S_IFBLK:
654
660
  case constants.S_IFCHR:
661
+ unlinkSync(path);
662
+ break;
655
663
  case constants.S_IFIFO:
656
664
  case constants.S_IFSOCK:
657
665
  default:
658
- cache.stats.clearSync();
666
+ cache.stats.clear();
659
667
  throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
660
668
  }
661
669
  if (!options?._isIndirect) {
662
- cache.stats.clearSync();
670
+ cache.stats.clear();
663
671
  }
664
672
  }
665
673
  rmSync;
@@ -2,7 +2,7 @@ import { EventEmitter } from 'eventemitter3';
2
2
  import { ErrnoError } from '../error.js';
3
3
  import { isStatsEqual } from '../stats.js';
4
4
  import { normalizePath } from '../utils.js';
5
- import { dirname, basename } from './path.js';
5
+ import { basename, dirname } from './path.js';
6
6
  import { statSync } from './sync.js';
7
7
  /**
8
8
  * Base class for file system watchers.
@@ -130,22 +130,22 @@ export function removeWatcher(path, watcher) {
130
130
  }
131
131
  }
132
132
  export function emitChange(eventType, filename) {
133
- let normalizedFilename = normalizePath(filename);
133
+ filename = normalizePath(filename);
134
134
  // Notify watchers on the specific file
135
- if (watchers.has(normalizedFilename)) {
136
- for (const watcher of watchers.get(normalizedFilename)) {
135
+ if (watchers.has(filename)) {
136
+ for (const watcher of watchers.get(filename)) {
137
137
  watcher.emit('change', eventType, basename(filename));
138
138
  }
139
139
  }
140
140
  // Notify watchers on parent directories if they are watching recursively
141
- let parent = dirname(normalizedFilename);
142
- while (parent !== normalizedFilename && parent !== '/') {
141
+ let parent = filename, normalizedFilename;
142
+ while (parent !== normalizedFilename) {
143
+ normalizedFilename = parent;
144
+ parent = dirname(parent);
143
145
  if (watchers.has(parent)) {
144
146
  for (const watcher of watchers.get(parent)) {
145
- watcher.emit('change', eventType, basename(filename));
147
+ watcher.emit('change', eventType, filename.slice(parent.length + (parent == '/' ? 0 : 1)));
146
148
  }
147
149
  }
148
- normalizedFilename = parent;
149
- parent = dirname(parent);
150
150
  }
151
151
  }
package/dist/file.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { FileReadResult } from 'node:fs/promises';
2
2
  import type { FileSystem } from './filesystem.js';
3
3
  import './polyfills.js';
4
- import { Stats, type FileType } from './stats.js';
4
+ import { Stats } from './stats.js';
5
5
  /**
6
6
  Typescript does not include a type declaration for resizable array buffers.
7
7
  It has been standardized into ECMAScript though
@@ -123,16 +123,6 @@ export declare abstract class File<FS extends FileSystem = FileSystem> {
123
123
  * Change the file timestamps of the file.
124
124
  */
125
125
  abstract utimesSync(atime: Date, mtime: Date): void;
126
- /**
127
- * Set the file type
128
- * @internal
129
- */
130
- abstract _setType(type: FileType): Promise<void>;
131
- /**
132
- * Set the file type
133
- * @internal
134
- */
135
- abstract _setTypeSync(type: FileType): void;
136
126
  }
137
127
  /**
138
128
  * An implementation of `File` that operates completely in-memory.
@@ -243,8 +233,6 @@ export declare class PreloadFile<FS extends FileSystem> extends File<FS> {
243
233
  chownSync(uid: number, gid: number): void;
244
234
  utimes(atime: Date, mtime: Date): Promise<void>;
245
235
  utimesSync(atime: Date, mtime: Date): void;
246
- _setType(type: FileType): Promise<void>;
247
- _setTypeSync(type: FileType): void;
248
236
  }
249
237
  /**
250
238
  * For the file systems which do not sync to anything.