New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@zenfs/core

Package Overview
Dependencies
Maintainers
0
Versions
182
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zenfs/core - npm Package Compare versions

Comparing version

to
1.3.5

8

dist/config.d.ts
import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
import type { AbsolutePath } from './emulation/path.js';
/**

@@ -12,4 +11,7 @@ * Configuration for a specific mount point

export declare function resolveMountConfig<T extends Backend>(configuration: MountConfiguration<T>, _depth?: number): Promise<FilesystemOf<T>>;
/**
* An object mapping mount points to backends
*/
export interface ConfigMounts {
[K: AbsolutePath]: Backend;
[K: string]: Backend;
}

@@ -24,3 +26,3 @@ /**

mounts: {
[K in keyof T & AbsolutePath]: MountConfiguration<T[K]>;
[K in keyof T]: MountConfiguration<T[K]>;
};

@@ -27,0 +29,0 @@ /**

@@ -99,6 +99,4 @@ import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';

let unmountRoot = false;
for (const [point, mountConfig] of Object.entries(configuration.mounts)) {
if (!point.startsWith('/')) {
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
}
for (const [_point, mountConfig] of Object.entries(configuration.mounts)) {
const point = _point.startsWith('/') ? _point : '/' + _point;
if (isBackendConfig(mountConfig)) {

@@ -105,0 +103,0 @@ mountConfig.disableAsyncCache ?? (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);

@@ -11,21 +11,25 @@ import type { Stats } from '../stats.js';

/**
* Whether the data exists in the cache
*/
has(path: string): boolean;
/**
* Gets data from the cache, if is exists and the cache is enabled.
*/
getSync(path: string): T | undefined;
get(path: string): T | undefined;
/**
* Adds data if the cache is enabled
*/
setSync(path: string, value: T): void;
set(path: string, value: T): void;
/**
* Clears the cache if it is enabled
* Whether the data exists in the cache
*/
clearSync(): void;
hasAsync(path: string): boolean;
/**
* Gets data from the cache, if it exists and the cache is enabled.
*/
get(path: string): Promise<T> | undefined;
getAsync(path: string): Promise<T> | undefined;
/**
* Adds data if the cache is enabled
*/
set(path: string, value: Promise<T>): void;
setAsync(path: string, value: Promise<T>): void;
/**

@@ -32,0 +36,0 @@ * Clears the cache if it is enabled

@@ -13,5 +13,11 @@ /* Experimental caching */

/**
* Whether the data exists in the cache
*/
has(path) {
return this.isEnabled && this.sync.has(path);
}
/**
* Gets data from the cache, if is exists and the cache is enabled.
*/
getSync(path) {
get(path) {
if (!this.isEnabled)

@@ -24,3 +30,3 @@ return;

*/
setSync(path, value) {
set(path, value) {
if (!this.isEnabled)

@@ -32,8 +38,6 @@ return;

/**
* Clears the cache if it is enabled
* Whether the data exists in the cache
*/
clearSync() {
if (!this.isEnabled)
return;
this.sync.clear();
hasAsync(path) {
return this.isEnabled && this.async.has(path);
}

@@ -43,3 +47,3 @@ /**

*/
get(path) {
getAsync(path) {
if (!this.isEnabled)

@@ -52,3 +56,3 @@ return;

*/
set(path, value) {
setAsync(path, value) {
if (!this.isEnabled)

@@ -65,2 +69,3 @@ return;

return;
this.sync.clear();
this.async.clear();

@@ -67,0 +72,0 @@ }

@@ -59,3 +59,3 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { dirname, join, parse, resolve } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';

@@ -364,2 +364,3 @@ import { ReadStream, WriteStream } from './streams.js';

emitChange('rename', oldPath.toString());
emitChange('change', newPath.toString());
return;

@@ -440,3 +441,3 @@ }

try {
if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
if (config.checkAccess && !(await (cache.stats.getAsync(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -589,3 +590,3 @@ }

try {
const stats = await (cache.stats.get(path) || fs.stat(resolved));
const stats = await (cache.stats.getAsync(path) || fs.stat(resolved));
if (!stats) {

@@ -649,4 +650,4 @@ throw ErrnoError.With('ENOENT', path, 'rmdir');

const { fs, path: resolved } = resolveMount(path);
const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
cache.stats.set(path, _stats);
const _stats = cache.stats.getAsync(path) || fs.stat(resolved).catch(handleError);
cache.stats.setAsync(path, _stats);
const stats = await _stats;

@@ -667,4 +668,4 @@ if (!stats) {

if (options?.recursive || options?.withFileTypes) {
const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
cache.stats.set(join(path, entry), _entryStats);
const _entryStats = cache.stats.getAsync(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
cache.stats.setAsync(join(path, entry), _entryStats);
entryStats = await _entryStats;

@@ -889,13 +890,19 @@ }

path = normalizePath(path);
if (cache.paths.hasAsync(path))
return cache.paths.getAsync(path);
const { base, dir } = parse(path);
const lpath = join(dir == '/' ? '/' : await (cache.paths.get(dir) || realpath(dir)), base);
const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
const realDir = dir == '/' ? '/' : await (cache.paths.getAsync(dir) || realpath(dir));
const lpath = join(realDir, base);
const { fs, path: resolvedPath } = resolveMount(lpath);
try {
const _stats = cache.stats.get(lpath) || fs.stat(resolvedPath);
cache.stats.set(lpath, _stats);
const _stats = cache.stats.getAsync(lpath) || fs.stat(resolvedPath);
cache.stats.setAsync(lpath, _stats);
if (!(await _stats).isSymbolicLink()) {
cache.paths.set(path, lpath);
return lpath;
}
const target = mountPoint + (await readlink(lpath));
return await (cache.paths.get(target) || realpath(target));
const target = resolve(realDir, await readlink(lpath));
const real = cache.paths.getAsync(target) || realpath(target);
cache.paths.setAsync(path, real);
return await real;
}

@@ -955,3 +962,3 @@ catch (e) {

path = normalizePath(path);
const stats = await (cache.stats.get(path) ||
const stats = await (cache.stats.getAsync(path) ||
stat(path).catch((error) => {

@@ -965,3 +972,3 @@ if (error.code == 'ENOENT' && options?.force)

}
cache.stats.setSync(path, stats);
cache.stats.set(path, stats);
switch (stats.mode & constants.S_IFMT) {

@@ -978,6 +985,6 @@ case constants.S_IFDIR:

case constants.S_IFLNK:
case constants.S_IFBLK:
case constants.S_IFCHR:
await unlink(path);
break;
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:

@@ -984,0 +991,0 @@ case constants.S_IFSOCK:

@@ -7,2 +7,3 @@ // Utilities and shared data

import { size_max } from './constants.js';
import { paths as pathCache } from './cache.js';
// descriptors

@@ -41,2 +42,3 @@ export const fdMap = new Map();

mounts.set(mountPoint, fs);
pathCache.clear();
}

@@ -55,2 +57,3 @@ /**

mounts.delete(mountPoint);
pathCache.clear();
}

@@ -57,0 +60,0 @@ /**

@@ -57,3 +57,3 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { dirname, join, parse, resolve } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';

@@ -73,2 +73,3 @@ import { emitChange } from './watchers.js';

emitChange('rename', oldPath.toString());
emitChange('change', newPath.toString());
return;

@@ -152,3 +153,3 @@ }

try {
if (config.checkAccess && !(cache.stats.getSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
if (config.checkAccess && !(cache.stats.get(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -399,3 +400,3 @@ }

try {
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
const stats = cache.stats.get(path) || fs.statSync(resolved);
if (!stats.isDirectory()) {

@@ -453,4 +454,4 @@ throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');

try {
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
cache.stats.setSync(path, stats);
const stats = cache.stats.get(path) || fs.statSync(resolved);
cache.stats.set(path, stats);
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {

@@ -470,4 +471,4 @@ throw ErrnoError.With('EACCES', resolved, 'readdir');

for (const entry of entries) {
const entryStat = cache.stats.getSync(join(path, entry)) || fs.statSync(join(resolved, entry));
cache.stats.setSync(join(path, entry), entryStat);
const entryStat = cache.stats.get(join(path, entry)) || fs.statSync(join(resolved, entry));
cache.stats.set(join(path, entry), entryStat);
if (options?.withFileTypes) {

@@ -498,3 +499,3 @@ values.push(new Dirent(entry, entryStat));

if (!options?._isIndirect) {
cache.stats.clearSync();
cache.stats.clear();
}

@@ -602,12 +603,19 @@ return values;

path = normalizePath(path);
if (cache.paths.has(path))
return cache.paths.get(path);
const { base, dir } = parse(path);
const lpath = join(dir == '/' ? '/' : cache.paths.getSync(dir) || realpathSync(dir), base);
const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
const realDir = dir == '/' ? '/' : cache.paths.get(dir) || realpathSync(dir);
const lpath = join(realDir, base);
const { fs, path: resolvedPath } = resolveMount(lpath);
try {
const stats = fs.statSync(resolvedPath);
const stats = cache.stats.get(lpath) || fs.statSync(resolvedPath);
cache.stats.set(lpath, stats);
if (!stats.isSymbolicLink()) {
cache.paths.set(path, lpath);
return lpath;
}
const target = mountPoint + readlinkSync(lpath, options).toString();
return cache.paths.getSync(target) || realpathSync(target);
const target = resolve(realDir, readlinkSync(lpath, options).toString());
const real = cache.paths.get(target) || realpathSync(target);
cache.paths.set(path, real);
return real;
}

@@ -638,3 +646,3 @@ catch (e) {

try {
stats = cache.stats.getSync(path) || statSync(path);
stats = cache.stats.get(path) || statSync(path);
}

@@ -648,3 +656,3 @@ catch (error) {

}
cache.stats.setSync(path, stats);
cache.stats.set(path, stats);
switch (stats.mode & constants.S_IFMT) {

@@ -661,14 +669,14 @@ case constants.S_IFDIR:

case constants.S_IFLNK:
case constants.S_IFBLK:
case constants.S_IFCHR:
unlinkSync(path);
break;
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:
cache.stats.clearSync();
cache.stats.clear();
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');
}
if (!options?._isIndirect) {
cache.stats.clearSync();
cache.stats.clear();
}

@@ -675,0 +683,0 @@ }

{
"name": "@zenfs/core",
"version": "1.3.4",
"version": "1.3.5",
"description": "A filesystem, anywhere",

@@ -77,6 +77,8 @@ "funding": {

"eventemitter3": "^5.0.1",
"minimatch": "^9.0.3",
"readable-stream": "^4.5.2",
"utilium": "^1.0.0"
},
"optionalDependencies": {
"minimatch": "^9.0.3"
},
"devDependencies": {

@@ -83,0 +85,0 @@ "@eslint/js": "^9.8.0",

@@ -67,2 +67,4 @@ # ZenFS

Note that while you aren't required to use absolute paths for the keys of `mounts`, it is a good practice to do so.
> [!TIP]

@@ -69,0 +71,0 @@ > When configuring a mount point, you can pass in

#!/usr/bin/env node
import { readdirSync, statSync, writeFileSync } from 'node:fs';
import { minimatch } from 'minimatch';
import { join, relative, resolve } from 'node:path/posix';
import _path from 'node:path/posix';
import { parseArgs } from 'node:util';

@@ -40,2 +39,20 @@

let matchesGlob = _path.matchesGlob;
if (matchesGlob && options.verbose) {
console.debug('[debug] path.matchesGlob is available.');
}
if (!matchesGlob) {
console.warn('Warning: path.matchesGlob is not available, falling back to minimatch. (Node 20.17.0+ or 22.5.0+ needed)');
try {
const { minimatch } = await import('minimatch');
matchesGlob = minimatch;
} catch {
console.error('Fatal error: Failed to fall back to minimatch (is it installed?)');
process.exit(1);
}
}
function fixSlash(path) {

@@ -75,3 +92,3 @@ return path.replaceAll('\\', '/');

try {
if (options.ignore.some(pattern => minimatch(path, pattern))) {
if (options.ignore.some(pattern => matchesGlob(path, pattern))) {
if (!options.quiet) console.log(`${color('yellow', 'skip')} ${path}`);

@@ -84,3 +101,3 @@ return;

if (stats.isFile()) {
entries.set('/' + relative(resolvedRoot, path), stats);
entries.set('/' + _path.relative(resolvedRoot, path), stats);
if (options.verbose) {

@@ -93,5 +110,5 @@ console.log(`${color('green', 'file')} ${path}`);

for (const file of readdirSync(path)) {
computeEntries(join(path, file));
computeEntries(_path.join(path, file));
}
entries.set('/' + relative(resolvedRoot, path), stats);
entries.set('/' + _path.relative(resolvedRoot, path), stats);
if (options.verbose) {

@@ -109,3 +126,3 @@ console.log(`${color('bright_green', ' dir')} ${path}`);

if (!options.quiet) {
console.log('Generated listing for ' + fixSlash(resolve(root)));
console.log('Generated listing for ' + fixSlash(_path.resolve(root)));
}

@@ -112,0 +129,0 @@

@@ -8,3 +8,2 @@ import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';

import * as fs from './emulation/index.js';
import type { AbsolutePath } from './emulation/path.js';
import { Errno, ErrnoError } from './error.js';

@@ -72,4 +71,7 @@ import { FileSystem } from './filesystem.js';

/**
* An object mapping mount points to backends
*/
export interface ConfigMounts {
[K: AbsolutePath]: Backend;
[K: string]: Backend;
}

@@ -84,3 +86,3 @@

*/
mounts: { [K in keyof T & AbsolutePath]: MountConfiguration<T[K]> };
mounts: { [K in keyof T]: MountConfiguration<T[K]> };

@@ -206,6 +208,4 @@ /**

for (const [point, mountConfig] of Object.entries(configuration.mounts)) {
if (!point.startsWith('/')) {
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
}
for (const [_point, mountConfig] of Object.entries(configuration.mounts)) {
const point = _point.startsWith('/') ? _point : '/' + _point;

@@ -212,0 +212,0 @@ if (isBackendConfig(mountConfig)) {

@@ -17,5 +17,12 @@ /* Experimental caching */

/**
* Whether the data exists in the cache
*/
has(path: string): boolean {
return this.isEnabled && this.sync.has(path);
}
/**
* Gets data from the cache, if is exists and the cache is enabled.
*/
getSync(path: string): T | undefined {
get(path: string): T | undefined {
if (!this.isEnabled) return;

@@ -29,3 +36,3 @@

*/
setSync(path: string, value: T): void {
set(path: string, value: T): void {
if (!this.isEnabled) return;

@@ -38,8 +45,6 @@

/**
* Clears the cache if it is enabled
* Whether the data exists in the cache
*/
clearSync(): void {
if (!this.isEnabled) return;
this.sync.clear();
hasAsync(path: string): boolean {
return this.isEnabled && this.async.has(path);
}

@@ -50,3 +55,3 @@

*/
get(path: string): Promise<T> | undefined {
getAsync(path: string): Promise<T> | undefined {
if (!this.isEnabled) return;

@@ -60,3 +65,3 @@

*/
set(path: string, value: Promise<T>): void {
setAsync(path: string, value: Promise<T>): void {
if (!this.isEnabled) return;

@@ -73,3 +78,3 @@

if (!this.isEnabled) return;
this.sync.clear();
this.async.clear();

@@ -76,0 +81,0 @@ }

@@ -19,3 +19,3 @@ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */

import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { dirname, join, parse, resolve } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js';

@@ -399,2 +399,3 @@ import { ReadStream, WriteStream } from './streams.js';

emitChange('rename', oldPath.toString());
emitChange('change', newPath.toString());
return;

@@ -476,3 +477,3 @@ }

try {
if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
if (config.checkAccess && !(await (cache.stats.getAsync(path) || fs.stat(resolved))).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -629,3 +630,3 @@ }

try {
const stats = await (cache.stats.get(path) || fs.stat(resolved));
const stats = await (cache.stats.getAsync(path) || fs.stat(resolved));
if (!stats) {

@@ -725,4 +726,4 @@ throw ErrnoError.With('ENOENT', path, 'rmdir');

const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError);
cache.stats.set(path, _stats);
const _stats = cache.stats.getAsync(path) || fs.stat(resolved).catch(handleError);
cache.stats.setAsync(path, _stats);
const stats = await _stats;

@@ -748,4 +749,4 @@

if (options?.recursive || options?.withFileTypes) {
const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
cache.stats.set(join(path, entry), _entryStats);
const _entryStats = cache.stats.getAsync(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError);
cache.stats.setAsync(join(path, entry), _entryStats);
entryStats = await _entryStats;

@@ -900,16 +901,21 @@ }

path = normalizePath(path);
if (cache.paths.hasAsync(path)) return cache.paths.getAsync(path)!;
const { base, dir } = parse(path);
const lpath = join(dir == '/' ? '/' : await (cache.paths.get(dir) || realpath(dir)), base);
const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
const realDir = dir == '/' ? '/' : await (cache.paths.getAsync(dir) || realpath(dir));
const lpath = join(realDir, base);
const { fs, path: resolvedPath } = resolveMount(lpath);
try {
const _stats = cache.stats.get(lpath) || fs.stat(resolvedPath);
cache.stats.set(lpath, _stats);
const _stats = cache.stats.getAsync(lpath) || fs.stat(resolvedPath);
cache.stats.setAsync(lpath, _stats);
if (!(await _stats).isSymbolicLink()) {
cache.paths.set(path, lpath);
return lpath;
}
const target = mountPoint + (await readlink(lpath));
const target = resolve(realDir, await readlink(lpath));
return await (cache.paths.get(target) || realpath(target));
const real = cache.paths.getAsync(target) || realpath(target);
cache.paths.setAsync(path, real);
return await real;
} catch (e) {

@@ -978,3 +984,3 @@ if ((e as ErrnoError).code == 'ENOENT') {

const stats = await (cache.stats.get(path) ||
const stats = await (cache.stats.getAsync(path) ||
stat(path).catch((error: ErrnoError) => {

@@ -989,3 +995,3 @@ if (error.code == 'ENOENT' && options?.force) return undefined;

cache.stats.setSync(path, stats);
cache.stats.set(path, stats);

@@ -1004,6 +1010,6 @@ switch (stats.mode & constants.S_IFMT) {

case constants.S_IFLNK:
case constants.S_IFBLK:
case constants.S_IFCHR:
await unlink(path);
break;
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:

@@ -1010,0 +1016,0 @@ case constants.S_IFSOCK:

@@ -11,2 +11,3 @@ // Utilities and shared data

import { size_max } from './constants.js';
import { paths as pathCache } from './cache.js';

@@ -51,2 +52,3 @@ // descriptors

mounts.set(mountPoint, fs);
pathCache.clear();
}

@@ -66,2 +68,3 @@

mounts.delete(mountPoint);
pathCache.clear();
}

@@ -68,0 +71,0 @@

@@ -13,3 +13,3 @@ import { Buffer } from 'buffer';

import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { dirname, join, parse, resolve } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js';

@@ -30,2 +30,3 @@ import { emitChange } from './watchers.js';

emitChange('rename', oldPath.toString());
emitChange('change', newPath.toString());
return;

@@ -111,3 +112,3 @@ }

try {
if (config.checkAccess && !(cache.stats.getSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
if (config.checkAccess && !(cache.stats.get(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');

@@ -392,3 +393,3 @@ }

try {
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
const stats = cache.stats.get(path) || fs.statSync(resolved);
if (!stats.isDirectory()) {

@@ -466,4 +467,4 @@ throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');

try {
const stats = cache.stats.getSync(path) || fs.statSync(resolved);
cache.stats.setSync(path, stats);
const stats = cache.stats.get(path) || fs.statSync(resolved);
cache.stats.set(path, stats);
if (config.checkAccess && !stats.hasAccess(constants.R_OK)) {

@@ -483,4 +484,4 @@ throw ErrnoError.With('EACCES', resolved, 'readdir');

for (const entry of entries) {
const entryStat = cache.stats.getSync(join(path, entry)) || fs.statSync(join(resolved, entry));
cache.stats.setSync(join(path, entry), entryStat);
const entryStat = cache.stats.get(join(path, entry)) || fs.statSync(join(resolved, entry));
cache.stats.set(join(path, entry), entryStat);

@@ -509,3 +510,3 @@ if (options?.withFileTypes) {

if (!options?._isIndirect) {
cache.stats.clearSync();
cache.stats.clear();
}

@@ -631,14 +632,20 @@ return values as string[] | Dirent[] | Buffer[];

path = normalizePath(path);
if (cache.paths.has(path)) return cache.paths.get(path)!;
const { base, dir } = parse(path);
const lpath = join(dir == '/' ? '/' : cache.paths.getSync(dir) || realpathSync(dir), base);
const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
const realDir = dir == '/' ? '/' : cache.paths.get(dir) || realpathSync(dir);
const lpath = join(realDir, base);
const { fs, path: resolvedPath } = resolveMount(lpath);
try {
const stats = fs.statSync(resolvedPath);
const stats = cache.stats.get(lpath) || fs.statSync(resolvedPath);
cache.stats.set(lpath, stats);
if (!stats.isSymbolicLink()) {
cache.paths.set(path, lpath);
return lpath;
}
const target = mountPoint + readlinkSync(lpath, options).toString();
return cache.paths.getSync(target) || realpathSync(target);
const target = resolve(realDir, readlinkSync(lpath, options).toString());
const real = cache.paths.get(target) || realpathSync(target);
cache.paths.set(path, real);
return real;
} catch (e) {

@@ -670,3 +677,3 @@ if ((e as ErrnoError).code == 'ENOENT') {

try {
stats = cache.stats.getSync(path) || statSync(path);
stats = cache.stats.get(path) || statSync(path);
} catch (error) {

@@ -680,3 +687,3 @@ if ((error as ErrnoError).code != 'ENOENT' || !options?.force) throw error;

cache.stats.setSync(path, stats);
cache.stats.set(path, stats);

@@ -695,10 +702,10 @@ switch (stats.mode & constants.S_IFMT) {

case constants.S_IFLNK:
case constants.S_IFBLK:
case constants.S_IFCHR:
unlinkSync(path);
break;
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:
cache.stats.clearSync();
cache.stats.clear();
throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');

@@ -708,3 +715,3 @@ }

if (!options?._isIndirect) {
cache.stats.clearSync();
cache.stats.clear();
}

@@ -711,0 +718,0 @@ }

@@ -26,2 +26,6 @@ import assert from 'node:assert';

test('read target contents', async () => {
assert.equal(await fs.promises.readFile(target, 'utf-8'), await fs.promises.readFile(symlink, 'utf-8'));
});
test('unlink', async () => {

@@ -28,0 +32,0 @@ await fs.promises.unlink(symlink);

@@ -74,10 +74,20 @@ import assert from 'node:assert';

test('fs.watch should detect file renames', async () => {
const oldFile = `${testDir}/oldFile.txt`;
const newFile = `${testDir}/newFile.txt`;
const oldFileName = `oldFile.txt`;
const newFileName = `newFile.txt`;
const oldFile = `${testDir}/${oldFileName}`;
const newFile = `${testDir}/${newFileName}`;
await fs.promises.writeFile(oldFile, 'Some content');
const oldFileResolver = Promise.withResolvers<void>();
const newFileResolver = Promise.withResolvers<void>();
const fileResolvers: Record<string, { resolver: PromiseWithResolvers<void>; eventType: string }> = {
[oldFileName]: { resolver: oldFileResolver, eventType: 'rename' },
[newFileName]: { resolver: newFileResolver, eventType: 'change' },
};
using watcher = fs.watch(testDir, (eventType, filename) => {
assert.strictEqual(eventType, 'rename');
assert.strictEqual(filename, 'oldFile.txt');
const resolver = fileResolvers[filename];
assert.notEqual(resolver, undefined); // should have a resolver so file is expected
assert.strictEqual(eventType, resolver.eventType);
resolver.resolver.resolve();
});

@@ -87,2 +97,3 @@

await fs.promises.rename(oldFile, newFile);
await Promise.all([newFileResolver.promise, oldFileResolver.promise]);
});

@@ -89,0 +100,0 @@