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
169
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 0.17.1 to 0.18.0

dist/credentials.d.ts

5

dist/backends/backend.d.ts

@@ -87,6 +87,5 @@ import type { RequiredKeys } from 'utilium';

*
* Individual options can recursively contain BackendConfig objects for
* option values that require file systems.
* Individual options can recursively contain BackendConfiguration objects for values that require file systems.
*
* The option object for each file system corresponds to that file system's option object passed to its `Create()` method.
* The configuration for each file system corresponds to that file system's option object passed to its `create()` method.
*/

@@ -93,0 +92,0 @@ export type BackendConfiguration<T extends Backend> = OptionsOf<T> & Partial<SharedConfig> & {

4

dist/backends/fetch.js

@@ -83,6 +83,6 @@ import { Errno, ErrnoError } from '../error.js';

if (!stats) {
throw ErrnoError.With('ENOENT', path, 'preloadFile');
throw ErrnoError.With('ENOENT', path, 'preload');
}
if (!stats.isFile()) {
throw ErrnoError.With('EISDIR', path, 'preloadFile');
throw ErrnoError.With('EISDIR', path, 'preload');
}

@@ -89,0 +89,0 @@ stats.size = buffer.length;

@@ -1,2 +0,1 @@

import type { Cred } from '../cred.js';
import { NoSyncFile } from '../file.js';

@@ -48,14 +47,14 @@ import { FileSystem } from '../filesystem.js';

metadata(): import("../filesystem.js").FileSystemMetadata;
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<import("../file.js").File>;
createFileSync(path: string, flag: string, mode: number, cred: Cred): import("../file.js").File;
unlink(path: string, cred: Cred): Promise<void>;
unlinkSync(path: string, cred: Cred): void;
rmdir(path: string, cred: Cred): Promise<void>;
rmdirSync(path: string, cred: Cred): void;
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
mkdirSync(path: string, mode: number, cred: Cred): void;
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
rename(oldPath: string, newPath: string): Promise<void>;
renameSync(oldPath: string, newPath: string): void;
createFile(path: string, flag: string, mode: number): Promise<import("../file.js").File>;
createFileSync(path: string, flag: string, mode: number): import("../file.js").File;
unlink(path: string): Promise<void>;
unlinkSync(path: string): void;
rmdir(path: string): Promise<void>;
rmdirSync(path: string): void;
mkdir(path: string, mode: number): Promise<void>;
mkdirSync(path: string, mode: number): void;
link(srcpath: string, dstpath: string): Promise<void>;
linkSync(srcpath: string, dstpath: string): void;
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;

@@ -74,4 +73,4 @@ syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;

statSync(path: string): Stats;
openFile(path: string, flag: string, cred: Cred): Promise<NoSyncFile<this>>;
openFileSync(path: string, flag: string, cred: Cred): NoSyncFile<this>;
openFile(path: string, flag: string): Promise<NoSyncFile<this>>;
openFileSync(path: string, flag: string): NoSyncFile<this>;
readdir(path: string): Promise<string[]>;

@@ -78,0 +77,0 @@ readdirSync(path: string): string[];

@@ -5,3 +5,3 @@ /* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */

import { Errno, ErrnoError } from '../error.js';
import { NoSyncFile, flagToMode, isWriteable } from '../file.js';
import { NoSyncFile, isWriteable } from '../file.js';
import { FileSystem } from '../filesystem.js';

@@ -122,3 +122,3 @@ import { Readonly } from '../mixins/readonly.js';

}
async openFile(path, flag, cred) {
async openFile(path, flag) {
if (isWriteable(flag)) {

@@ -133,8 +133,5 @@ // You can't write to files on this file system.

}
if (!stats.hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : await this.getData(path, stats));
}
openFileSync(path, flag, cred) {
openFileSync(path, flag) {
if (isWriteable(flag)) {

@@ -149,5 +146,2 @@ // You can't write to files on this file system.

}
if (!stats.hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));

@@ -154,0 +148,0 @@ }

@@ -0,6 +1,5 @@

import type { File } from '../file.js';
import type { FileSystemMetadata } from '../filesystem.js';
import { FileSystem } from '../filesystem.js';
import type { File } from '../file.js';
import { Stats } from '../stats.js';
import type { Cred } from '../cred.js';
/**

@@ -48,21 +47,21 @@ * Configuration options for OverlayFS instances.

getDeletionLog(): string;
restoreDeletionLog(log: string, cred: Cred): Promise<void>;
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
stat(path: string, cred: Cred): Promise<Stats>;
statSync(path: string, cred: Cred): Stats;
openFile(path: string, flag: string, cred: Cred): Promise<File>;
openFileSync(path: string, flag: string, cred: Cred): File;
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
unlink(path: string, cred: Cred): Promise<void>;
unlinkSync(path: string, cred: Cred): void;
rmdir(path: string, cred: Cred): Promise<void>;
rmdirSync(path: string, cred: Cred): void;
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
mkdirSync(path: string, mode: number, cred: Cred): void;
readdir(path: string, cred: Cred): Promise<string[]>;
readdirSync(path: string, cred: Cred): string[];
restoreDeletionLog(log: string): Promise<void>;
rename(oldPath: string, newPath: string): Promise<void>;
renameSync(oldPath: string, newPath: string): void;
stat(path: string): Promise<Stats>;
statSync(path: string): Stats;
openFile(path: string, flag: string): Promise<File>;
openFileSync(path: string, flag: string): File;
createFile(path: string, flag: string, mode: number): Promise<File>;
createFileSync(path: string, flag: string, mode: number): File;
link(srcpath: string, dstpath: string): Promise<void>;
linkSync(srcpath: string, dstpath: string): void;
unlink(path: string): Promise<void>;
unlinkSync(path: string): void;
rmdir(path: string): Promise<void>;
rmdirSync(path: string): void;
mkdir(path: string, mode: number): Promise<void>;
mkdirSync(path: string, mode: number): void;
readdir(path: string): Promise<string[]>;
readdirSync(path: string): string[];
private deletePath;

@@ -94,3 +93,3 @@ private updateLog;

declare const OverlayFS_base: import("../mixins/shared.js").Mixin<typeof UnmutexedOverlayFS, {
lock(path: string): Promise<import("../mixins/mutexed.js").MutexLock>;
lock(path: string, syscall: string): Promise<import("../mixins/mutexed.js").MutexLock>;
lockSync(path: string): import("../mixins/mutexed.js").MutexLock;

@@ -97,0 +96,0 @@ isLocked(path: string): boolean;

@@ -0,8 +1,7 @@

import { dirname } from '../emulation/path.js';
import { Errno, ErrnoError } from '../error.js';
import { PreloadFile, parseFlag } from '../file.js';
import { FileSystem } from '../filesystem.js';
import { ErrnoError, Errno } from '../error.js';
import { PreloadFile, parseFlag } from '../file.js';
import { Mutexed } from '../mixins/mutexed.js';
import { Stats } from '../stats.js';
import { Mutexed } from '../mixins/mutexed.js';
import { dirname } from '../emulation/path.js';
import { rootCred } from '../cred.js';
import { decode, encode } from '../utils.js';

@@ -51,6 +50,5 @@ /**

async sync(path, data, stats) {
const cred = stats.cred(0, 0);
await this.createParentDirectories(path, cred);
if (!(await this.writable.exists(path, cred))) {
await this.writable.createFile(path, 'w', 0o644, cred);
await this.createParentDirectories(path);
if (!(await this.writable.exists(path))) {
await this.writable.createFile(path, 'w', 0o644);
}

@@ -60,4 +58,3 @@ await this.writable.sync(path, data, stats);

syncSync(path, data, stats) {
const cred = stats.cred(0, 0);
this.createParentDirectoriesSync(path, cred);
this.createParentDirectoriesSync(path);
this.writable.syncSync(path, data, stats);

@@ -75,3 +72,3 @@ }

try {
const file = await this.writable.openFile(deletionLogPath, parseFlag('r'), rootCred);
const file = await this.writable.openFile(deletionLogPath, parseFlag('r'));
const { size } = await file.stat();

@@ -92,8 +89,8 @@ const { buffer } = await file.read(new Uint8Array(size));

}
async restoreDeletionLog(log, cred) {
async restoreDeletionLog(log) {
this._deleteLog = log;
this._reparseDeletionLog();
await this.updateLog('', cred);
await this.updateLog('');
}
async rename(oldPath, newPath, cred) {
async rename(oldPath, newPath) {
this.checkInitialized();

@@ -103,3 +100,3 @@ this.checkPath(oldPath);

try {
await this.writable.rename(oldPath, newPath, cred);
await this.writable.rename(oldPath, newPath);
}

@@ -112,3 +109,3 @@ catch (e) {

}
renameSync(oldPath, newPath, cred) {
renameSync(oldPath, newPath) {
this.checkInitialized();

@@ -118,3 +115,3 @@ this.checkPath(oldPath);

try {
this.writable.renameSync(oldPath, newPath, cred);
this.writable.renameSync(oldPath, newPath);
}

@@ -127,6 +124,6 @@ catch (e) {

}
async stat(path, cred) {
async stat(path) {
this.checkInitialized();
try {
return await this.writable.stat(path, cred);
return await this.writable.stat(path);
}

@@ -137,3 +134,3 @@ catch (e) {

}
const oldStat = new Stats(await this.readable.stat(path, cred));
const oldStat = new Stats(await this.readable.stat(path));
// Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type

@@ -144,6 +141,6 @@ oldStat.mode |= 0o222;

}
statSync(path, cred) {
statSync(path) {
this.checkInitialized();
try {
return this.writable.statSync(path, cred);
return this.writable.statSync(path);
}

@@ -154,3 +151,3 @@ catch (e) {

}
const oldStat = new Stats(this.readable.statSync(path, cred));
const oldStat = new Stats(this.readable.statSync(path));
// Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type.

@@ -161,8 +158,8 @@ oldStat.mode |= 0o222;

}
async openFile(path, flag, cred) {
if (await this.writable.exists(path, cred)) {
return this.writable.openFile(path, flag, cred);
async openFile(path, flag) {
if (await this.writable.exists(path)) {
return this.writable.openFile(path, flag);
}
// Create an OverlayFile.
const file = await this.readable.openFile(path, parseFlag('r'), cred);
const file = await this.readable.openFile(path, parseFlag('r'));
const stats = new Stats(await file.stat());

@@ -172,8 +169,8 @@ const { buffer } = await file.read(new Uint8Array(stats.size));

}
openFileSync(path, flag, cred) {
if (this.writable.existsSync(path, cred)) {
return this.writable.openFileSync(path, flag, cred);
openFileSync(path, flag) {
if (this.writable.existsSync(path)) {
return this.writable.openFileSync(path, flag);
}
// Create an OverlayFile.
const file = this.readable.openFileSync(path, parseFlag('r'), cred);
const file = this.readable.openFileSync(path, parseFlag('r'));
const stats = new Stats(file.statSync());

@@ -184,105 +181,105 @@ const data = new Uint8Array(stats.size);

}
async createFile(path, flag, mode, cred) {
async createFile(path, flag, mode) {
this.checkInitialized();
await this.writable.createFile(path, flag, mode, cred);
return this.openFile(path, flag, cred);
await this.writable.createFile(path, flag, mode);
return this.openFile(path, flag);
}
createFileSync(path, flag, mode, cred) {
createFileSync(path, flag, mode) {
this.checkInitialized();
this.writable.createFileSync(path, flag, mode, cred);
return this.openFileSync(path, flag, cred);
this.writable.createFileSync(path, flag, mode);
return this.openFileSync(path, flag);
}
async link(srcpath, dstpath, cred) {
async link(srcpath, dstpath) {
this.checkInitialized();
await this.writable.link(srcpath, dstpath, cred);
await this.writable.link(srcpath, dstpath);
}
linkSync(srcpath, dstpath, cred) {
linkSync(srcpath, dstpath) {
this.checkInitialized();
this.writable.linkSync(srcpath, dstpath, cred);
this.writable.linkSync(srcpath, dstpath);
}
async unlink(path, cred) {
async unlink(path) {
this.checkInitialized();
this.checkPath(path);
if (!(await this.exists(path, cred))) {
if (!(await this.exists(path))) {
throw ErrnoError.With('ENOENT', path, 'unlink');
}
if (await this.writable.exists(path, cred)) {
await this.writable.unlink(path, cred);
if (await this.writable.exists(path)) {
await this.writable.unlink(path);
}
// if it still exists add to the delete log
if (await this.exists(path, cred)) {
await this.deletePath(path, cred);
if (await this.exists(path)) {
await this.deletePath(path);
}
}
unlinkSync(path, cred) {
unlinkSync(path) {
this.checkInitialized();
this.checkPath(path);
if (!this.existsSync(path, cred)) {
if (!this.existsSync(path)) {
throw ErrnoError.With('ENOENT', path, 'unlink');
}
if (this.writable.existsSync(path, cred)) {
this.writable.unlinkSync(path, cred);
if (this.writable.existsSync(path)) {
this.writable.unlinkSync(path);
}
// if it still exists add to the delete log
if (this.existsSync(path, cred)) {
void this.deletePath(path, cred);
if (this.existsSync(path)) {
void this.deletePath(path);
}
}
async rmdir(path, cred) {
async rmdir(path) {
this.checkInitialized();
if (!(await this.exists(path, cred))) {
if (!(await this.exists(path))) {
throw ErrnoError.With('ENOENT', path, 'rmdir');
}
if (await this.writable.exists(path, cred)) {
await this.writable.rmdir(path, cred);
if (await this.writable.exists(path)) {
await this.writable.rmdir(path);
}
if (await this.exists(path, cred)) {
if (await this.exists(path)) {
// Check if directory is empty.
if ((await this.readdir(path, cred)).length > 0) {
if ((await this.readdir(path)).length > 0) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
}
else {
await this.deletePath(path, cred);
await this.deletePath(path);
}
}
}
rmdirSync(path, cred) {
rmdirSync(path) {
this.checkInitialized();
if (!this.existsSync(path, cred)) {
if (!this.existsSync(path)) {
throw ErrnoError.With('ENOENT', path, 'rmdir');
}
if (this.writable.existsSync(path, cred)) {
this.writable.rmdirSync(path, cred);
if (this.writable.existsSync(path)) {
this.writable.rmdirSync(path);
}
if (this.existsSync(path, cred)) {
if (this.existsSync(path)) {
// Check if directory is empty.
if (this.readdirSync(path, cred).length > 0) {
if (this.readdirSync(path).length > 0) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
}
else {
void this.deletePath(path, cred);
void this.deletePath(path);
}
}
}
async mkdir(path, mode, cred) {
async mkdir(path, mode) {
this.checkInitialized();
if (await this.exists(path, cred)) {
if (await this.exists(path)) {
throw ErrnoError.With('EEXIST', path, 'mkdir');
}
// The below will throw should any of the parent directories fail to exist on _writable.
await this.createParentDirectories(path, cred);
await this.writable.mkdir(path, mode, cred);
await this.createParentDirectories(path);
await this.writable.mkdir(path, mode);
}
mkdirSync(path, mode, cred) {
mkdirSync(path, mode) {
this.checkInitialized();
if (this.existsSync(path, cred)) {
if (this.existsSync(path)) {
throw ErrnoError.With('EEXIST', path, 'mkdir');
}
// The below will throw should any of the parent directories fail to exist on _writable.
this.createParentDirectoriesSync(path, cred);
this.writable.mkdirSync(path, mode, cred);
this.createParentDirectoriesSync(path);
this.writable.mkdirSync(path, mode);
}
async readdir(path, cred) {
async readdir(path) {
this.checkInitialized();
const dirStats = await this.stat(path, cred);
const dirStats = await this.stat(path);
if (!dirStats.isDirectory()) {

@@ -294,3 +291,3 @@ throw ErrnoError.With('ENOTDIR', path, 'readdir');

try {
contents.push(...(await this.writable.readdir(path, cred)));
contents.push(...(await this.writable.readdir(path)));
}

@@ -301,3 +298,3 @@ catch (e) {

try {
contents.push(...(await this.readable.readdir(path, cred)).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
contents.push(...(await this.readable.readdir(path)).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
}

@@ -314,5 +311,5 @@ catch (e) {

}
readdirSync(path, cred) {
readdirSync(path) {
this.checkInitialized();
const dirStats = this.statSync(path, cred);
const dirStats = this.statSync(path);
if (!dirStats.isDirectory()) {

@@ -324,3 +321,3 @@ throw ErrnoError.With('ENOTDIR', path, 'readdir');

try {
contents = contents.concat(this.writable.readdirSync(path, cred));
contents = contents.concat(this.writable.readdirSync(path));
}

@@ -331,3 +328,3 @@ catch (e) {

try {
contents = contents.concat(this.readable.readdirSync(path, cred).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
contents = contents.concat(this.readable.readdirSync(path).filter((fPath) => !this._deletedFiles.has(`${path}/${fPath}`)));
}

@@ -344,7 +341,7 @@ catch (e) {

}
async deletePath(path, cred) {
async deletePath(path) {
this._deletedFiles.add(path);
await this.updateLog(`d${path}\n`, cred);
await this.updateLog(`d${path}\n`);
}
async updateLog(addition, cred) {
async updateLog(addition) {
this._deleteLog += addition;

@@ -356,3 +353,3 @@ if (this._deleteLogUpdatePending) {

this._deleteLogUpdatePending = true;
const log = await this.writable.openFile(deletionLogPath, parseFlag('w'), cred);
const log = await this.writable.openFile(deletionLogPath, parseFlag('w'));
try {

@@ -362,3 +359,3 @@ await log.write(encode(this._deleteLog));

this._deleteLogUpdateNeeded = false;
await this.updateLog('', cred);
await this.updateLog('');
}

@@ -403,5 +400,5 @@ }

*/
createParentDirectoriesSync(path, cred) {
createParentDirectoriesSync(path) {
let parent = dirname(path), toCreate = [];
while (!this.writable.existsSync(parent, cred)) {
while (!this.writable.existsSync(parent)) {
toCreate.push(parent);

@@ -412,8 +409,8 @@ parent = dirname(parent);

for (const p of toCreate) {
this.writable.mkdirSync(p, this.statSync(p, cred).mode, cred);
this.writable.mkdirSync(p, this.statSync(p).mode);
}
}
async createParentDirectories(path, cred) {
async createParentDirectories(path) {
let parent = dirname(path), toCreate = [];
while (!(await this.writable.exists(parent, cred))) {
while (!(await this.writable.exists(parent))) {
toCreate.push(parent);

@@ -424,4 +421,4 @@ parent = dirname(parent);

for (const p of toCreate) {
const stats = await this.stat(p, cred);
await this.writable.mkdir(p, stats.mode, cred);
const stats = await this.stat(p);
await this.writable.mkdir(p, stats.mode);
}

@@ -434,18 +431,18 @@ }

*/
operateOnWritable(path, cred) {
if (!this.existsSync(path, cred)) {
operateOnWritable(path) {
if (!this.existsSync(path)) {
throw ErrnoError.With('ENOENT', path, 'operateOnWriteable');
}
if (!this.writable.existsSync(path, cred)) {
if (!this.writable.existsSync(path)) {
// File is on readable storage. Copy to writable storage before
// changing its mode.
this.copyToWritableSync(path, cred);
this.copyToWritableSync(path);
}
}
async operateOnWritableAsync(path, cred) {
if (!(await this.exists(path, cred))) {
async operateOnWritableAsync(path) {
if (!(await this.exists(path))) {
throw ErrnoError.With('ENOENT', path, 'operateOnWritable');
}
if (!(await this.writable.exists(path, cred))) {
return this.copyToWritable(path, cred);
if (!(await this.writable.exists(path))) {
return this.copyToWritable(path);
}

@@ -457,27 +454,27 @@ }

*/
copyToWritableSync(path, cred) {
const stats = this.statSync(path, cred);
copyToWritableSync(path) {
const stats = this.statSync(path);
if (stats.isDirectory()) {
this.writable.mkdirSync(path, stats.mode, cred);
this.writable.mkdirSync(path, stats.mode);
return;
}
const data = new Uint8Array(stats.size);
const readable = this.readable.openFileSync(path, parseFlag('r'), cred);
const readable = this.readable.openFileSync(path, parseFlag('r'));
readable.readSync(data);
readable.closeSync();
const writable = this.writable.openFileSync(path, parseFlag('w'), cred);
const writable = this.writable.openFileSync(path, parseFlag('w'));
writable.writeSync(data);
writable.closeSync();
}
async copyToWritable(path, cred) {
const stats = await this.stat(path, cred);
async copyToWritable(path) {
const stats = await this.stat(path);
if (stats.isDirectory()) {
await this.writable.mkdir(path, stats.mode, cred);
await this.writable.mkdir(path, stats.mode);
return;
}
const data = new Uint8Array(stats.size);
const readable = await this.readable.openFile(path, parseFlag('r'), cred);
const readable = await this.readable.openFile(path, parseFlag('r'));
await readable.read(data);
await readable.close();
const writable = await this.writable.openFile(path, parseFlag('w'), cred);
const writable = await this.writable.openFile(path, parseFlag('w'));
await writable.write(data);

@@ -484,0 +481,0 @@ await writable.close();

@@ -5,3 +5,3 @@ /// <reference types="node" resolution-mode="require"/>

import type { ExtractProperties } from 'utilium';
import type { Cred } from '../../cred.js';
import { type MountConfiguration } from '../../config.js';
import { File } from '../../file.js';

@@ -12,3 +12,2 @@ import { FileSystem, type FileSystemMetadata } from '../../filesystem.js';

import * as RPC from './rpc.js';
import { type MountConfiguration } from '../../config.js';
type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;

@@ -28,3 +27,3 @@ type FileMethod = keyof FileMethods;

constructor(fs: PortFS, fd: number, path: string, position: number);
rpc<const T extends FileMethod & string>(method: T, ...args: Parameters<FileMethods[T]>): Promise<Awaited<ReturnType<FileMethods[T]>>>;
rpc<const T extends FileMethod>(method: T, ...args: Parameters<FileMethods[T]>): Promise<Awaited<ReturnType<FileMethods[T]>>>;
protected _throwNoSync(syscall: string): never;

@@ -63,11 +62,11 @@ stat(): Promise<Stats>;

ready(): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
statSync(path: string, cred: Cred): Stats;
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
openFileSync(path: string, flag: string, cred: Cred): File;
unlinkSync(path: string, cred: Cred): void;
rmdirSync(path: string, cred: Cred): void;
mkdirSync(path: string, mode: number, cred: Cred): void;
readdirSync(path: string, cred: Cred): string[];
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
renameSync(oldPath: string, newPath: string): void;
statSync(path: string): Stats;
createFileSync(path: string, flag: string, mode: number): File;
openFileSync(path: string, flag: string): File;
unlinkSync(path: string): void;
rmdirSync(path: string): void;
mkdirSync(path: string, mode: number): void;
readdirSync(path: string): string[];
linkSync(srcpath: string, dstpath: string): void;
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;

@@ -96,13 +95,13 @@ }>;

ready(): Promise<void>;
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
stat(path: string, cred: Cred): Promise<Stats>;
rename(oldPath: string, newPath: string): Promise<void>;
stat(path: string): Promise<Stats>;
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
openFile(path: string, flag: string, cred: Cred): Promise<File>;
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
unlink(path: string, cred: Cred): Promise<void>;
rmdir(path: string, cred: Cred): Promise<void>;
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
readdir(path: string, cred: Cred): Promise<string[]>;
exists(path: string, cred: Cred): Promise<boolean>;
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
openFile(path: string, flag: string): Promise<File>;
createFile(path: string, flag: string, mode: number): Promise<File>;
unlink(path: string): Promise<void>;
rmdir(path: string): Promise<void>;
mkdir(path: string, mode: number): Promise<void>;
readdir(path: string): Promise<string[]>;
exists(path: string): Promise<boolean>;
link(srcpath: string, dstpath: string): Promise<void>;
}

@@ -109,0 +108,0 @@ /**

@@ -0,1 +1,2 @@

import { resolveMountConfig } from '../../config.js';
import { Errno, ErrnoError } from '../../error.js';

@@ -8,3 +9,2 @@ import { File } from '../../file.js';

import * as RPC from './rpc.js';
import { resolveMountConfig } from '../../config.js';
export class PortFile extends File {

@@ -29,4 +29,4 @@ constructor(fs, fd, path, position) {

}
stat() {
return this.rpc('stat');
async stat() {
return new Stats(await this.rpc('stat'));
}

@@ -130,7 +130,7 @@ statSync() {

}
rename(oldPath, newPath, cred) {
return this.rpc('rename', oldPath, newPath, cred);
rename(oldPath, newPath) {
return this.rpc('rename', oldPath, newPath);
}
async stat(path, cred) {
return new Stats(await this.rpc('stat', path, cred));
async stat(path) {
return new Stats(await this.rpc('stat', path));
}

@@ -140,25 +140,25 @@ sync(path, data, stats) {

}
openFile(path, flag, cred) {
return this.rpc('openFile', path, flag, cred);
openFile(path, flag) {
return this.rpc('openFile', path, flag);
}
createFile(path, flag, mode, cred) {
return this.rpc('createFile', path, flag, mode, cred);
createFile(path, flag, mode) {
return this.rpc('createFile', path, flag, mode);
}
unlink(path, cred) {
return this.rpc('unlink', path, cred);
unlink(path) {
return this.rpc('unlink', path);
}
rmdir(path, cred) {
return this.rpc('rmdir', path, cred);
rmdir(path) {
return this.rpc('rmdir', path);
}
mkdir(path, mode, cred) {
return this.rpc('mkdir', path, mode, cred);
mkdir(path, mode) {
return this.rpc('mkdir', path, mode);
}
readdir(path, cred) {
return this.rpc('readdir', path, cred);
readdir(path) {
return this.rpc('readdir', path);
}
exists(path, cred) {
return this.rpc('exists', path, cred);
exists(path) {
return this.rpc('exists', path);
}
link(srcpath, dstpath, cred) {
return this.rpc('link', srcpath, dstpath, cred);
link(srcpath, dstpath) {
return this.rpc('link', srcpath, dstpath);
}

@@ -165,0 +165,0 @@ }

@@ -1,2 +0,1 @@

import type { Cred } from '../../cred.js';
import { PreloadFile } from '../../file.js';

@@ -34,18 +33,18 @@ import { FileSystem, type FileSystemMetadata } from '../../filesystem.js';

*/
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
stat(path: string, cred: Cred): Promise<Stats>;
statSync(path: string, cred: Cred): Stats;
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<PreloadFile<this>>;
createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this>;
openFile(path: string, flag: string, cred: Cred): Promise<PreloadFile<this>>;
openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this>;
unlink(path: string, cred: Cred): Promise<void>;
unlinkSync(path: string, cred: Cred): void;
rmdir(path: string, cred: Cred): Promise<void>;
rmdirSync(path: string, cred: Cred): void;
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
mkdirSync(path: string, mode: number, cred: Cred): void;
readdir(path: string, cred: Cred): Promise<string[]>;
readdirSync(path: string, cred: Cred): string[];
rename(oldPath: string, newPath: string): Promise<void>;
renameSync(oldPath: string, newPath: string): void;
stat(path: string): Promise<Stats>;
statSync(path: string): Stats;
createFile(path: string, flag: string, mode: number): Promise<PreloadFile<this>>;
createFileSync(path: string, flag: string, mode: number): PreloadFile<this>;
openFile(path: string, flag: string): Promise<PreloadFile<this>>;
openFileSync(path: string, flag: string): PreloadFile<this>;
unlink(path: string): Promise<void>;
unlinkSync(path: string): void;
rmdir(path: string): Promise<void>;
rmdirSync(path: string): void;
mkdir(path: string, mode: number): Promise<void>;
mkdirSync(path: string, mode: number): void;
readdir(path: string): Promise<string[]>;
readdirSync(path: string): string[];
/**

@@ -61,4 +60,4 @@ * Updated the inode and data node at the given path

syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
link(existing: string, newpath: string, cred: Cred): Promise<void>;
linkSync(existing: string, newpath: string, cred: Cred): void;
link(target: string, link: string): Promise<void>;
linkSync(target: string, link: string): void;
/**

@@ -156,3 +155,3 @@ * Checks if the root directory exists. Creates it if it doesn't.

*/
protected commitNewSync(path: string, type: FileType, mode: number, cred: Cred, data?: Uint8Array): Inode;
protected commitNewSync(path: string, type: FileType, mode: number, data?: Uint8Array): Inode;
/**

@@ -171,3 +170,3 @@ * Remove all traces of the given path from the file system.

*/
protected removeSync(path: string, isDir: boolean, cred: Cred): void;
protected removeSync(path: string, isDir: boolean): void;
}

@@ -46,6 +46,7 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

});
import { R_OK, S_IFDIR, S_IFREG, W_OK } from '../../emulation/constants.js';
import { credentials } from '../../credentials.js';
import { S_IFDIR, S_IFREG } from '../../emulation/constants.js';
import { basename, dirname, join, resolve } from '../../emulation/path.js';
import { Errno, ErrnoError } from '../../error.js';
import { PreloadFile, flagToMode } from '../../file.js';
import { PreloadFile } from '../../file.js';
import { FileSystem } from '../../filesystem.js';

@@ -103,3 +104,3 @@ import { Inode, randomIno, rootIno } from '../../inode.js';

*/
async rename(oldPath, newPath, cred) {
async rename(oldPath, newPath) {
const env_1 = { stack: [], error: void 0, hasError: false };

@@ -111,5 +112,2 @@ try {

oldDirNode = await this.findINode(tx, oldParent), oldDirList = await this.getDirListing(tx, oldDirNode, oldParent);
if (!oldDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
if (!oldDirList[oldName]) {

@@ -140,12 +138,9 @@ throw ErrnoError.With('ENOENT', oldPath, 'rename');

if (newDirList[newName]) {
// If it's a file, delete it.
// If it's a file, delete it, if it's a directory, throw a permissions error.
const newNameNode = await this.getINode(tx, newDirList[newName], newPath);
if (newNameNode.toStats().isFile()) {
await tx.remove(newNameNode.ino);
await tx.remove(newDirList[newName]);
}
else {
// If it's a directory, throw a permissions error.
if (!newNameNode.toStats().isFile()) {
throw ErrnoError.With('EPERM', newPath, 'rename');
}
await tx.remove(newNameNode.ino);
await tx.remove(newDirList[newName]);
}

@@ -168,3 +163,3 @@ newDirList[newName] = nodeId;

}
renameSync(oldPath, newPath, cred) {
renameSync(oldPath, newPath) {
const env_2 = { stack: [], error: void 0, hasError: false };

@@ -176,5 +171,2 @@ try {

oldDirNode = this.findINodeSync(tx, oldParent), oldDirList = this.getDirListingSync(tx, oldDirNode, oldParent);
if (!oldDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
if (!oldDirList[oldName]) {

@@ -205,12 +197,9 @@ throw ErrnoError.With('ENOENT', oldPath, 'rename');

if (newDirList[newName]) {
// If it's a file, delete it.
// If it's a file, delete it, if it's a directory, throw a permissions error.
const newNameNode = this.getINodeSync(tx, newDirList[newName], newPath);
if (newNameNode.toStats().isFile()) {
tx.removeSync(newNameNode.ino);
tx.removeSync(newDirList[newName]);
}
else {
// If it's a directory, throw a permissions error.
if (!newNameNode.toStats().isFile()) {
throw ErrnoError.With('EPERM', newPath, 'rename');
}
tx.removeSync(newNameNode.ino);
tx.removeSync(newDirList[newName]);
}

@@ -231,3 +220,3 @@ newDirList[newName] = ino;

}
async stat(path, cred) {
async stat(path) {
const env_3 = { stack: [], error: void 0, hasError: false };

@@ -240,7 +229,3 @@ try {

}
const stats = inode.toStats();
if (!stats.hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return stats;
return inode.toStats();
}

@@ -257,3 +242,3 @@ catch (e_3) {

}
statSync(path, cred) {
statSync(path) {
const env_4 = { stack: [], error: void 0, hasError: false };

@@ -263,7 +248,3 @@ try {

// Get the inode to the item, convert it into a Stats object.
const stats = this.findINodeSync(tx, path).toStats();
if (!stats.hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return stats;
return this.findINodeSync(tx, path).toStats();
}

@@ -278,11 +259,11 @@ catch (e_4) {

}
async createFile(path, flag, mode, cred) {
const node = await this.commitNew(path, S_IFREG, mode, cred, new Uint8Array(0));
async createFile(path, flag, mode) {
const node = await this.commitNew(path, S_IFREG, mode, new Uint8Array(0));
return new PreloadFile(this, path, flag, node.toStats(), new Uint8Array(0));
}
createFileSync(path, flag, mode, cred) {
this.commitNewSync(path, S_IFREG, mode, cred);
return this.openFileSync(path, flag, cred);
createFileSync(path, flag, mode) {
this.commitNewSync(path, S_IFREG, mode);
return this.openFileSync(path, flag);
}
async openFile(path, flag, cred) {
async openFile(path, flag) {
const env_5 = { stack: [], error: void 0, hasError: false };

@@ -292,5 +273,2 @@ try {

const node = await this.findINode(tx, path), data = await tx.get(node.ino);
if (!node.toStats().hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
if (!data) {

@@ -311,3 +289,3 @@ throw ErrnoError.With('ENOENT', path, 'openFile');

}
openFileSync(path, flag, cred) {
openFileSync(path, flag) {
const env_6 = { stack: [], error: void 0, hasError: false };

@@ -317,5 +295,2 @@ try {

const node = this.findINodeSync(tx, path), data = tx.getSync(node.ino);
if (!node.toStats().hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
if (!data) {

@@ -334,32 +309,29 @@ throw ErrnoError.With('ENOENT', path, 'openFile');

}
async unlink(path, cred) {
return this.remove(path, false, cred);
async unlink(path) {
return this.remove(path, false);
}
unlinkSync(path, cred) {
this.removeSync(path, false, cred);
unlinkSync(path) {
this.removeSync(path, false);
}
async rmdir(path, cred) {
async rmdir(path) {
// Check first if directory is empty.
const list = await this.readdir(path, cred);
if (list.length > 0) {
if ((await this.readdir(path)).length) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
}
await this.remove(path, true, cred);
await this.remove(path, true);
}
rmdirSync(path, cred) {
rmdirSync(path) {
// Check first if directory is empty.
if (this.readdirSync(path, cred).length > 0) {
if (this.readdirSync(path).length) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
}
else {
this.removeSync(path, true, cred);
}
this.removeSync(path, true);
}
async mkdir(path, mode, cred) {
await this.commitNew(path, S_IFDIR, mode, cred, encode('{}'));
async mkdir(path, mode) {
await this.commitNew(path, S_IFDIR, mode, encode('{}'));
}
mkdirSync(path, mode, cred) {
this.commitNewSync(path, S_IFDIR, mode, cred, encode('{}'));
mkdirSync(path, mode) {
this.commitNewSync(path, S_IFDIR, mode, encode('{}'));
}
async readdir(path, cred) {
async readdir(path) {
const env_7 = { stack: [], error: void 0, hasError: false };

@@ -369,5 +341,2 @@ try {

const node = await this.findINode(tx, path);
if (!node.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'readdur');
}
return Object.keys(await this.getDirListing(tx, node, path));

@@ -385,3 +354,3 @@ }

}
readdirSync(path, cred) {
readdirSync(path) {
const env_8 = { stack: [], error: void 0, hasError: false };

@@ -391,5 +360,2 @@ try {

const node = this.findINodeSync(tx, path);
if (!node.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'readdir');
}
return Object.keys(this.getDirListingSync(tx, node, path));

@@ -459,23 +425,13 @@ }

}
async link(existing, newpath, cred) {
async link(target, link) {
const env_11 = { stack: [], error: void 0, hasError: false };
try {
const tx = __addDisposableResource(env_11, this.store.transaction(), true);
const existingDir = dirname(existing), existingDirNode = await this.findINode(tx, existingDir);
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', existingDir, 'link');
}
const newDir = dirname(newpath), newDirNode = await this.findINode(tx, newDir), newListing = await this.getDirListing(tx, newDirNode, newDir);
if (!newDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newDir, 'link');
}
const ino = await this._findINode(tx, existingDir, basename(existing));
const node = await this.getINode(tx, ino, existing);
if (!node.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newpath, 'link');
}
const newDir = dirname(link), newDirNode = await this.findINode(tx, newDir), listing = await this.getDirListing(tx, newDirNode, newDir);
const ino = await this._findINode(tx, dirname(target), basename(target));
const node = await this.getINode(tx, ino, target);
node.nlink++;
newListing[basename(newpath)] = ino;
listing[basename(link)] = ino;
tx.setSync(ino, node.data);
tx.setSync(newDirNode.ino, encodeDirListing(newListing));
tx.setSync(newDirNode.ino, encodeDirListing(listing));
tx.commitSync();

@@ -493,23 +449,13 @@ }

}
linkSync(existing, newpath, cred) {
linkSync(target, link) {
const env_12 = { stack: [], error: void 0, hasError: false };
try {
const tx = __addDisposableResource(env_12, this.store.transaction(), false);
const existingDir = dirname(existing), existingDirNode = this.findINodeSync(tx, existingDir);
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', existingDir, 'link');
}
const newDir = dirname(newpath), newDirNode = this.findINodeSync(tx, newDir), newListing = this.getDirListingSync(tx, newDirNode, newDir);
if (!newDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newDir, 'link');
}
const ino = this._findINodeSync(tx, existingDir, basename(existing));
const node = this.getINodeSync(tx, ino, existing);
if (!node.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newpath, 'link');
}
const newDir = dirname(link), newDirNode = this.findINodeSync(tx, newDir), listing = this.getDirListingSync(tx, newDirNode, newDir);
const ino = this._findINodeSync(tx, dirname(target), basename(target));
const node = this.getINodeSync(tx, ino, target);
node.nlink++;
newListing[basename(newpath)] = ino;
listing[basename(link)] = ino;
tx.setSync(ino, node.data);
tx.setSync(newDirNode.ino, encodeDirListing(newListing));
tx.setSync(newDirNode.ino, encodeDirListing(listing));
tx.commitSync();

@@ -743,3 +689,3 @@ }

*/
async commitNew(path, type, mode, cred, data) {
async commitNew(path, type, mode, data) {
const env_15 = { stack: [], error: void 0, hasError: false };

@@ -749,6 +695,2 @@ try {

const parentPath = dirname(path), parent = await this.findINode(tx, parentPath);
//Check that the creater has correct access
if (!parent.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'commitNewFile');
}
const fname = basename(path), listing = await this.getDirListing(tx, parent, parentPath);

@@ -761,3 +703,3 @@ /*

if (path === '/') {
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}

@@ -767,3 +709,3 @@ // Check if file already exists.

await tx.abort();
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}

@@ -774,4 +716,4 @@ // Commit data.

inode.mode = mode | type;
inode.uid = cred.uid;
inode.gid = cred.gid;
inode.uid = credentials.uid;
inode.gid = credentials.gid;
inode.size = data.length;

@@ -803,3 +745,3 @@ // Update and commit parent directory listing.

*/
commitNewSync(path, type, mode, cred, data = new Uint8Array()) {
commitNewSync(path, type, mode, data = new Uint8Array()) {
const env_16 = { stack: [], error: void 0, hasError: false };

@@ -809,6 +751,2 @@ try {

const parentPath = dirname(path), parent = this.findINodeSync(tx, parentPath);
//Check that the creater has correct access
if (!parent.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'commitNewFile');
}
const fname = basename(path), listing = this.getDirListingSync(tx, parent, parentPath);

@@ -821,7 +759,7 @@ /*

if (path === '/') {
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}
// Check if file already exists.
if (listing[fname]) {
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}

@@ -833,4 +771,4 @@ // Commit data.

node.mode = mode | type;
node.uid = cred.uid;
node.gid = cred.gid;
node.uid = credentials.uid;
node.gid = credentials.gid;
// Update and commit parent directory listing.

@@ -856,3 +794,3 @@ listing[fname] = this.addNewSync(tx, node.data, path);

*/
async remove(path, isDir, cred) {
async remove(path, isDir) {
const env_17 = { stack: [], error: void 0, hasError: false };

@@ -863,3 +801,3 @@ try {

if (!listing[fileName]) {
throw ErrnoError.With('ENOENT', path, 'removeEntry');
throw ErrnoError.With('ENOENT', path, 'remove');
}

@@ -869,12 +807,9 @@ const fileIno = listing[fileName];

const fileNode = await this.getINode(tx, fileIno, path);
if (!fileNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'removeEntry');
}
// Remove from directory listing of parent.
delete listing[fileName];
if (!isDir && fileNode.toStats().isDirectory()) {
throw ErrnoError.With('EISDIR', path, 'removeEntry');
throw ErrnoError.With('EISDIR', path, 'remove');
}
if (isDir && !fileNode.toStats().isDirectory()) {
throw ErrnoError.With('ENOTDIR', path, 'removeEntry');
throw ErrnoError.With('ENOTDIR', path, 'remove');
}

@@ -906,3 +841,3 @@ await tx.set(parentNode.ino, encodeDirListing(listing));

*/
removeSync(path, isDir, cred) {
removeSync(path, isDir) {
const env_18 = { stack: [], error: void 0, hasError: false };

@@ -913,16 +848,13 @@ try {

if (!fileIno) {
throw ErrnoError.With('ENOENT', path, 'removeEntry');
throw ErrnoError.With('ENOENT', path, 'remove');
}
// Get file inode.
const fileNode = this.getINodeSync(tx, fileIno, path);
if (!fileNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'removeEntry');
}
// Remove from directory listing of parent.
delete listing[fileName];
if (!isDir && fileNode.toStats().isDirectory()) {
throw ErrnoError.With('EISDIR', path, 'removeEntry');
throw ErrnoError.With('EISDIR', path, 'remove');
}
if (isDir && !fileNode.toStats().isDirectory()) {
throw ErrnoError.With('ENOTDIR', path, 'removeEntry');
throw ErrnoError.With('ENOTDIR', path, 'remove');
}

@@ -929,0 +861,0 @@ // Update directory listing.

import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
import { credentials } from './credentials.js';
import * as fs from './emulation/index.js';
import { setCred } from './emulation/shared.js';
import { Errno, ErrnoError } from './error.js';

@@ -66,3 +66,3 @@ import { FileSystem } from './filesystem.js';

const gid = 'gid' in config ? config.gid || 0 : 0;
setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
if (!config.mounts) {

@@ -69,0 +69,0 @@ return;

@@ -13,2 +13,3 @@ /// <reference types="node" resolution-mode="require"/>

import { ReadStream, WriteStream } from './streams.js';
import { FSWatcher } from './watchers.js';
/**

@@ -362,3 +363,11 @@ * Asynchronous rename. No arguments other than a possible exception are given

/**
* @todo Implement
* Watch for changes on a file. The callback listener will be called each time the file is accessed.
*
* The `options` argument may be omitted. If provided, it should be an object with a `persistent` boolean and an `interval` number specifying the polling interval in milliseconds.
*
* When a change is detected, the `listener` callback is called with the current and previous `Stats` objects.
*
* @param path The path to the file to watch.
* @param options Optional options object specifying `persistent` and `interval`.
* @param listener The callback listener to be called when the file changes.
*/

@@ -371,9 +380,15 @@ export declare function watchFile(path: fs.PathLike, listener: (curr: Stats, prev: Stats) => void): void;

/**
* @todo Implement
* Stop watching for changes on a file.
*
* If the `listener` is specified, only that particular listener is removed.
* If no `listener` is specified, all listeners are removed, and the file is no longer watched.
*
* @param path The path to the file to stop watching.
* @param listener Optional listener to remove.
*/
export declare function unwatchFile(path: fs.PathLike, listener?: (curr: Stats, prev: Stats) => void): void;
export declare function watch(path: fs.PathLike, listener?: (event: string, filename: string) => any): fs.FSWatcher;
export declare function watch(path: fs.PathLike, listener?: (event: string, filename: string) => any): FSWatcher;
export declare function watch(path: fs.PathLike, options: {
persistent?: boolean;
}, listener?: (event: string, filename: string) => any): fs.FSWatcher;
}, listener?: (event: string, filename: string) => any): FSWatcher;
interface StreamOptions {

@@ -380,0 +395,0 @@ flags?: string;

import { Buffer } from 'buffer';
import { Errno, ErrnoError } from '../error.js';
import { BigIntStats } from '../stats.js';
import { normalizeMode } from '../utils.js';
import { normalizeMode, normalizePath } from '../utils.js';
import { R_OK } from './constants.js';

@@ -9,3 +9,3 @@ import * as promises from './promises.js';

import { ReadStream, WriteStream } from './streams.js';
import { FSWatcher } from './watchers.js';
import { FSWatcher, StatWatcher } from './watchers.js';
const nop = () => { };

@@ -437,15 +437,62 @@ /**

access;
export function watchFile(path, optsListener, listener = nop) {
throw ErrnoError.With('ENOSYS', path.toString(), 'watchFile');
const statWatchers = new Map();
export function watchFile(path, optsListener, listener) {
const normalizedPath = normalizePath(path.toString());
const options = typeof optsListener != 'function' ? optsListener : {};
if (typeof optsListener === 'function') {
listener = optsListener;
}
if (!listener) {
throw new ErrnoError(Errno.EINVAL, 'No listener specified', path.toString(), 'watchFile');
}
if (statWatchers.has(normalizedPath)) {
const entry = statWatchers.get(normalizedPath);
if (entry) {
entry.listeners.add(listener);
}
return;
}
const watcher = new StatWatcher(normalizedPath, options);
watcher.on('change', (curr, prev) => {
const entry = statWatchers.get(normalizedPath);
if (!entry) {
return;
}
for (const listener of entry.listeners) {
listener(curr, prev);
}
});
statWatchers.set(normalizedPath, { watcher, listeners: new Set() });
}
watchFile;
/**
* @todo Implement
* Stop watching for changes on a file.
*
* If the `listener` is specified, only that particular listener is removed.
* If no `listener` is specified, all listeners are removed, and the file is no longer watched.
*
* @param path The path to the file to stop watching.
* @param listener Optional listener to remove.
*/
export function unwatchFile(path, listener = nop) {
throw ErrnoError.With('ENOSYS', path.toString(), 'unwatchFile');
const normalizedPath = normalizePath(path.toString());
const entry = statWatchers.get(normalizedPath);
if (entry) {
if (listener && listener !== nop) {
entry.listeners.delete(listener);
}
else {
// If no listener is specified, remove all listeners
entry.listeners.clear();
}
if (entry.listeners.size === 0) {
// No more listeners, stop the watcher
entry.watcher.stop();
statWatchers.delete(normalizedPath);
}
}
}
unwatchFile;
export function watch(path, options, listener) {
const watcher = new FSWatcher(typeof options == 'object' ? options : {});
const watcher = new FSWatcher(normalizePath(path), typeof options == 'object' ? options : {});
listener = typeof options == 'function' ? options : listener;

@@ -488,3 +535,3 @@ watcher.on('change', listener || nop);

.then(() => callback(error))
.catch(callback);
.catch(nop);
},

@@ -491,0 +538,0 @@ });

/// <reference types="node" resolution-mode="require"/>
import type { Dirent as _Dirent, Dir as _Dir } from 'fs';
import type { Dir as _Dir, Dirent as _Dirent } from 'fs';
import type { Stats } from '../stats.js';
import type { Callback } from '../utils.js';
import type { Stats } from '../stats.js';
export declare class Dirent implements _Dirent {

@@ -26,7 +26,3 @@ path: string;

protected checkClosed(): void;
protected _entries: Dirent[];
/**
* @internal
*/
_loadEntries(): Promise<void>;
protected _entries?: Dirent[];
constructor(path: string);

@@ -58,2 +54,3 @@ /**

readSync(): Dirent | null;
next(): Promise<IteratorResult<Dirent>>;
/**

@@ -60,0 +57,0 @@ * Asynchronously iterates over the directory via `readdir(3)` until all entries have been read.

@@ -0,5 +1,5 @@

import { Errno, ErrnoError } from '../error.js';
import { basename } from './path.js';
import { readdir } from './promises.js';
import { ErrnoError, Errno } from '../error.js';
import { readdirSync } from './sync.js';
import { basename } from './path.js';
export class Dirent {

@@ -47,12 +47,5 @@ get name() {

}
/**
* @internal
*/
async _loadEntries() {
this._entries ?? (this._entries = await readdir(this.path, { withFileTypes: true }));
}
constructor(path) {
this.path = path;
this.closed = false;
this._entries = [];
}

@@ -74,7 +67,8 @@ close(cb) {

async _read() {
await this._loadEntries();
this.checkClosed();
this._entries ?? (this._entries = await readdir(this.path, { withFileTypes: true }));
if (!this._entries.length) {
return null;
}
return this._entries.shift() || null;
return this._entries.shift() ?? null;
}

@@ -93,2 +87,3 @@ read(cb) {

readSync() {
this.checkClosed();
this._entries ?? (this._entries = readdirSync(this.path, { withFileTypes: true }));

@@ -98,4 +93,12 @@ if (!this._entries.length) {

}
return this._entries.shift() || null;
return this._entries.shift() ?? null;
}
async next() {
const value = await this._read();
if (value) {
return { done: false, value };
}
await this.close();
return { done: true, value: undefined };
}
/**

@@ -105,15 +108,4 @@ * Asynchronously iterates over the directory via `readdir(3)` until all entries have been read.

[Symbol.asyncIterator]() {
const _this = this;
return {
[Symbol.asyncIterator]: this[Symbol.asyncIterator],
async next() {
const value = await _this._read();
if (value != null) {
return { done: false, value };
}
await _this.close();
return { done: true, value: undefined };
},
};
return this;
}
}

@@ -317,6 +317,6 @@ /// <reference types="node" resolution-mode="require"/>

* `link`.
* @param existing
* @param newpath
* @param targetPath
* @param linkPath
*/
export declare function link(existing: fs.PathLike, newpath: fs.PathLike): Promise<void>;
export declare function link(targetPath: fs.PathLike, linkPath: fs.PathLike): Promise<void>;
/**

@@ -323,0 +323,0 @@ * `symlink`.

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

import { Errno, ErrnoError } from '../error.js';
import { isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import '../polyfills.js';

@@ -57,5 +57,6 @@ import { BigIntStats } from '../stats.js';

import { dirname, join, parse } from './path.js';
import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { ReadStream, WriteStream } from './streams.js';
import { FSWatcher } from './watchers.js';
import { FSWatcher, emitChange } from './watchers.js';
export * as constants from './constants.js';

@@ -71,4 +72,5 @@ export class FileHandle {

*/
chown(uid, gid) {
return this.file.chown(uid, gid);
async chown(uid, gid) {
await this.file.chown(uid, gid);
emitChange('change', this.file.path);
}

@@ -79,3 +81,3 @@ /**

*/
chmod(mode) {
async chmod(mode) {
const numMode = normalizeMode(mode, -1);

@@ -85,3 +87,4 @@ if (numMode < 0) {

}
return this.file.chmod(numMode);
await this.file.chmod(numMode);
emitChange('change', this.file.path);
}

@@ -104,3 +107,3 @@ /**

*/
truncate(len) {
async truncate(len) {
len || (len = 0);

@@ -110,3 +113,4 @@ if (len < 0) {

}
return this.file.truncate(len);
await this.file.truncate(len);
emitChange('change', this.file.path);
}

@@ -118,4 +122,5 @@ /**

*/
utimes(atime, mtime) {
return this.file.utimes(normalizeTime(atime), normalizeTime(mtime));
async utimes(atime, mtime) {
await this.file.utimes(normalizeTime(atime), normalizeTime(mtime));
emitChange('change', this.file.path);
}

@@ -143,2 +148,3 @@ /**

await this.file.write(encodedData, 0, encodedData.length);
emitChange('change', this.file.path);
}

@@ -219,2 +225,5 @@ /**

const stats = await this.file.stat();
if (!stats.hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', this.file.path, 'stat');
}
return opts?.bigint ? new BigIntStats(stats) : stats;

@@ -241,2 +250,3 @@ }

const bytesWritten = await this.file.write(buffer, offset, length, position);
emitChange('change', this.file.path);
return { buffer, bytesWritten };

@@ -266,2 +276,3 @@ }

await this.file.write(encodedData, 0, encodedData.length, 0);
emitChange('change', this.file.path);
}

@@ -361,5 +372,9 @@ /**

const dst = resolveMount(newPath);
if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
try {
if (src.mountPoint == dst.mountPoint) {
await src.fs.rename(src.path, dst.path, cred);
await src.fs.rename(src.path, dst.path);
emitChange('rename', oldPath.toString());
return;

@@ -369,2 +384,3 @@ }

await unlink(oldPath);
emitChange('rename', oldPath.toString());
}

@@ -383,3 +399,3 @@ catch (e) {

const { fs, path: resolved } = resolveMount(await realpath(path));
return await fs.exists(resolved, cred);
return await fs.exists(resolved);
}

@@ -397,3 +413,6 @@ catch (e) {

try {
const stats = await fs.stat(resolved, cred);
const stats = await fs.stat(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -410,3 +429,3 @@ }

try {
const stats = await fs.stat(resolved, cred);
const stats = await fs.stat(resolved);
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -450,3 +469,7 @@ }

try {
await fs.unlink(resolved, cred);
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');
}
await fs.unlink(resolved);
emitChange('rename', path.toString());
}

@@ -467,3 +490,4 @@ catch (e) {

const { fs, path: resolved } = resolveMount(path);
if (!(await fs.exists(resolved, cred))) {
const stats = await fs.stat(resolved).catch(() => null);
if (!stats) {
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {

@@ -473,14 +497,18 @@ throw ErrnoError.With('ENOENT', path, '_open');

// Create the file
const parentStats = await fs.stat(dirname(resolved), cred);
if (parentStats && !parentStats.isDirectory()) {
const parentStats = await fs.stat(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');
}
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
return new FileHandle(await fs.createFile(resolved, flag, mode));
}
if (!stats.hasAccess(flagToMode(flag), credentials)) {
throw ErrnoError.With('EACCES', path, '_open');
}
if (isExclusive(flag)) {
throw ErrnoError.With('EEXIST', path, '_open');
}
if (!isTruncating(flag)) {
return new FileHandle(await fs.openFile(resolved, flag, cred));
}
const handle = new FileHandle(await fs.openFile(resolved, flag));
/*

@@ -492,6 +520,7 @@ In a previous implementation, we deleted the file and

*/
const file = await fs.openFile(resolved, flag, cred);
await file.truncate(0);
await file.sync();
return new FileHandle(file);
if (isTruncating(flag)) {
await handle.truncate(0);
await handle.sync();
}
return handle;
}

@@ -605,3 +634,7 @@ /**

try {
await fs.rmdir(resolved, cred);
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');
}
await fs.rmdir(resolved);
emitChange('rename', path.toString());
}

@@ -622,6 +655,11 @@ catch (e) {

if (!options?.recursive) {
await fs.mkdir(resolved, mode, cred);
if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');
}
await fs.mkdir(resolved, mode);
emitChange('rename', path.toString());
return;
}
const dirs = [];
for (let dir = resolved, origDir = path; !(await fs.exists(dir, cred)); dir = dirname(dir), origDir = dirname(origDir)) {
for (let dir = resolved, origDir = path; !(await fs.exists(dir)); dir = dirname(dir), origDir = dirname(origDir)) {
dirs.unshift(dir);

@@ -631,3 +669,7 @@ errorPaths[dir] = origDir;

for (const dir of dirs) {
await fs.mkdir(dir, mode, cred);
if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');
}
await fs.mkdir(dir, mode);
emitChange('rename', dir);
}

@@ -643,2 +685,5 @@ return dirs[0];

path = normalizePath(path);
if (!(await stat(path)).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'readdir');
}
path = (await exists(path)) ? await realpath(path) : path;

@@ -648,3 +693,3 @@ const { fs, path: resolved } = resolveMount(path);

try {
entries = await fs.readdir(resolved, cred);
entries = await fs.readdir(resolved);
}

@@ -674,14 +719,27 @@ catch (e) {

* `link`.
* @param existing
* @param newpath
* @param targetPath
* @param linkPath
*/
export async function link(existing, newpath) {
existing = normalizePath(existing);
newpath = normalizePath(newpath);
const { fs, path: resolved } = resolveMount(newpath);
export async function link(targetPath, linkPath) {
targetPath = normalizePath(targetPath);
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
}
const { fs, path } = resolveMount(targetPath);
const link = resolveMount(linkPath);
if (fs != link.fs) {
throw ErrnoError.With('EXDEV', linkPath, 'link');
}
try {
return await fs.link(existing, newpath, cred);
if (!(await fs.stat(path)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'link');
}
return await fs.link(path, link.path);
}
catch (e) {
throw fixError(e, { [resolved]: newpath });
throw fixError(e, { [link.path]: linkPath, [path]: targetPath });
}

@@ -870,3 +928,3 @@ }

try {
const stats = await fs.stat(resolvedPath, cred);
const stats = await fs.stat(resolvedPath);
if (!stats.isSymbolicLink()) {

@@ -885,3 +943,3 @@ return lpath;

[Symbol.asyncIterator]() {
const watcher = new FSWatcher(typeof options != 'string' ? options : { encoding: options });
const watcher = new FSWatcher(filename.toString(), typeof options != 'string' ? options : { encoding: options });
function withDone(done) {

@@ -912,3 +970,3 @@ return function () {

const stats = await stat(path);
if (!stats.hasAccess(mode, cred)) {
if (!stats.hasAccess(mode, credentials)) {
throw new ErrnoError(Errno.EACCES);

@@ -969,2 +1027,3 @@ }

await writeFile(dest, await readFile(src));
emitChange('rename', dest.toString());
}

@@ -981,5 +1040,3 @@ copyFile;

path = normalizePath(path);
const dir = new Dir(path);
await dir._loadEntries();
return dir;
return new Dir(path);
}

@@ -986,0 +1043,0 @@ opendir;

/// <reference types="node" resolution-mode="require"/>
import type { BigIntStatsFs, StatsFs } from 'node:fs';
import type { Cred } from '../cred.js';
import type { File } from '../file.js';
import type { FileSystem } from '../filesystem.js';
import { type AbsolutePath } from './path.js';
export declare let cred: Cred;
export declare function setCred(val: Cred): void;
export declare const fdMap: Map<number, File>;

@@ -10,0 +7,0 @@ export declare function file2fd(file: File): number;

// Utilities and shared data
import { InMemory } from '../backends/memory.js';
import { rootCred } from '../cred.js';
import { Errno, ErrnoError } from '../error.js';

@@ -8,7 +7,2 @@ import { size_max } from '../inode.js';

import { resolve } from './path.js';
// credentials
export let cred = rootCred;
export function setCred(val) {
cred = val;
}
// descriptors

@@ -15,0 +9,0 @@ export const fdMap = new Map();

@@ -200,3 +200,2 @@ /// <reference types="node" resolution-mode="require"/>

* @param mode defaults to o777
* @todo Implement recursion
*/

@@ -234,6 +233,6 @@ export declare function mkdirSync(path: fs.PathLike, options: fs.MakeDirectoryOptions & {

* Synchronous `link`.
* @param existing
* @param newpath
* @param targetPath
* @param linkPath
*/
export declare function linkSync(existing: fs.PathLike, newpath: fs.PathLike): void;
export declare function linkSync(targetPath: fs.PathLike, linkPath: fs.PathLike): void;
/**

@@ -240,0 +239,0 @@ * Synchronous `symlink`.

@@ -48,9 +48,11 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

import { Errno, ErrnoError } from '../error.js';
import { isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import { BigIntStats } from '../stats.js';
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
import { COPYFILE_EXCL, F_OK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK } from './constants.js';
import * as constants from './constants.js';
import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { emitChange } from './watchers.js';
/**

@@ -64,14 +66,19 @@ * Synchronous rename.

newPath = normalizePath(newPath);
const _old = resolveMount(oldPath);
const _new = resolveMount(newPath);
const paths = { [_old.path]: oldPath, [_new.path]: newPath };
const oldMount = resolveMount(oldPath);
const newMount = resolveMount(newPath);
if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
try {
if (_old === _new) {
return _old.fs.renameSync(_old.path, _new.path, cred);
if (oldMount === newMount) {
oldMount.fs.renameSync(oldMount.path, newMount.path);
emitChange('rename', oldPath.toString());
return;
}
writeFileSync(newPath, readFileSync(oldPath));
unlinkSync(oldPath);
emitChange('rename', oldPath.toString());
}
catch (e) {
throw fixError(e, paths);
throw fixError(e, { [oldMount.path]: oldPath, [newMount.path]: newPath });
}

@@ -88,3 +95,3 @@ }

const { fs, path: resolvedPath } = resolveMount(realpathSync(path));
return fs.existsSync(resolvedPath, cred);
return fs.existsSync(resolvedPath);
}

@@ -103,3 +110,6 @@ catch (e) {

try {
const stats = fs.statSync(resolved, cred);
const stats = fs.statSync(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -116,3 +126,3 @@ }

try {
const stats = fs.statSync(resolved, cred);
const stats = fs.statSync(resolved);
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -157,3 +167,7 @@ }

try {
return fs.unlinkSync(resolved, cred);
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');
}
fs.unlinkSync(resolved);
emitChange('rename', path.toString());
}

@@ -170,3 +184,3 @@ catch (e) {

const { fs, path: resolved } = resolveMount(path);
if (!fs.existsSync(resolved, cred)) {
if (!fs.existsSync(resolved)) {
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {

@@ -176,10 +190,13 @@ throw ErrnoError.With('ENOENT', path, '_open');

// Create the file
const parentStats = fs.statSync(dirname(resolved), cred);
const parentStats = fs.statSync(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');
}
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return fs.createFileSync(resolved, flag, mode, cred);
return fs.createFileSync(resolved, flag, mode);
}
const stats = fs.statSync(resolved, cred);
if (!stats.hasAccess(mode, cred)) {
const stats = fs.statSync(resolved);
if (!stats.hasAccess(mode, credentials) || !stats.hasAccess(flagToMode(flag), credentials)) {
throw ErrnoError.With('EACCES', path, '_open');

@@ -190,14 +207,8 @@ }

}
if (!isTruncating(flag)) {
return fs.openFileSync(resolved, flag, cred);
const file = fs.openFileSync(resolved, flag);
if (isTruncating(flag)) {
file.truncateSync(0);
file.syncSync();
}
// Delete file.
fs.unlinkSync(resolved, cred);
/*
Create file. Use the same mode as the old file.
Node itself modifies the ctime when this occurs, so this action
will preserve that behavior if the underlying file system
supports those properties.
*/
return fs.createFileSync(resolved, flag, stats.mode, cred);
return file;
}

@@ -212,3 +223,3 @@ /**

*/
export function openSync(path, flag, mode = F_OK) {
export function openSync(path, flag, mode = constants.F_OK) {
return file2fd(_openSync(path, flag, mode, true));

@@ -273,2 +284,3 @@ }

file.writeSync(encodedData, 0, encodedData.byteLength, 0);
emitChange('change', path.toString());
}

@@ -381,3 +393,5 @@ catch (e_3) {

position ?? (position = file.position);
return file.writeSync(buffer, offset, length, position);
const bytesWritten = file.writeSync(buffer, offset, length, position);
emitChange('change', file.path);
return bytesWritten;
}

@@ -441,3 +455,7 @@ writeSync;

try {
fs.rmdirSync(resolved, cred);
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');
}
fs.rmdirSync(resolved);
emitChange('rename', path.toString());
}

@@ -458,6 +476,9 @@ catch (e) {

if (!options?.recursive) {
return fs.mkdirSync(resolved, mode, cred);
if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');
}
return fs.mkdirSync(resolved, mode);
}
const dirs = [];
for (let dir = resolved, original = path; !fs.existsSync(dir, cred); dir = dirname(dir), original = dirname(original)) {
for (let dir = resolved, original = path; !fs.existsSync(dir); dir = dirname(dir), original = dirname(original)) {
dirs.unshift(dir);

@@ -467,3 +488,7 @@ errorPaths[dir] = original;

for (const dir of dirs) {
fs.mkdirSync(dir, mode, cred);
if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');
}
fs.mkdirSync(dir, mode);
emitChange('rename', dir);
}

@@ -481,4 +506,7 @@ return dirs[0];

let entries;
if (!statSync(path).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'readdir');
}
try {
entries = fs.readdirSync(resolved, cred);
entries = fs.readdirSync(resolved);
}

@@ -513,14 +541,27 @@ catch (e) {

* Synchronous `link`.
* @param existing
* @param newpath
* @param targetPath
* @param linkPath
*/
export function linkSync(existing, newpath) {
existing = normalizePath(existing);
newpath = normalizePath(newpath);
const { fs, path: resolved } = resolveMount(existing);
export function linkSync(targetPath, linkPath) {
targetPath = normalizePath(targetPath);
if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
}
const { fs, path } = resolveMount(targetPath);
const link = resolveMount(linkPath);
if (fs != link.fs) {
throw ErrnoError.With('EXDEV', linkPath, 'link');
}
try {
return fs.linkSync(resolved, newpath, cred);
if (!fs.statSync(path).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'link');
}
return fs.linkSync(path, linkPath);
}
catch (e) {
throw fixError(e, { [resolved]: existing });
throw fixError(e, { [path]: targetPath, [link.path]: linkPath });
}

@@ -544,3 +585,3 @@ }

const file = _openSync(path, 'r+', 0o644, false);
file._setTypeSync(S_IFLNK);
file._setTypeSync(constants.S_IFLNK);
}

@@ -634,3 +675,3 @@ symlinkSync;

try {
const stats = fs.statSync(resolvedPath, cred);
const stats = fs.statSync(resolvedPath);
if (!stats.isSymbolicLink()) {

@@ -653,3 +694,3 @@ return lpath;

const stats = statSync(path);
if (!stats.hasAccess(mode, cred)) {
if (!stats.hasAccess(mode, credentials)) {
throw new ErrnoError(Errno.EACCES);

@@ -666,4 +707,4 @@ }

const stats = statSync(path);
switch (stats.mode & S_IFMT) {
case S_IFDIR:
switch (stats.mode & constants.S_IFMT) {
case constants.S_IFDIR:
if (options?.recursive) {

@@ -676,10 +717,10 @@ for (const entry of readdirSync(path)) {

return;
case S_IFREG:
case S_IFLNK:
case constants.S_IFREG:
case constants.S_IFLNK:
unlinkSync(path);
return;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
case S_IFSOCK:
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:

@@ -708,6 +749,7 @@ throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');

dest = normalizePath(dest);
if (flags && flags & COPYFILE_EXCL && existsSync(dest)) {
if (flags && flags & constants.COPYFILE_EXCL && existsSync(dest)) {
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');
}
writeFileSync(dest, readFileSync(src));
emitChange('rename', dest.toString());
}

@@ -755,3 +797,3 @@ copyFileSync;

path = normalizePath(path);
return new Dir(path); // Re-use existing `Dir` class
return new Dir(path);
}

@@ -778,4 +820,4 @@ opendirSync;

}
switch (srcStats.mode & S_IFMT) {
case S_IFDIR:
switch (srcStats.mode & constants.S_IFMT) {
case constants.S_IFDIR:
if (!opts?.recursive) {

@@ -792,10 +834,10 @@ throw new ErrnoError(Errno.EISDIR, source + ' is a directory (not copied)', source, 'cp');

break;
case S_IFREG:
case S_IFLNK:
case constants.S_IFREG:
case constants.S_IFLNK:
copyFileSync(source, destination);
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
case S_IFSOCK:
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:

@@ -802,0 +844,0 @@ throw new ErrnoError(Errno.EPERM, 'File type not supported', source, 'rm');

@@ -7,5 +7,14 @@ /// <reference types="node" resolution-mode="require"/>

import type * as fs from 'node:fs';
import { type Stats } from '../stats.js';
/**
* Base class for file system watchers.
* Provides event handling capabilities for watching file system changes.
*
* @template TEvents The type of events emitted by the watcher.
*/
declare class Watcher<TEvents extends Record<string, unknown[]> = Record<string, unknown[]>> extends EventEmitter<TEvents> implements NodeEventEmitter {
readonly path: string;
off<T extends EventEmitter.EventNames<TEvents>>(event: T, fn?: (...args: any[]) => void, context?: any, once?: boolean): this;
removeListener<T extends EventEmitter.EventNames<TEvents>>(event: T, fn?: (...args: any[]) => void, context?: any, once?: boolean): this;
constructor(path: string);
setMaxListeners(): never;

@@ -20,3 +29,5 @@ getMaxListeners(): never;

/**
* @todo Actually emit events
* Watches for changes on the file system.
*
* @template T The type of the filename, either `string` or `Buffer`.
*/

@@ -29,7 +40,33 @@ export declare class FSWatcher<T extends string | Buffer = string | Buffer> extends Watcher<{

readonly options: fs.WatchOptions;
constructor(options: fs.WatchOptions);
constructor(path: string, options: fs.WatchOptions);
close(): void;
[Symbol.dispose](): void;
}
export declare class StatWatcher extends Watcher implements fs.StatWatcher {
/**
* Watches for changes to a file's stats.
*
* Instances of `StatWatcher` are used by `fs.watchFile()` to monitor changes to a file's statistics.
*/
export declare class StatWatcher extends Watcher<{
change: [current: Stats, previous: Stats];
close: [];
error: [error: Error];
}> implements fs.StatWatcher {
private options;
private intervalId?;
private previous?;
constructor(path: string, options: {
persistent?: boolean;
interval?: number;
});
protected onInterval(): void;
protected start(): void;
/**
* @internal
*/
stop(): void;
}
export declare function addWatcher(path: string, watcher: FSWatcher): void;
export declare function removeWatcher(path: string, watcher: FSWatcher): void;
export declare function emitChange(eventType: fs.WatchEventType, filename: string): void;
export {};
import { EventEmitter } from 'eventemitter3';
import { ErrnoError } from '../error.js';
import { isStatsEqual } from '../stats.js';
import { normalizePath } from '../utils.js';
import { dirname, basename } from './path.js';
import { statSync } from './sync.js';
/**
* Base class for file system watchers.
* Provides event handling capabilities for watching file system changes.
*
* @template TEvents The type of events emitted by the watcher.
*/
class Watcher extends EventEmitter {

@@ -12,16 +22,20 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

/* eslint-enable @typescript-eslint/no-explicit-any */
constructor(path) {
super();
this.path = path;
}
setMaxListeners() {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.setMaxListeners');
}
getMaxListeners() {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.getMaxListeners');
}
prependListener() {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.prependListener');
}
prependOnceListener() {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.prependOnceListener');
}
rawListeners() {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.rawListeners');
}

@@ -36,12 +50,104 @@ ref() {

/**
* @todo Actually emit events
* Watches for changes on the file system.
*
* @template T The type of the filename, either `string` or `Buffer`.
*/
export class FSWatcher extends Watcher {
constructor(options) {
super();
constructor(path, options) {
super(path);
this.options = options;
addWatcher(path.toString(), this);
}
close() { }
close() {
super.emit('close');
removeWatcher(this.path.toString(), this);
}
[Symbol.dispose]() {
this.close();
}
}
/**
* Watches for changes to a file's stats.
*
* Instances of `StatWatcher` are used by `fs.watchFile()` to monitor changes to a file's statistics.
*/
export class StatWatcher extends Watcher {
constructor(path, options) {
super(path);
this.options = options;
this.start();
}
onInterval() {
try {
const current = statSync(this.path);
if (!isStatsEqual(this.previous, current)) {
this.emit('change', current, this.previous);
this.previous = current;
}
}
catch (e) {
this.emit('error', e);
}
}
start() {
const interval = this.options.interval || 5000;
try {
this.previous = statSync(this.path);
}
catch (e) {
this.emit('error', e);
return;
}
this.intervalId = setInterval(this.onInterval.bind(this), interval);
if (!this.options.persistent && typeof this.intervalId == 'object') {
this.intervalId.unref();
}
}
/**
* @internal
*/
stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = undefined;
}
this.removeAllListeners();
}
}
const watchers = new Map();
export function addWatcher(path, watcher) {
const normalizedPath = normalizePath(path);
if (!watchers.has(normalizedPath)) {
watchers.set(normalizedPath, new Set());
}
watchers.get(normalizedPath).add(watcher);
}
export function removeWatcher(path, watcher) {
const normalizedPath = normalizePath(path);
if (watchers.has(normalizedPath)) {
watchers.get(normalizedPath).delete(watcher);
if (watchers.get(normalizedPath).size === 0) {
watchers.delete(normalizedPath);
}
}
}
export function emitChange(eventType, filename) {
let normalizedFilename = normalizePath(filename);
// Notify watchers on the specific file
if (watchers.has(normalizedFilename)) {
for (const watcher of watchers.get(normalizedFilename)) {
watcher.emit('change', eventType, basename(filename));
}
}
// Notify watchers on parent directories if they are watching recursively
let parent = dirname(normalizedFilename);
while (parent !== normalizedFilename && parent !== '/') {
if (watchers.has(parent)) {
for (const watcher of watchers.get(parent)) {
watcher.emit('change', eventType, basename(filename));
}
}
normalizedFilename = parent;
parent = dirname(parent);
}
}

@@ -195,3 +195,3 @@ /// <reference types="node" resolution-mode="require"/>

/**
* @return A friendly error message.
* @returns A friendly error message.
*/

@@ -198,0 +198,0 @@ toString(): string;

@@ -269,3 +269,3 @@ /**

/**
* @return A friendly error message.
* @returns A friendly error message.
*/

@@ -272,0 +272,0 @@ toString() {

@@ -1,2 +0,1 @@

import type { Cred } from './cred.js';
import type { File } from './file.js';

@@ -78,15 +77,15 @@ import { type Stats } from './stats.js';

*/
abstract rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
abstract rename(oldPath: string, newPath: string): Promise<void>;
/**
* Synchronous rename.
*/
abstract renameSync(oldPath: string, newPath: string, cred: Cred): void;
abstract renameSync(oldPath: string, newPath: string): void;
/**
* Asynchronous `stat`.
*/
abstract stat(path: string, cred: Cred): Promise<Stats>;
abstract stat(path: string): Promise<Stats>;
/**
* Synchronous `stat`.
*/
abstract statSync(path: string, cred: Cred): Stats;
abstract statSync(path: string): Stats;
/**

@@ -97,3 +96,3 @@ * Opens the file at `path` with the given flag. The file must exist.

*/
abstract openFile(path: string, flag: string, cred: Cred): Promise<File>;
abstract openFile(path: string, flag: string): Promise<File>;
/**

@@ -105,27 +104,27 @@ * Opens the file at `path` with the given flag. The file must exist.

*/
abstract openFileSync(path: string, flag: string, cred: Cred): File;
abstract openFileSync(path: string, flag: string): File;
/**
* Create the file at `path` with the given mode. Then, open it with the given flag.
*/
abstract createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
abstract createFile(path: string, flag: string, mode: number): Promise<File>;
/**
* Create the file at `path` with the given mode. Then, open it with the given flag.
*/
abstract createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
abstract createFileSync(path: string, flag: string, mode: number): File;
/**
* Asynchronous `unlink`.
*/
abstract unlink(path: string, cred: Cred): Promise<void>;
abstract unlink(path: string): Promise<void>;
/**
* Synchronous `unlink`.
*/
abstract unlinkSync(path: string, cred: Cred): void;
abstract unlinkSync(path: string): void;
/**
* Asynchronous `rmdir`.
*/
abstract rmdir(path: string, cred: Cred): Promise<void>;
abstract rmdir(path: string): Promise<void>;
/**
* Synchronous `rmdir`.
*/
abstract rmdirSync(path: string, cred: Cred): void;
abstract rmdirSync(path: string): void;
/**

@@ -135,3 +134,3 @@ * Asynchronous `mkdir`.

*/
abstract mkdir(path: string, mode: number, cred: Cred): Promise<void>;
abstract mkdir(path: string, mode: number): Promise<void>;
/**

@@ -141,27 +140,27 @@ * Synchronous `mkdir`.

*/
abstract mkdirSync(path: string, mode: number, cred: Cred): void;
abstract mkdirSync(path: string, mode: number): void;
/**
* Asynchronous `readdir`. Reads the contents of a directory.
*/
abstract readdir(path: string, cred: Cred): Promise<string[]>;
abstract readdir(path: string): Promise<string[]>;
/**
* Synchronous `readdir`. Reads the contents of a directory.
*/
abstract readdirSync(path: string, cred: Cred): string[];
abstract readdirSync(path: string): string[];
/**
* Test whether or not the given path exists.
*/
exists(path: string, cred: Cred): Promise<boolean>;
exists(path: string): Promise<boolean>;
/**
* Test whether or not the given path exists.
*/
existsSync(path: string, cred: Cred): boolean;
existsSync(path: string): boolean;
/**
* Asynchronous `link`.
*/
abstract link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
abstract link(target: string, link: string): Promise<void>;
/**
* Synchronous `link`.
*/
abstract linkSync(srcpath: string, dstpath: string, cred: Cred): void;
abstract linkSync(target: string, link: string): void;
/**

@@ -168,0 +167,0 @@ * Synchronize the data and stats for path asynchronously

@@ -30,5 +30,5 @@ import { ZenFsType } from './stats.js';

*/
async exists(path, cred) {
async exists(path) {
try {
await this.stat(path, cred);
await this.stat(path);
return true;

@@ -43,5 +43,5 @@ }

*/
existsSync(path, cred) {
existsSync(path) {
try {
this.statSync(path, cred);
this.statSync(path);
return true;

@@ -48,0 +48,0 @@ }

@@ -12,3 +12,3 @@ export * from './error.js';

export * from './config.js';
export * from './cred.js';
export * from './credentials.js';
export * from './file.js';

@@ -15,0 +15,0 @@ export * from './filesystem.js';

@@ -12,3 +12,3 @@ export * from './error.js';

export * from './config.js';
export * from './cred.js';
export * from './credentials.js';
export * from './file.js';

@@ -15,0 +15,0 @@ export * from './filesystem.js';

@@ -1,2 +0,1 @@

import { type Cred } from '../cred.js';
import { type File } from '../file.js';

@@ -16,7 +15,7 @@ import type { FileSystem } from '../filesystem.js';

* Implementing classes must define `_sync` for the synchronous file system used as a cache.
* Synchronous methods on an asynchronous FS are implemented by:
* - Performing operations over the in-memory copy,
* while asynchronously pipelining them to the backing store.
* - During loading, the contents of the async file system are preloaded into the synchronous store.
*
* Synchronous methods on an asynchronous FS are implemented by performing operations over the in-memory copy,
* while asynchronously pipelining them to the backing store.
* During loading, the contents of the async file system are preloaded into the synchronous store.
*
*/

@@ -30,12 +29,12 @@ export declare function Async<T extends typeof FileSystem>(FS: T): Mixin<T, {

ready(): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
statSync(path: string, cred: Cred): Stats;
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
openFileSync(path: string, flag: string, cred: Cred): File;
unlinkSync(path: string, cred: Cred): void;
rmdirSync(path: string, cred: Cred): void;
mkdirSync(path: string, mode: number, cred: Cred): void;
readdirSync(path: string, cred: Cred): string[];
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
renameSync(oldPath: string, newPath: string): void;
statSync(path: string): Stats;
createFileSync(path: string, flag: string, mode: number): File;
openFileSync(path: string, flag: string): File;
unlinkSync(path: string): void;
rmdirSync(path: string): void;
mkdirSync(path: string, mode: number): void;
readdirSync(path: string): string[];
linkSync(srcpath: string, dstpath: string): void;
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
}>;

@@ -46,6 +46,5 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

});
import { rootCred } from '../cred.js';
import { join } from '../emulation/path.js';
import { Errno, ErrnoError } from '../error.js';
import { PreloadFile, parseFlag } from '../file.js';
import { parseFlag, PreloadFile } from '../file.js';
/**

@@ -55,7 +54,7 @@ * Async() implements synchronous methods on an asynchronous file system

* Implementing classes must define `_sync` for the synchronous file system used as a cache.
* Synchronous methods on an asynchronous FS are implemented by:
* - Performing operations over the in-memory copy,
* while asynchronously pipelining them to the backing store.
* - During loading, the contents of the async file system are preloaded into the synchronous store.
*
* Synchronous methods on an asynchronous FS are implemented by performing operations over the in-memory copy,
* while asynchronously pipelining them to the backing store.
* During loading, the contents of the async file system are preloaded into the synchronous store.
*
*/

@@ -105,20 +104,20 @@ export function Async(FS) {

}
renameSync(oldPath, newPath, cred) {
renameSync(oldPath, newPath) {
this.checkSync(oldPath, 'rename');
this._sync.renameSync(oldPath, newPath, cred);
this.queue('rename', oldPath, newPath, cred);
this._sync.renameSync(oldPath, newPath);
this.queue('rename', oldPath, newPath);
}
statSync(path, cred) {
statSync(path) {
this.checkSync(path, 'stat');
return this._sync.statSync(path, cred);
return this._sync.statSync(path);
}
createFileSync(path, flag, mode, cred) {
createFileSync(path, flag, mode) {
this.checkSync(path, 'createFile');
this._sync.createFileSync(path, flag, mode, cred);
this.queue('createFile', path, flag, mode, cred);
return this.openFileSync(path, flag, cred);
this._sync.createFileSync(path, flag, mode);
this.queue('createFile', path, flag, mode);
return this.openFileSync(path, flag);
}
openFileSync(path, flag, cred) {
openFileSync(path, flag) {
this.checkSync(path, 'openFile');
const file = this._sync.openFileSync(path, flag, cred);
const file = this._sync.openFileSync(path, flag);
const stats = file.statSync();

@@ -129,25 +128,25 @@ const buffer = new Uint8Array(stats.size);

}
unlinkSync(path, cred) {
unlinkSync(path) {
this.checkSync(path, 'unlinkSync');
this._sync.unlinkSync(path, cred);
this.queue('unlink', path, cred);
this._sync.unlinkSync(path);
this.queue('unlink', path);
}
rmdirSync(path, cred) {
rmdirSync(path) {
this.checkSync(path, 'rmdir');
this._sync.rmdirSync(path, cred);
this.queue('rmdir', path, cred);
this._sync.rmdirSync(path);
this.queue('rmdir', path);
}
mkdirSync(path, mode, cred) {
mkdirSync(path, mode) {
this.checkSync(path, 'mkdir');
this._sync.mkdirSync(path, mode, cred);
this.queue('mkdir', path, mode, cred);
this._sync.mkdirSync(path, mode);
this.queue('mkdir', path, mode);
}
readdirSync(path, cred) {
readdirSync(path) {
this.checkSync(path, 'readdir');
return this._sync.readdirSync(path, cred);
return this._sync.readdirSync(path);
}
linkSync(srcpath, dstpath, cred) {
linkSync(srcpath, dstpath) {
this.checkSync(srcpath, 'link');
this._sync.linkSync(srcpath, dstpath, cred);
this.queue('link', srcpath, dstpath, cred);
this._sync.linkSync(srcpath, dstpath);
this.queue('link', srcpath, dstpath);
}

@@ -159,5 +158,5 @@ syncSync(path, data, stats) {

}
existsSync(path, cred) {
existsSync(path) {
this.checkSync(path, 'exists');
return this._sync.existsSync(path, cred);
return this._sync.existsSync(path);
}

@@ -169,21 +168,12 @@ /**

this.checkSync(path, 'crossCopy');
const stats = await this.stat(path, rootCred);
if (stats.isDirectory()) {
if (path !== '/') {
const stats = await this.stat(path, rootCred);
this._sync.mkdirSync(path, stats.mode, stats.cred());
}
const files = await this.readdir(path, rootCred);
for (const file of files) {
await this.crossCopy(join(path, file));
}
}
else {
const stats = await this.stat(path);
if (!stats.isDirectory()) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const asyncFile = __addDisposableResource(env_1, await this.openFile(path, parseFlag('r'), rootCred), true);
const syncFile = __addDisposableResource(env_1, this._sync.createFileSync(path, parseFlag('w'), stats.mode, stats.cred()), false);
const asyncFile = __addDisposableResource(env_1, await this.openFile(path, parseFlag('r')), true);
const syncFile = __addDisposableResource(env_1, this._sync.createFileSync(path, parseFlag('w'), stats.mode), false);
const buffer = new Uint8Array(stats.size);
await asyncFile.read(buffer);
syncFile.writeSync(buffer, 0, stats.size);
return;
}

@@ -200,2 +190,10 @@ catch (e_1) {

}
if (path !== '/') {
const stats = await this.stat(path);
this._sync.mkdirSync(path, stats.mode);
}
const files = await this.readdir(path);
for (const file of files) {
await this.crossCopy(join(path, file));
}
}

@@ -202,0 +200,0 @@ /**

@@ -30,5 +30,5 @@ import type { FileSystem } from '../filesystem.js';

export declare function Mutexed<T extends new (...args: any[]) => FileSystem>(FS: T): Mixin<T, {
lock(path: string): Promise<MutexLock>;
lock(path: string, syscall: string): Promise<MutexLock>;
lockSync(path: string): MutexLock;
isLocked(path: string): boolean;
}>;

@@ -109,5 +109,13 @@ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {

*/
async lock(path) {
async lock(path, syscall) {
const previous = this.locks.get(path);
const lock = this.addLock(path);
const stack = new Error().stack;
setTimeout(() => {
if (lock.isLocked) {
const error = ErrnoError.With('EDEADLK', path, syscall);
error.stack += stack?.slice('Error'.length);
throw error;
}
}, 5000);
await previous?.done();

@@ -124,3 +132,3 @@ return lock;

// Non-null assertion: we already checked locks has path
throw ErrnoError.With('EBUSY', path, 'lockSync');
throw ErrnoError.With('EBUSY', path, 'lock');
}

@@ -137,8 +145,8 @@ return this.addLock(path);

/* eslint-disable @typescript-eslint/no-unused-vars */
async rename(oldPath, newPath, cred) {
async rename(oldPath, newPath) {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_1, await this.lock(oldPath), false);
const _ = __addDisposableResource(env_1, await this.lock(oldPath, 'rename'), false);
// @ts-expect-error 2513
await super.rename(oldPath, newPath, cred);
await super.rename(oldPath, newPath);
}

@@ -153,3 +161,3 @@ catch (e_1) {

}
renameSync(oldPath, newPath, cred) {
renameSync(oldPath, newPath) {
const env_2 = { stack: [], error: void 0, hasError: false };

@@ -159,3 +167,3 @@ try {

// @ts-expect-error 2513
return super.renameSync(oldPath, newPath, cred);
return super.renameSync(oldPath, newPath);
}

@@ -170,8 +178,8 @@ catch (e_2) {

}
async stat(path, cred) {
async stat(path) {
const env_3 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_3, await this.lock(path), false);
const _ = __addDisposableResource(env_3, await this.lock(path, 'stat'), false);
// @ts-expect-error 2513
return await super.stat(path, cred);
return await super.stat(path);
}

@@ -186,3 +194,3 @@ catch (e_3) {

}
statSync(path, cred) {
statSync(path) {
const env_4 = { stack: [], error: void 0, hasError: false };

@@ -192,3 +200,3 @@ try {

// @ts-expect-error 2513
return super.statSync(path, cred);
return super.statSync(path);
}

@@ -203,8 +211,8 @@ catch (e_4) {

}
async openFile(path, flag, cred) {
async openFile(path, flag) {
const env_5 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_5, await this.lock(path), false);
const _ = __addDisposableResource(env_5, await this.lock(path, 'openFile'), false);
// @ts-expect-error 2513
return await super.openFile(path, flag, cred);
return await super.openFile(path, flag);
}

@@ -219,3 +227,3 @@ catch (e_5) {

}
openFileSync(path, flag, cred) {
openFileSync(path, flag) {
const env_6 = { stack: [], error: void 0, hasError: false };

@@ -225,3 +233,3 @@ try {

// @ts-expect-error 2513
return super.openFileSync(path, flag, cred);
return super.openFileSync(path, flag);
}

@@ -236,8 +244,8 @@ catch (e_6) {

}
async createFile(path, flag, mode, cred) {
async createFile(path, flag, mode) {
const env_7 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_7, await this.lock(path), false);
const _ = __addDisposableResource(env_7, await this.lock(path, 'createFile'), false);
// @ts-expect-error 2513
return await super.createFile(path, flag, mode, cred);
return await super.createFile(path, flag, mode);
}

@@ -252,3 +260,3 @@ catch (e_7) {

}
createFileSync(path, flag, mode, cred) {
createFileSync(path, flag, mode) {
const env_8 = { stack: [], error: void 0, hasError: false };

@@ -258,3 +266,3 @@ try {

// @ts-expect-error 2513
return super.createFileSync(path, flag, mode, cred);
return super.createFileSync(path, flag, mode);
}

@@ -269,8 +277,8 @@ catch (e_8) {

}
async unlink(path, cred) {
async unlink(path) {
const env_9 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_9, await this.lock(path), false);
const _ = __addDisposableResource(env_9, await this.lock(path, 'unlink'), false);
// @ts-expect-error 2513
await super.unlink(path, cred);
await super.unlink(path);
}

@@ -285,3 +293,3 @@ catch (e_9) {

}
unlinkSync(path, cred) {
unlinkSync(path) {
const env_10 = { stack: [], error: void 0, hasError: false };

@@ -291,3 +299,3 @@ try {

// @ts-expect-error 2513
return super.unlinkSync(path, cred);
return super.unlinkSync(path);
}

@@ -302,8 +310,8 @@ catch (e_10) {

}
async rmdir(path, cred) {
async rmdir(path) {
const env_11 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_11, await this.lock(path), false);
const _ = __addDisposableResource(env_11, await this.lock(path, 'rmdir'), false);
// @ts-expect-error 2513
await super.rmdir(path, cred);
await super.rmdir(path);
}

@@ -318,3 +326,3 @@ catch (e_11) {

}
rmdirSync(path, cred) {
rmdirSync(path) {
const env_12 = { stack: [], error: void 0, hasError: false };

@@ -324,3 +332,3 @@ try {

// @ts-expect-error 2513
return super.rmdirSync(path, cred);
return super.rmdirSync(path);
}

@@ -335,8 +343,8 @@ catch (e_12) {

}
async mkdir(path, mode, cred) {
async mkdir(path, mode) {
const env_13 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_13, await this.lock(path), false);
const _ = __addDisposableResource(env_13, await this.lock(path, 'mkdir'), false);
// @ts-expect-error 2513
await super.mkdir(path, mode, cred);
await super.mkdir(path, mode);
}

@@ -351,3 +359,3 @@ catch (e_13) {

}
mkdirSync(path, mode, cred) {
mkdirSync(path, mode) {
const env_14 = { stack: [], error: void 0, hasError: false };

@@ -357,3 +365,3 @@ try {

// @ts-expect-error 2513
return super.mkdirSync(path, mode, cred);
return super.mkdirSync(path, mode);
}

@@ -368,8 +376,8 @@ catch (e_14) {

}
async readdir(path, cred) {
async readdir(path) {
const env_15 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_15, await this.lock(path), false);
const _ = __addDisposableResource(env_15, await this.lock(path, 'readdir'), false);
// @ts-expect-error 2513
return await super.readdir(path, cred);
return await super.readdir(path);
}

@@ -384,3 +392,3 @@ catch (e_15) {

}
readdirSync(path, cred) {
readdirSync(path) {
const env_16 = { stack: [], error: void 0, hasError: false };

@@ -390,3 +398,3 @@ try {

// @ts-expect-error 2513
return super.readdirSync(path, cred);
return super.readdirSync(path);
}

@@ -401,7 +409,7 @@ catch (e_16) {

}
async exists(path, cred) {
async exists(path) {
const env_17 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_17, await this.lock(path), false);
return await super.exists(path, cred);
const _ = __addDisposableResource(env_17, await this.lock(path, 'exists'), false);
return await super.exists(path);
}

@@ -416,7 +424,7 @@ catch (e_17) {

}
existsSync(path, cred) {
existsSync(path) {
const env_18 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_18, this.lockSync(path), false);
return super.existsSync(path, cred);
return super.existsSync(path);
}

@@ -431,8 +439,8 @@ catch (e_18) {

}
async link(srcpath, dstpath, cred) {
async link(srcpath, dstpath) {
const env_19 = { stack: [], error: void 0, hasError: false };
try {
const _ = __addDisposableResource(env_19, await this.lock(srcpath), false);
const _ = __addDisposableResource(env_19, await this.lock(srcpath, 'link'), false);
// @ts-expect-error 2513
await super.link(srcpath, dstpath, cred);
await super.link(srcpath, dstpath);
}

@@ -447,3 +455,3 @@ catch (e_19) {

}
linkSync(srcpath, dstpath, cred) {
linkSync(srcpath, dstpath) {
const env_20 = { stack: [], error: void 0, hasError: false };

@@ -453,3 +461,3 @@ try {

// @ts-expect-error 2513
return super.linkSync(srcpath, dstpath, cred);
return super.linkSync(srcpath, dstpath);
}

@@ -467,3 +475,3 @@ catch (e_20) {

try {
const _ = __addDisposableResource(env_21, await this.lock(path), false);
const _ = __addDisposableResource(env_21, await this.lock(path, 'sync'), false);
// @ts-expect-error 2513

@@ -470,0 +478,0 @@ await super.sync(path, data, stats);

@@ -1,2 +0,1 @@

import type { Cred } from '../cred.js';
import type { File } from '../file.js';

@@ -11,16 +10,16 @@ import type { FileSystem, FileSystemMetadata } from '../filesystem.js';

metadata(): FileSystemMetadata;
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
unlink(path: string, cred: Cred): Promise<void>;
unlinkSync(path: string, cred: Cred): void;
rmdir(path: string, cred: Cred): Promise<void>;
rmdirSync(path: string, cred: Cred): void;
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
mkdirSync(path: string, mode: number, cred: Cred): void;
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
rename(oldPath: string, newPath: string): Promise<void>;
renameSync(oldPath: string, newPath: string): void;
createFile(path: string, flag: string, mode: number): Promise<File>;
createFileSync(path: string, flag: string, mode: number): File;
unlink(path: string): Promise<void>;
unlinkSync(path: string): void;
rmdir(path: string): Promise<void>;
rmdirSync(path: string): void;
mkdir(path: string, mode: number): Promise<void>;
mkdirSync(path: string, mode: number): void;
link(srcpath: string, dstpath: string): Promise<void>;
linkSync(srcpath: string, dstpath: string): void;
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
}>;

@@ -12,36 +12,36 @@ import { Errno, ErrnoError } from '../error.js';

/* eslint-disable @typescript-eslint/no-unused-vars */
async rename(oldPath, newPath, cred) {
async rename(oldPath, newPath) {
throw new ErrnoError(Errno.EROFS);
}
renameSync(oldPath, newPath, cred) {
renameSync(oldPath, newPath) {
throw new ErrnoError(Errno.EROFS);
}
async createFile(path, flag, mode, cred) {
async createFile(path, flag, mode) {
throw new ErrnoError(Errno.EROFS);
}
createFileSync(path, flag, mode, cred) {
createFileSync(path, flag, mode) {
throw new ErrnoError(Errno.EROFS);
}
async unlink(path, cred) {
async unlink(path) {
throw new ErrnoError(Errno.EROFS);
}
unlinkSync(path, cred) {
unlinkSync(path) {
throw new ErrnoError(Errno.EROFS);
}
async rmdir(path, cred) {
async rmdir(path) {
throw new ErrnoError(Errno.EROFS);
}
rmdirSync(path, cred) {
rmdirSync(path) {
throw new ErrnoError(Errno.EROFS);
}
async mkdir(path, mode, cred) {
async mkdir(path, mode) {
throw new ErrnoError(Errno.EROFS);
}
mkdirSync(path, mode, cred) {
mkdirSync(path, mode) {
throw new ErrnoError(Errno.EROFS);
}
async link(srcpath, dstpath, cred) {
async link(srcpath, dstpath) {
throw new ErrnoError(Errno.EROFS);
}
linkSync(srcpath, dstpath, cred) {
linkSync(srcpath, dstpath) {
throw new ErrnoError(Errno.EROFS);

@@ -48,0 +48,0 @@ }

@@ -7,31 +7,31 @@ /**

class SyncFS extends FS {
async exists(path, cred) {
return this.existsSync(path, cred);
async exists(path) {
return this.existsSync(path);
}
async rename(oldPath, newPath, cred) {
return this.renameSync(oldPath, newPath, cred);
async rename(oldPath, newPath) {
return this.renameSync(oldPath, newPath);
}
async stat(path, cred) {
return this.statSync(path, cred);
async stat(path) {
return this.statSync(path);
}
async createFile(path, flag, mode, cred) {
return this.createFileSync(path, flag, mode, cred);
async createFile(path, flag, mode) {
return this.createFileSync(path, flag, mode);
}
async openFile(path, flag, cred) {
return this.openFileSync(path, flag, cred);
async openFile(path, flag) {
return this.openFileSync(path, flag);
}
async unlink(path, cred) {
return this.unlinkSync(path, cred);
async unlink(path) {
return this.unlinkSync(path);
}
async rmdir(path, cred) {
return this.rmdirSync(path, cred);
async rmdir(path) {
return this.rmdirSync(path);
}
async mkdir(path, mode, cred) {
return this.mkdirSync(path, mode, cred);
async mkdir(path, mode) {
return this.mkdirSync(path, mode);
}
async readdir(path, cred) {
return this.readdirSync(path, cred);
async readdir(path) {
return this.readdirSync(path);
}
async link(srcpath, dstpath, cred) {
return this.linkSync(srcpath, dstpath, cred);
async link(srcpath, dstpath) {
return this.linkSync(srcpath, dstpath);
}

@@ -38,0 +38,0 @@ async sync(path, data, stats) {

/// <reference types="node" resolution-mode="require"/>
import type * as Node from 'fs';
import type { Cred } from './cred.js';
import type { Credentials } from './credentials.js';
import { S_IFDIR, S_IFLNK, S_IFREG } from './emulation/constants.js';

@@ -153,3 +153,3 @@ /**

*/
hasAccess(mode: number, cred: Cred): boolean;
hasAccess(mode: number, cred: Credentials): boolean;
/**

@@ -159,3 +159,3 @@ * Convert the current stats object into a credentials object

*/
cred(uid?: number, gid?: number): Cred;
cred(uid?: number, gid?: number): Credentials;
/**

@@ -190,4 +190,2 @@ * Change the mode of the file. We use this helper function to prevent messing

* Stats with bigint
* @todo Implement with bigint instead of wrapping Stats
* @internal
*/

@@ -198,4 +196,13 @@ export declare class BigIntStats extends StatsCommon<bigint> implements Node.BigIntStats, StatsLike {

/**
* Determines if the file stats have changed by comparing relevant properties.
*
* @param left The previous stats.
* @param right The current stats.
* @returns `true` if stats have changed; otherwise, `false`.
* @internal
*/
export declare function isStatsEqual<T extends number | bigint>(left: StatsCommon<T>, right: StatsCommon<T>): boolean;
/**
* @internal
*/
export declare const ZenFsType = 525687744115;

@@ -202,0 +209,0 @@ /**

@@ -197,4 +197,2 @@ import { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRWXG, S_IRWXO, S_IRWXU } from './emulation/constants.js';

* Stats with bigint
* @todo Implement with bigint instead of wrapping Stats
* @internal
*/

@@ -208,4 +206,15 @@ export class BigIntStats extends StatsCommon {

/**
* Determines if the file stats have changed by comparing relevant properties.
*
* @param left The previous stats.
* @param right The current stats.
* @returns `true` if stats have changed; otherwise, `false`.
* @internal
*/
export function isStatsEqual(left, right) {
return left.size == right.size && +left.atime == +right.atime && +left.mtime == +right.mtime && +left.ctime == +right.ctime && left.mode == right.mode;
}
/**
* @internal
*/
export const ZenFsType = 0x7a656e6673; // 'z' 'e' 'n' 'f' 's'

@@ -212,0 +221,0 @@ /**

/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
import type * as fs from 'node:fs';
import type { OptionalTuple } from 'utilium';
import { type AbsolutePath } from './emulation/path.js';
import { ErrnoError } from './error.js';
import type { Cred } from './cred.js';
import { type AbsolutePath } from './emulation/path.js';
import type { FileSystem } from './filesystem.js';
import type * as fs from 'node:fs';
declare global {

@@ -17,3 +16,3 @@ function atob(data: string): string;

*/
export declare function mkdirpSync(path: string, mode: number, cred: Cred, fs: FileSystem): void;
export declare function mkdirpSync(path: string, mode: number, fs: FileSystem): void;
/**

@@ -20,0 +19,0 @@ * Calculates levenshtein distance.

@@ -1,3 +0,3 @@

import { ErrnoError, Errno } from './error.js';
import { dirname, resolve } from './emulation/path.js';
import { Errno, ErrnoError } from './error.js';
/**

@@ -7,6 +7,6 @@ * Synchronous recursive makedir.

*/
export function mkdirpSync(path, mode, cred, fs) {
if (!fs.existsSync(path, cred)) {
mkdirpSync(dirname(path), mode, cred, fs);
fs.mkdirSync(path, mode, cred);
export function mkdirpSync(path, mode, fs) {
if (!fs.existsSync(path)) {
mkdirpSync(dirname(path), mode, fs);
fs.mkdirSync(path, mode);
}

@@ -118,8 +118,3 @@ }

export function decodeDirListing(data) {
return JSON.parse(decode(data), (k, v) => {
if (k == '') {
return v;
}
return BigInt(v);
});
return JSON.parse(decode(data), (k, v) => (k == '' ? v : BigInt(v)));
}

@@ -131,8 +126,3 @@ /**

export function encodeDirListing(data) {
return encode(JSON.stringify(data, (k, v) => {
if (k == '') {
return v;
}
return v.toString();
}));
return encode(JSON.stringify(data, (k, v) => (k == '' ? v : v.toString())));
}

@@ -139,0 +129,0 @@ /**

{
"name": "@zenfs/core",
"version": "0.17.1",
"version": "0.18.0",
"description": "A filesystem, anywhere",

@@ -68,3 +68,3 @@ "main": "dist/index.js",

"readable-stream": "^4.5.2",
"utilium": "^0.4.0"
"utilium": ">=0.4.0"
},

@@ -71,0 +71,0 @@ "devDependencies": {

@@ -153,6 +153,5 @@ import type { RequiredKeys } from 'utilium';

*
* Individual options can recursively contain BackendConfig objects for
* option values that require file systems.
* Individual options can recursively contain BackendConfiguration objects for values that require file systems.
*
* The option object for each file system corresponds to that file system's option object passed to its `Create()` method.
* The configuration for each file system corresponds to that file system's option object passed to its `create()` method.
*/

@@ -159,0 +158,0 @@ export type BackendConfiguration<T extends Backend> = OptionsOf<T> & Partial<SharedConfig> & { backend: T };

@@ -123,6 +123,6 @@ import { Errno, ErrnoError } from '../error.js';

if (!stats) {
throw ErrnoError.With('ENOENT', path, 'preloadFile');
throw ErrnoError.With('ENOENT', path, 'preload');
}
if (!stats.isFile()) {
throw ErrnoError.With('EISDIR', path, 'preloadFile');
throw ErrnoError.With('EISDIR', path, 'preload');
}

@@ -129,0 +129,0 @@ stats.size = buffer.length;

/* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */
import { isJSON } from 'utilium';
import type { Cred } from '../cred.js';
import { basename, dirname } from '../emulation/path.js';
import { Errno, ErrnoError } from '../error.js';
import { NoSyncFile, flagToMode, isWriteable } from '../file.js';
import { NoSyncFile, isWriteable } from '../file.js';
import { FileSystem } from '../filesystem.js';

@@ -152,3 +151,3 @@ import { Readonly } from '../mixins/readonly.js';

public async openFile(path: string, flag: string, cred: Cred): Promise<NoSyncFile<this>> {
public async openFile(path: string, flag: string): Promise<NoSyncFile<this>> {
if (isWriteable(flag)) {

@@ -166,10 +165,6 @@ // You can't write to files on this file system.

if (!stats.hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : await this.getData(path, stats));
}
public openFileSync(path: string, flag: string, cred: Cred): NoSyncFile<this> {
public openFileSync(path: string, flag: string): NoSyncFile<this> {
if (isWriteable(flag)) {

@@ -187,6 +182,2 @@ // You can't write to files on this file system.

if (!stats.hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));

@@ -193,0 +184,0 @@ }

@@ -0,11 +1,9 @@

import { dirname } from '../emulation/path.js';
import { Errno, ErrnoError } from '../error.js';
import type { File } from '../file.js';
import { PreloadFile, parseFlag } from '../file.js';
import type { FileSystemMetadata } from '../filesystem.js';
import { FileSystem } from '../filesystem.js';
import { ErrnoError, Errno } from '../error.js';
import type { File } from '../file.js';
import { PreloadFile, parseFlag } from '../file.js';
import { Mutexed } from '../mixins/mutexed.js';
import { Stats } from '../stats.js';
import { Mutexed } from '../mixins/mutexed.js';
import { dirname } from '../emulation/path.js';
import type { Cred } from '../cred.js';
import { rootCred } from '../cred.js';
import { decode, encode } from '../utils.js';

@@ -80,6 +78,5 @@ import type { Backend } from './backend.js';

public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
const cred = stats.cred(0, 0);
await this.createParentDirectories(path, cred);
if (!(await this.writable.exists(path, cred))) {
await this.writable.createFile(path, 'w', 0o644, cred);
await this.createParentDirectories(path);
if (!(await this.writable.exists(path))) {
await this.writable.createFile(path, 'w', 0o644);
}

@@ -90,4 +87,3 @@ await this.writable.sync(path, data, stats);

public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
const cred = stats.cred(0, 0);
this.createParentDirectoriesSync(path, cred);
this.createParentDirectoriesSync(path);
this.writable.syncSync(path, data, stats);

@@ -107,3 +103,3 @@ }

try {
const file = await this.writable.openFile(deletionLogPath, parseFlag('r'), rootCred);
const file = await this.writable.openFile(deletionLogPath, parseFlag('r'));
const { size } = await file.stat();

@@ -125,9 +121,9 @@ const { buffer } = await file.read(new Uint8Array(size));

public async restoreDeletionLog(log: string, cred: Cred): Promise<void> {
public async restoreDeletionLog(log: string): Promise<void> {
this._deleteLog = log;
this._reparseDeletionLog();
await this.updateLog('', cred);
await this.updateLog('');
}
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
public async rename(oldPath: string, newPath: string): Promise<void> {
this.checkInitialized();

@@ -138,3 +134,3 @@ this.checkPath(oldPath);

try {
await this.writable.rename(oldPath, newPath, cred);
await this.writable.rename(oldPath, newPath);
} catch (e) {

@@ -147,3 +143,3 @@ if (this._deletedFiles.has(oldPath)) {

public renameSync(oldPath: string, newPath: string, cred: Cred): void {
public renameSync(oldPath: string, newPath: string): void {
this.checkInitialized();

@@ -154,3 +150,3 @@ this.checkPath(oldPath);

try {
this.writable.renameSync(oldPath, newPath, cred);
this.writable.renameSync(oldPath, newPath);
} catch (e) {

@@ -163,6 +159,6 @@ if (this._deletedFiles.has(oldPath)) {

public async stat(path: string, cred: Cred): Promise<Stats> {
public async stat(path: string): Promise<Stats> {
this.checkInitialized();
try {
return await this.writable.stat(path, cred);
return await this.writable.stat(path);
} catch (e) {

@@ -172,3 +168,3 @@ if (this._deletedFiles.has(path)) {

}
const oldStat = new Stats(await this.readable.stat(path, cred));
const oldStat = new Stats(await this.readable.stat(path));
// Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type

@@ -180,6 +176,6 @@ oldStat.mode |= 0o222;

public statSync(path: string, cred: Cred): Stats {
public statSync(path: string): Stats {
this.checkInitialized();
try {
return this.writable.statSync(path, cred);
return this.writable.statSync(path);
} catch (e) {

@@ -189,3 +185,3 @@ if (this._deletedFiles.has(path)) {

}
const oldStat = new Stats(this.readable.statSync(path, cred));
const oldStat = new Stats(this.readable.statSync(path));
// Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type.

@@ -197,8 +193,8 @@ oldStat.mode |= 0o222;

public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
if (await this.writable.exists(path, cred)) {
return this.writable.openFile(path, flag, cred);
public async openFile(path: string, flag: string): Promise<File> {
if (await this.writable.exists(path)) {
return this.writable.openFile(path, flag);
}
// Create an OverlayFile.
const file = await this.readable.openFile(path, parseFlag('r'), cred);
const file = await this.readable.openFile(path, parseFlag('r'));
const stats = new Stats(await file.stat());

@@ -209,8 +205,8 @@ const { buffer } = await file.read(new Uint8Array(stats.size));

public openFileSync(path: string, flag: string, cred: Cred): File {
if (this.writable.existsSync(path, cred)) {
return this.writable.openFileSync(path, flag, cred);
public openFileSync(path: string, flag: string): File {
if (this.writable.existsSync(path)) {
return this.writable.openFileSync(path, flag);
}
// Create an OverlayFile.
const file = this.readable.openFileSync(path, parseFlag('r'), cred);
const file = this.readable.openFileSync(path, parseFlag('r'));
const stats = new Stats(file.statSync());

@@ -222,72 +218,72 @@ const data = new Uint8Array(stats.size);

public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
public async createFile(path: string, flag: string, mode: number): Promise<File> {
this.checkInitialized();
await this.writable.createFile(path, flag, mode, cred);
return this.openFile(path, flag, cred);
await this.writable.createFile(path, flag, mode);
return this.openFile(path, flag);
}
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
public createFileSync(path: string, flag: string, mode: number): File {
this.checkInitialized();
this.writable.createFileSync(path, flag, mode, cred);
return this.openFileSync(path, flag, cred);
this.writable.createFileSync(path, flag, mode);
return this.openFileSync(path, flag);
}
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
public async link(srcpath: string, dstpath: string): Promise<void> {
this.checkInitialized();
await this.writable.link(srcpath, dstpath, cred);
await this.writable.link(srcpath, dstpath);
}
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
public linkSync(srcpath: string, dstpath: string): void {
this.checkInitialized();
this.writable.linkSync(srcpath, dstpath, cred);
this.writable.linkSync(srcpath, dstpath);
}
public async unlink(path: string, cred: Cred): Promise<void> {
public async unlink(path: string): Promise<void> {
this.checkInitialized();
this.checkPath(path);
if (!(await this.exists(path, cred))) {
if (!(await this.exists(path))) {
throw ErrnoError.With('ENOENT', path, 'unlink');
}
if (await this.writable.exists(path, cred)) {
await this.writable.unlink(path, cred);
if (await this.writable.exists(path)) {
await this.writable.unlink(path);
}
// if it still exists add to the delete log
if (await this.exists(path, cred)) {
await this.deletePath(path, cred);
if (await this.exists(path)) {
await this.deletePath(path);
}
}
public unlinkSync(path: string, cred: Cred): void {
public unlinkSync(path: string): void {
this.checkInitialized();
this.checkPath(path);
if (!this.existsSync(path, cred)) {
if (!this.existsSync(path)) {
throw ErrnoError.With('ENOENT', path, 'unlink');
}
if (this.writable.existsSync(path, cred)) {
this.writable.unlinkSync(path, cred);
if (this.writable.existsSync(path)) {
this.writable.unlinkSync(path);
}
// if it still exists add to the delete log
if (this.existsSync(path, cred)) {
void this.deletePath(path, cred);
if (this.existsSync(path)) {
void this.deletePath(path);
}
}
public async rmdir(path: string, cred: Cred): Promise<void> {
public async rmdir(path: string): Promise<void> {
this.checkInitialized();
if (!(await this.exists(path, cred))) {
if (!(await this.exists(path))) {
throw ErrnoError.With('ENOENT', path, 'rmdir');
}
if (await this.writable.exists(path, cred)) {
await this.writable.rmdir(path, cred);
if (await this.writable.exists(path)) {
await this.writable.rmdir(path);
}
if (await this.exists(path, cred)) {
if (await this.exists(path)) {
// Check if directory is empty.
if ((await this.readdir(path, cred)).length > 0) {
if ((await this.readdir(path)).length > 0) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
} else {
await this.deletePath(path, cred);
await this.deletePath(path);
}

@@ -297,16 +293,16 @@ }

public rmdirSync(path: string, cred: Cred): void {
public rmdirSync(path: string): void {
this.checkInitialized();
if (!this.existsSync(path, cred)) {
if (!this.existsSync(path)) {
throw ErrnoError.With('ENOENT', path, 'rmdir');
}
if (this.writable.existsSync(path, cred)) {
this.writable.rmdirSync(path, cred);
if (this.writable.existsSync(path)) {
this.writable.rmdirSync(path);
}
if (this.existsSync(path, cred)) {
if (this.existsSync(path)) {
// Check if directory is empty.
if (this.readdirSync(path, cred).length > 0) {
if (this.readdirSync(path).length > 0) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
} else {
void this.deletePath(path, cred);
void this.deletePath(path);
}

@@ -316,25 +312,25 @@ }

public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
public async mkdir(path: string, mode: number): Promise<void> {
this.checkInitialized();
if (await this.exists(path, cred)) {
if (await this.exists(path)) {
throw ErrnoError.With('EEXIST', path, 'mkdir');
}
// The below will throw should any of the parent directories fail to exist on _writable.
await this.createParentDirectories(path, cred);
await this.writable.mkdir(path, mode, cred);
await this.createParentDirectories(path);
await this.writable.mkdir(path, mode);
}
public mkdirSync(path: string, mode: number, cred: Cred): void {
public mkdirSync(path: string, mode: number): void {
this.checkInitialized();
if (this.existsSync(path, cred)) {
if (this.existsSync(path)) {
throw ErrnoError.With('EEXIST', path, 'mkdir');
}
// The below will throw should any of the parent directories fail to exist on _writable.
this.createParentDirectoriesSync(path, cred);
this.writable.mkdirSync(path, mode, cred);
this.createParentDirectoriesSync(path);
this.writable.mkdirSync(path, mode);
}
public async readdir(path: string, cred: Cred): Promise<string[]> {
public async readdir(path: string): Promise<string[]> {
this.checkInitialized();
const dirStats = await this.stat(path, cred);
const dirStats = await this.stat(path);
if (!dirStats.isDirectory()) {

@@ -347,3 +343,3 @@ throw ErrnoError.With('ENOTDIR', path, 'readdir');

try {
contents.push(...(await this.writable.readdir(path, cred)));
contents.push(...(await this.writable.readdir(path)));
} catch (e) {

@@ -353,3 +349,3 @@ // NOP.

try {
contents.push(...(await this.readable.readdir(path, cred)).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
contents.push(...(await this.readable.readdir(path)).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
} catch (e) {

@@ -366,5 +362,5 @@ // NOP.

public readdirSync(path: string, cred: Cred): string[] {
public readdirSync(path: string): string[] {
this.checkInitialized();
const dirStats = this.statSync(path, cred);
const dirStats = this.statSync(path);
if (!dirStats.isDirectory()) {

@@ -377,3 +373,3 @@ throw ErrnoError.With('ENOTDIR', path, 'readdir');

try {
contents = contents.concat(this.writable.readdirSync(path, cred));
contents = contents.concat(this.writable.readdirSync(path));
} catch (e) {

@@ -383,3 +379,3 @@ // NOP.

try {
contents = contents.concat(this.readable.readdirSync(path, cred).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
contents = contents.concat(this.readable.readdirSync(path).filter((fPath: string) => !this._deletedFiles.has(`${path}/${fPath}`)));
} catch (e) {

@@ -396,8 +392,8 @@ // NOP.

private async deletePath(path: string, cred: Cred): Promise<void> {
private async deletePath(path: string): Promise<void> {
this._deletedFiles.add(path);
await this.updateLog(`d${path}\n`, cred);
await this.updateLog(`d${path}\n`);
}
private async updateLog(addition: string, cred: Cred) {
private async updateLog(addition: string) {
this._deleteLog += addition;

@@ -409,3 +405,3 @@ if (this._deleteLogUpdatePending) {

this._deleteLogUpdatePending = true;
const log = await this.writable.openFile(deletionLogPath, parseFlag('w'), cred);
const log = await this.writable.openFile(deletionLogPath, parseFlag('w'));
try {

@@ -415,3 +411,3 @@ await log.write(encode(this._deleteLog));

this._deleteLogUpdateNeeded = false;
await this.updateLog('', cred);
await this.updateLog('');
}

@@ -462,6 +458,6 @@ } catch (e) {

*/
private createParentDirectoriesSync(path: string, cred: Cred): void {
private createParentDirectoriesSync(path: string): void {
let parent = dirname(path),
toCreate: string[] = [];
while (!this.writable.existsSync(parent, cred)) {
while (!this.writable.existsSync(parent)) {
toCreate.push(parent);

@@ -473,10 +469,10 @@ parent = dirname(parent);

for (const p of toCreate) {
this.writable.mkdirSync(p, this.statSync(p, cred).mode, cred);
this.writable.mkdirSync(p, this.statSync(p).mode);
}
}
private async createParentDirectories(path: string, cred: Cred): Promise<void> {
private async createParentDirectories(path: string): Promise<void> {
let parent = dirname(path),
toCreate: string[] = [];
while (!(await this.writable.exists(parent, cred))) {
while (!(await this.writable.exists(parent))) {
toCreate.push(parent);

@@ -488,4 +484,4 @@ parent = dirname(parent);

for (const p of toCreate) {
const stats = await this.stat(p, cred);
await this.writable.mkdir(p, stats.mode, cred);
const stats = await this.stat(p);
await this.writable.mkdir(p, stats.mode);
}

@@ -499,20 +495,20 @@ }

*/
private operateOnWritable(path: string, cred: Cred): void {
if (!this.existsSync(path, cred)) {
private operateOnWritable(path: string): void {
if (!this.existsSync(path)) {
throw ErrnoError.With('ENOENT', path, 'operateOnWriteable');
}
if (!this.writable.existsSync(path, cred)) {
if (!this.writable.existsSync(path)) {
// File is on readable storage. Copy to writable storage before
// changing its mode.
this.copyToWritableSync(path, cred);
this.copyToWritableSync(path);
}
}
private async operateOnWritableAsync(path: string, cred: Cred): Promise<void> {
if (!(await this.exists(path, cred))) {
private async operateOnWritableAsync(path: string): Promise<void> {
if (!(await this.exists(path))) {
throw ErrnoError.With('ENOENT', path, 'operateOnWritable');
}
if (!(await this.writable.exists(path, cred))) {
return this.copyToWritable(path, cred);
if (!(await this.writable.exists(path))) {
return this.copyToWritable(path);
}

@@ -525,6 +521,6 @@ }

*/
private copyToWritableSync(path: string, cred: Cred): void {
const stats = this.statSync(path, cred);
private copyToWritableSync(path: string): void {
const stats = this.statSync(path);
if (stats.isDirectory()) {
this.writable.mkdirSync(path, stats.mode, cred);
this.writable.mkdirSync(path, stats.mode);
return;

@@ -534,6 +530,6 @@ }

const data = new Uint8Array(stats.size);
const readable = this.readable.openFileSync(path, parseFlag('r'), cred);
const readable = this.readable.openFileSync(path, parseFlag('r'));
readable.readSync(data);
readable.closeSync();
const writable = this.writable.openFileSync(path, parseFlag('w'), cred);
const writable = this.writable.openFileSync(path, parseFlag('w'));
writable.writeSync(data);

@@ -543,6 +539,6 @@ writable.closeSync();

private async copyToWritable(path: string, cred: Cred): Promise<void> {
const stats = await this.stat(path, cred);
private async copyToWritable(path: string): Promise<void> {
const stats = await this.stat(path);
if (stats.isDirectory()) {
await this.writable.mkdir(path, stats.mode, cred);
await this.writable.mkdir(path, stats.mode);
return;

@@ -552,6 +548,6 @@ }

const data = new Uint8Array(stats.size);
const readable = await this.readable.openFile(path, parseFlag('r'), cred);
const readable = await this.readable.openFile(path, parseFlag('r'));
await readable.read(data);
await readable.close();
const writable = await this.writable.openFile(path, parseFlag('w'), cred);
const writable = await this.writable.openFile(path, parseFlag('w'));
await writable.write(data);

@@ -558,0 +554,0 @@ await writable.close();

/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FileReadResult } from 'node:fs/promises';
import type { ExtractProperties } from 'utilium';
import type { Cred } from '../../cred.js';
import { resolveMountConfig, type MountConfiguration } from '../../config.js';
import { Errno, ErrnoError } from '../../error.js';

@@ -10,6 +10,5 @@ import { File } from '../../file.js';

import { Stats, type FileType } from '../../stats.js';
import type { Backend, FilesystemOf } from '../backend.js';
import { InMemory } from '../memory.js';
import type { Backend, FilesystemOf } from '../backend.js';
import * as RPC from './rpc.js';
import { type MountConfiguration, resolveMountConfig } from '../../config.js';

@@ -35,3 +34,3 @@ type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;

public rpc<const T extends FileMethod & string>(method: T, ...args: Parameters<FileMethods[T]>): Promise<Awaited<ReturnType<FileMethods[T]>>> {
public rpc<const T extends FileMethod>(method: T, ...args: Parameters<FileMethods[T]>): Promise<Awaited<ReturnType<FileMethods[T]>>> {
return RPC.request<FileRequest<T>, Awaited<ReturnType<FileMethods[T]>>>(

@@ -52,4 +51,4 @@ {

public stat(): Promise<Stats> {
return this.rpc('stat');
public async stat(): Promise<Stats> {
return new Stats(await this.rpc('stat'));
}

@@ -190,8 +189,8 @@

public rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
return this.rpc('rename', oldPath, newPath, cred);
public rename(oldPath: string, newPath: string): Promise<void> {
return this.rpc('rename', oldPath, newPath);
}
public async stat(path: string, cred: Cred): Promise<Stats> {
return new Stats(await this.rpc('stat', path, cred));
public async stat(path: string): Promise<Stats> {
return new Stats(await this.rpc('stat', path));
}

@@ -202,25 +201,25 @@

}
public openFile(path: string, flag: string, cred: Cred): Promise<File> {
return this.rpc('openFile', path, flag, cred);
public openFile(path: string, flag: string): Promise<File> {
return this.rpc('openFile', path, flag);
}
public createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
return this.rpc('createFile', path, flag, mode, cred);
public createFile(path: string, flag: string, mode: number): Promise<File> {
return this.rpc('createFile', path, flag, mode);
}
public unlink(path: string, cred: Cred): Promise<void> {
return this.rpc('unlink', path, cred);
public unlink(path: string): Promise<void> {
return this.rpc('unlink', path);
}
public rmdir(path: string, cred: Cred): Promise<void> {
return this.rpc('rmdir', path, cred);
public rmdir(path: string): Promise<void> {
return this.rpc('rmdir', path);
}
public mkdir(path: string, mode: number, cred: Cred): Promise<void> {
return this.rpc('mkdir', path, mode, cred);
public mkdir(path: string, mode: number): Promise<void> {
return this.rpc('mkdir', path, mode);
}
public readdir(path: string, cred: Cred): Promise<string[]> {
return this.rpc('readdir', path, cred);
public readdir(path: string): Promise<string[]> {
return this.rpc('readdir', path);
}
public exists(path: string, cred: Cred): Promise<boolean> {
return this.rpc('exists', path, cred);
public exists(path: string): Promise<boolean> {
return this.rpc('exists', path);
}
public link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
return this.rpc('link', srcpath, dstpath, cred);
public link(srcpath: string, dstpath: string): Promise<void> {
return this.rpc('link', srcpath, dstpath);
}

@@ -227,0 +226,0 @@ }

@@ -1,6 +0,6 @@

import type { Cred } from '../../cred.js';
import { R_OK, S_IFDIR, S_IFREG, W_OK } from '../../emulation/constants.js';
import { credentials } from '../../credentials.js';
import { S_IFDIR, S_IFREG } from '../../emulation/constants.js';
import { basename, dirname, join, resolve } from '../../emulation/path.js';
import { Errno, ErrnoError } from '../../error.js';
import { PreloadFile, flagToMode } from '../../file.js';
import { PreloadFile } from '../../file.js';
import { FileSystem, type FileSystemMetadata } from '../../filesystem.js';

@@ -67,3 +67,3 @@ import { type Ino, Inode, randomIno, rootIno } from '../../inode.js';

*/
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
public async rename(oldPath: string, newPath: string): Promise<void> {
await using tx = this.store.transaction();

@@ -78,6 +78,2 @@ const oldParent = dirname(oldPath),

if (!oldDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
if (!oldDirList[oldName]) {

@@ -110,11 +106,9 @@ throw ErrnoError.With('ENOENT', oldPath, 'rename');

if (newDirList[newName]) {
// If it's a file, delete it.
// If it's a file, delete it, if it's a directory, throw a permissions error.
const newNameNode = await this.getINode(tx, newDirList[newName], newPath);
if (newNameNode.toStats().isFile()) {
await tx.remove(newNameNode.ino);
await tx.remove(newDirList[newName]);
} else {
// If it's a directory, throw a permissions error.
if (!newNameNode.toStats().isFile()) {
throw ErrnoError.With('EPERM', newPath, 'rename');
}
await tx.remove(newNameNode.ino);
await tx.remove(newDirList[newName]);
}

@@ -128,3 +122,3 @@ newDirList[newName] = nodeId;

public renameSync(oldPath: string, newPath: string, cred: Cred): void {
public renameSync(oldPath: string, newPath: string): void {
using tx = this.store.transaction();

@@ -139,6 +133,2 @@ const oldParent = dirname(oldPath),

if (!oldDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
if (!oldDirList[oldName]) {

@@ -171,11 +161,9 @@ throw ErrnoError.With('ENOENT', oldPath, 'rename');

if (newDirList[newName]) {
// If it's a file, delete it.
// If it's a file, delete it, if it's a directory, throw a permissions error.
const newNameNode = this.getINodeSync(tx, newDirList[newName], newPath);
if (newNameNode.toStats().isFile()) {
tx.removeSync(newNameNode.ino);
tx.removeSync(newDirList[newName]);
} else {
// If it's a directory, throw a permissions error.
if (!newNameNode.toStats().isFile()) {
throw ErrnoError.With('EPERM', newPath, 'rename');
}
tx.removeSync(newNameNode.ino);
tx.removeSync(newDirList[newName]);
}

@@ -190,3 +178,3 @@ newDirList[newName] = ino;

public async stat(path: string, cred: Cred): Promise<Stats> {
public async stat(path: string): Promise<Stats> {
await using tx = this.store.transaction();

@@ -197,36 +185,25 @@ const inode = await this.findINode(tx, path);

}
const stats = inode.toStats();
if (!stats.hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return stats;
return inode.toStats();
}
public statSync(path: string, cred: Cred): Stats {
public statSync(path: string): Stats {
using tx = this.store.transaction();
// Get the inode to the item, convert it into a Stats object.
const stats = this.findINodeSync(tx, path).toStats();
if (!stats.hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return stats;
return this.findINodeSync(tx, path).toStats();
}
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<PreloadFile<this>> {
const node = await this.commitNew(path, S_IFREG, mode, cred, new Uint8Array(0));
public async createFile(path: string, flag: string, mode: number): Promise<PreloadFile<this>> {
const node = await this.commitNew(path, S_IFREG, mode, new Uint8Array(0));
return new PreloadFile(this, path, flag, node.toStats(), new Uint8Array(0));
}
public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
this.commitNewSync(path, S_IFREG, mode, cred);
return this.openFileSync(path, flag, cred);
public createFileSync(path: string, flag: string, mode: number): PreloadFile<this> {
this.commitNewSync(path, S_IFREG, mode);
return this.openFileSync(path, flag);
}
public async openFile(path: string, flag: string, cred: Cred): Promise<PreloadFile<this>> {
public async openFile(path: string, flag: string): Promise<PreloadFile<this>> {
await using tx = this.store.transaction();
const node = await this.findINode(tx, path),
data = await tx.get(node.ino);
if (!node.toStats().hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
if (!data) {

@@ -238,9 +215,6 @@ throw ErrnoError.With('ENOENT', path, 'openFile');

public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
public openFileSync(path: string, flag: string): PreloadFile<this> {
using tx = this.store.transaction();
const node = this.findINodeSync(tx, path),
data = tx.getSync(node.ino);
if (!node.toStats().hasAccess(flagToMode(flag), cred)) {
throw ErrnoError.With('EACCES', path, 'openFile');
}
if (!data) {

@@ -252,51 +226,43 @@ throw ErrnoError.With('ENOENT', path, 'openFile');

public async unlink(path: string, cred: Cred): Promise<void> {
return this.remove(path, false, cred);
public async unlink(path: string): Promise<void> {
return this.remove(path, false);
}
public unlinkSync(path: string, cred: Cred): void {
this.removeSync(path, false, cred);
public unlinkSync(path: string): void {
this.removeSync(path, false);
}
public async rmdir(path: string, cred: Cred): Promise<void> {
public async rmdir(path: string): Promise<void> {
// Check first if directory is empty.
const list = await this.readdir(path, cred);
if (list.length > 0) {
if ((await this.readdir(path)).length) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
}
await this.remove(path, true, cred);
await this.remove(path, true);
}
public rmdirSync(path: string, cred: Cred): void {
public rmdirSync(path: string): void {
// Check first if directory is empty.
if (this.readdirSync(path, cred).length > 0) {
if (this.readdirSync(path).length) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
} else {
this.removeSync(path, true, cred);
}
this.removeSync(path, true);
}
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
await this.commitNew(path, S_IFDIR, mode, cred, encode('{}'));
public async mkdir(path: string, mode: number): Promise<void> {
await this.commitNew(path, S_IFDIR, mode, encode('{}'));
}
public mkdirSync(path: string, mode: number, cred: Cred): void {
this.commitNewSync(path, S_IFDIR, mode, cred, encode('{}'));
public mkdirSync(path: string, mode: number): void {
this.commitNewSync(path, S_IFDIR, mode, encode('{}'));
}
public async readdir(path: string, cred: Cred): Promise<string[]> {
public async readdir(path: string): Promise<string[]> {
await using tx = this.store.transaction();
const node = await this.findINode(tx, path);
if (!node.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'readdur');
}
return Object.keys(await this.getDirListing(tx, node, path));
}
public readdirSync(path: string, cred: Cred): string[] {
public readdirSync(path: string): string[] {
using tx = this.store.transaction();
const node = this.findINodeSync(tx, path);
if (!node.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'readdir');
}
return Object.keys(this.getDirListingSync(tx, node, path));

@@ -347,62 +313,35 @@ }

public async link(existing: string, newpath: string, cred: Cred): Promise<void> {
public async link(target: string, link: string): Promise<void> {
await using tx = this.store.transaction();
const existingDir: string = dirname(existing),
existingDirNode = await this.findINode(tx, existingDir);
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', existingDir, 'link');
}
const newDir: string = dirname(newpath),
const newDir: string = dirname(link),
newDirNode = await this.findINode(tx, newDir),
newListing = await this.getDirListing(tx, newDirNode, newDir);
listing = await this.getDirListing(tx, newDirNode, newDir);
if (!newDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newDir, 'link');
}
const ino = await this._findINode(tx, dirname(target), basename(target));
const node = await this.getINode(tx, ino, target);
const ino = await this._findINode(tx, existingDir, basename(existing));
const node = await this.getINode(tx, ino, existing);
if (!node.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newpath, 'link');
}
node.nlink++;
newListing[basename(newpath)] = ino;
listing[basename(link)] = ino;
tx.setSync(ino, node.data);
tx.setSync(newDirNode.ino, encodeDirListing(newListing));
tx.setSync(newDirNode.ino, encodeDirListing(listing));
tx.commitSync();
}
public linkSync(existing: string, newpath: string, cred: Cred): void {
public linkSync(target: string, link: string): void {
using tx = this.store.transaction();
const existingDir: string = dirname(existing),
existingDirNode = this.findINodeSync(tx, existingDir);
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) {
throw ErrnoError.With('EACCES', existingDir, 'link');
}
const newDir: string = dirname(newpath),
const newDir: string = dirname(link),
newDirNode = this.findINodeSync(tx, newDir),
newListing = this.getDirListingSync(tx, newDirNode, newDir);
listing = this.getDirListingSync(tx, newDirNode, newDir);
if (!newDirNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newDir, 'link');
}
const ino = this._findINodeSync(tx, dirname(target), basename(target));
const node = this.getINodeSync(tx, ino, target);
const ino = this._findINodeSync(tx, existingDir, basename(existing));
const node = this.getINodeSync(tx, ino, existing);
if (!node.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', newpath, 'link');
}
node.nlink++;
newListing[basename(newpath)] = ino;
listing[basename(link)] = ino;
tx.setSync(ino, node.data);
tx.setSync(newDirNode.ino, encodeDirListing(newListing));
tx.setSync(newDirNode.ino, encodeDirListing(listing));
tx.commitSync();

@@ -630,3 +569,3 @@ }

*/
private async commitNew(path: string, type: FileType, mode: number, cred: Cred, data: Uint8Array): Promise<Inode> {
private async commitNew(path: string, type: FileType, mode: number, data: Uint8Array): Promise<Inode> {
await using tx = this.store.transaction();

@@ -636,7 +575,2 @@ const parentPath = dirname(path),

//Check that the creater has correct access
if (!parent.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'commitNewFile');
}
const fname = basename(path),

@@ -651,3 +585,3 @@ listing = await this.getDirListing(tx, parent, parentPath);

if (path === '/') {
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}

@@ -658,3 +592,3 @@

await tx.abort();
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}

@@ -666,4 +600,4 @@

inode.mode = mode | type;
inode.uid = cred.uid;
inode.gid = cred.gid;
inode.uid = credentials.uid;
inode.gid = credentials.gid;
inode.size = data.length;

@@ -687,3 +621,3 @@

*/
protected commitNewSync(path: string, type: FileType, mode: number, cred: Cred, data: Uint8Array = new Uint8Array()): Inode {
protected commitNewSync(path: string, type: FileType, mode: number, data: Uint8Array = new Uint8Array()): Inode {
using tx = this.store.transaction();

@@ -693,7 +627,2 @@ const parentPath = dirname(path),

//Check that the creater has correct access
if (!parent.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'commitNewFile');
}
const fname = basename(path),

@@ -708,3 +637,3 @@ listing = this.getDirListingSync(tx, parent, parentPath);

if (path === '/') {
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}

@@ -714,3 +643,3 @@

if (listing[fname]) {
throw ErrnoError.With('EEXIST', path, 'commitNewFile');
throw ErrnoError.With('EEXIST', path, 'commitNew');
}

@@ -723,4 +652,4 @@

node.mode = mode | type;
node.uid = cred.uid;
node.gid = cred.gid;
node.uid = credentials.uid;
node.gid = credentials.gid;
// Update and commit parent directory listing.

@@ -739,3 +668,3 @@ listing[fname] = this.addNewSync(tx, node.data, path);

*/
private async remove(path: string, isDir: boolean, cred: Cred): Promise<void> {
private async remove(path: string, isDir: boolean): Promise<void> {
await using tx = this.store.transaction();

@@ -748,3 +677,3 @@ const parent: string = dirname(path),

if (!listing[fileName]) {
throw ErrnoError.With('ENOENT', path, 'removeEntry');
throw ErrnoError.With('ENOENT', path, 'remove');
}

@@ -757,6 +686,2 @@

if (!fileNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'removeEntry');
}
// Remove from directory listing of parent.

@@ -766,7 +691,7 @@ delete listing[fileName];

if (!isDir && fileNode.toStats().isDirectory()) {
throw ErrnoError.With('EISDIR', path, 'removeEntry');
throw ErrnoError.With('EISDIR', path, 'remove');
}
if (isDir && !fileNode.toStats().isDirectory()) {
throw ErrnoError.With('ENOTDIR', path, 'removeEntry');
throw ErrnoError.With('ENOTDIR', path, 'remove');
}

@@ -792,3 +717,3 @@

*/
protected removeSync(path: string, isDir: boolean, cred: Cred): void {
protected removeSync(path: string, isDir: boolean): void {
using tx = this.store.transaction();

@@ -802,3 +727,3 @@ const parent: string = dirname(path),

if (!fileIno) {
throw ErrnoError.With('ENOENT', path, 'removeEntry');
throw ErrnoError.With('ENOENT', path, 'remove');
}

@@ -809,6 +734,2 @@

if (!fileNode.toStats().hasAccess(W_OK, cred)) {
throw ErrnoError.With('EACCES', path, 'removeEntry');
}
// Remove from directory listing of parent.

@@ -818,7 +739,7 @@ delete listing[fileName];

if (!isDir && fileNode.toStats().isDirectory()) {
throw ErrnoError.With('EISDIR', path, 'removeEntry');
throw ErrnoError.With('EISDIR', path, 'remove');
}
if (isDir && !fileNode.toStats().isDirectory()) {
throw ErrnoError.With('ENOTDIR', path, 'removeEntry');
throw ErrnoError.With('ENOTDIR', path, 'remove');
}

@@ -825,0 +746,0 @@

import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
import { credentials } from './credentials.js';
import * as fs from './emulation/index.js';
import type { AbsolutePath } from './emulation/path.js';
import { setCred, type MountObject } from './emulation/shared.js';
import type { MountObject } from './emulation/shared.js';
import { Errno, ErrnoError } from './error.js';

@@ -110,3 +111,3 @@ import { FileSystem } from './filesystem.js';

setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
Object.assign(credentials, { uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });

@@ -113,0 +114,0 @@ if (!config.mounts) {

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

import { BigIntStats, type Stats } from '../stats.js';
import { normalizeMode, type Callback } from '../utils.js';
import { normalizeMode, normalizePath, type Callback } from '../utils.js';
import { R_OK } from './constants.js';

@@ -14,3 +14,3 @@ import type { Dirent } from './dir.js';

import { ReadStream, WriteStream } from './streams.js';
import { FSWatcher } from './watchers.js';
import { FSWatcher, StatWatcher } from './watchers.js';

@@ -678,4 +678,14 @@ const nop = () => {};

const statWatchers: Map<string, { watcher: StatWatcher; listeners: Set<(curr: Stats, prev: Stats) => void> }> = new Map();
/**
* @todo Implement
* Watch for changes on a file. The callback listener will be called each time the file is accessed.
*
* The `options` argument may be omitted. If provided, it should be an object with a `persistent` boolean and an `interval` number specifying the polling interval in milliseconds.
*
* When a change is detected, the `listener` callback is called with the current and previous `Stats` objects.
*
* @param path The path to the file to watch.
* @param options Optional options object specifying `persistent` and `interval`.
* @param listener The callback listener to be called when the file changes.
*/

@@ -687,5 +697,34 @@ export function watchFile(path: fs.PathLike, listener: (curr: Stats, prev: Stats) => void): void;

optsListener: { persistent?: boolean; interval?: number } | ((curr: Stats, prev: Stats) => void),
listener: (curr: Stats, prev: Stats) => void = nop
listener?: (curr: Stats, prev: Stats) => void
): void {
throw ErrnoError.With('ENOSYS', path.toString(), 'watchFile');
const normalizedPath = normalizePath(path.toString());
const options: { persistent?: boolean; interval?: number } = typeof optsListener != 'function' ? optsListener : {};
if (typeof optsListener === 'function') {
listener = optsListener;
}
if (!listener) {
throw new ErrnoError(Errno.EINVAL, 'No listener specified', path.toString(), 'watchFile');
}
if (statWatchers.has(normalizedPath)) {
const entry = statWatchers.get(normalizedPath);
if (entry) {
entry.listeners.add(listener);
}
return;
}
const watcher = new StatWatcher(normalizedPath, options);
watcher.on('change', (curr: Stats, prev: Stats) => {
const entry = statWatchers.get(normalizedPath);
if (!entry) {
return;
}
for (const listener of entry.listeners) {
listener(curr, prev);
}
});
statWatchers.set(normalizedPath, { watcher, listeners: new Set() });
}

@@ -695,17 +734,34 @@ watchFile satisfies Omit<typeof fs.watchFile, '__promisify__'>;

/**
* @todo Implement
* Stop watching for changes on a file.
*
* If the `listener` is specified, only that particular listener is removed.
* If no `listener` is specified, all listeners are removed, and the file is no longer watched.
*
* @param path The path to the file to stop watching.
* @param listener Optional listener to remove.
*/
export function unwatchFile(path: fs.PathLike, listener: (curr: Stats, prev: Stats) => void = nop): void {
throw ErrnoError.With('ENOSYS', path.toString(), 'unwatchFile');
const normalizedPath = normalizePath(path.toString());
const entry = statWatchers.get(normalizedPath);
if (entry) {
if (listener && listener !== nop) {
entry.listeners.delete(listener);
} else {
// If no listener is specified, remove all listeners
entry.listeners.clear();
}
if (entry.listeners.size === 0) {
// No more listeners, stop the watcher
entry.watcher.stop();
statWatchers.delete(normalizedPath);
}
}
}
unwatchFile satisfies Omit<typeof fs.unwatchFile, '__promisify__'>;
export function watch(path: fs.PathLike, listener?: (event: string, filename: string) => any): fs.FSWatcher;
export function watch(path: fs.PathLike, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): fs.FSWatcher;
export function watch(
path: fs.PathLike,
options?: fs.WatchOptions | ((event: string, filename: string) => any),
listener?: (event: string, filename: string) => any
): fs.FSWatcher {
const watcher = new FSWatcher<string>(typeof options == 'object' ? options : {});
export function watch(path: fs.PathLike, listener?: (event: string, filename: string) => any): FSWatcher;
export function watch(path: fs.PathLike, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): FSWatcher;
export function watch(path: fs.PathLike, options?: fs.WatchOptions | ((event: string, filename: string) => any), listener?: (event: string, filename: string) => any): FSWatcher {
const watcher = new FSWatcher<string>(normalizePath(path), typeof options == 'object' ? options : {});
listener = typeof options == 'function' ? options : listener;

@@ -778,3 +834,3 @@ watcher.on('change', listener || nop);

.then(() => callback(error))
.catch(callback);
.catch(nop);
},

@@ -781,0 +837,0 @@ });

@@ -1,8 +0,8 @@

import type { Dirent as _Dirent, Dir as _Dir } from 'fs';
import type { Dir as _Dir, Dirent as _Dirent } from 'fs';
import { Errno, ErrnoError } from '../error.js';
import type { Stats } from '../stats.js';
import type { Callback } from '../utils.js';
import type { Stats } from '../stats.js';
import { basename } from './path.js';
import { readdir } from './promises.js';
import { ErrnoError, Errno } from '../error.js';
import { readdirSync } from './sync.js';
import { basename } from './path.js';

@@ -58,11 +58,4 @@ export class Dirent implements _Dirent {

protected _entries: Dirent[] = [];
protected _entries?: Dirent[];
/**
* @internal
*/
public async _loadEntries() {
this._entries ??= await readdir(this.path, { withFileTypes: true });
}
public constructor(public readonly path: string) {}

@@ -93,7 +86,8 @@

protected async _read(): Promise<Dirent | null> {
await this._loadEntries();
this.checkClosed();
this._entries ??= await readdir(this.path, { withFileTypes: true });
if (!this._entries.length) {
return null;
}
return this._entries.shift() || null;
return this._entries.shift() ?? null;
}

@@ -122,2 +116,3 @@

public readSync(): Dirent | null {
this.checkClosed();
this._entries ??= readdirSync(this.path, { withFileTypes: true });

@@ -127,5 +122,15 @@ if (!this._entries.length) {

}
return this._entries.shift() || null;
return this._entries.shift() ?? null;
}
async next(): Promise<IteratorResult<Dirent>> {
const value = await this._read();
if (value) {
return { done: false, value };
}
await this.close();
return { done: true, value: undefined };
}
/**

@@ -135,17 +140,4 @@ * Asynchronously iterates over the directory via `readdir(3)` until all entries have been read.

public [Symbol.asyncIterator](): AsyncIterableIterator<Dirent> {
const _this = this;
return {
[Symbol.asyncIterator]: this[Symbol.asyncIterator],
async next(): Promise<IteratorResult<Dirent>> {
const value = await _this._read();
if (value != null) {
return { done: false, value };
}
await _this.close();
return { done: true, value: undefined };
},
};
return this;
}
}

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

import type { File } from '../file.js';
import { isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import type { FileContents } from '../filesystem.js';

@@ -21,5 +21,6 @@ import '../polyfills.js';

import { dirname, join, parse } from './path.js';
import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { ReadStream, WriteStream } from './streams.js';
import { FSWatcher } from './watchers.js';
import { FSWatcher, emitChange } from './watchers.js';
export * as constants from './constants.js';

@@ -48,4 +49,5 @@

*/
public chown(uid: number, gid: number): Promise<void> {
return this.file.chown(uid, gid);
public async chown(uid: number, gid: number): Promise<void> {
await this.file.chown(uid, gid);
emitChange('change', this.file.path);
}

@@ -57,3 +59,3 @@

*/
public chmod(mode: fs.Mode): Promise<void> {
public async chmod(mode: fs.Mode): Promise<void> {
const numMode = normalizeMode(mode, -1);

@@ -63,3 +65,4 @@ if (numMode < 0) {

}
return this.file.chmod(numMode);
await this.file.chmod(numMode);
emitChange('change', this.file.path);
}

@@ -85,3 +88,3 @@

*/
public truncate(len?: number | null): Promise<void> {
public async truncate(len?: number | null): Promise<void> {
len ||= 0;

@@ -91,3 +94,4 @@ if (len < 0) {

}
return this.file.truncate(len);
await this.file.truncate(len);
emitChange('change', this.file.path);
}

@@ -100,4 +104,5 @@

*/
public utimes(atime: string | number | Date, mtime: string | number | Date): Promise<void> {
return this.file.utimes(normalizeTime(atime), normalizeTime(mtime));
public async utimes(atime: string | number | Date, mtime: string | number | Date): Promise<void> {
await this.file.utimes(normalizeTime(atime), normalizeTime(mtime));
emitChange('change', this.file.path);
}

@@ -126,2 +131,3 @@

await this.file.write(encodedData, 0, encodedData.length);
emitChange('change', this.file.path);
}

@@ -226,2 +232,5 @@

const stats = await this.file.stat();
if (!stats.hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', this.file.path, 'stat');
}
return opts?.bigint ? new BigIntStats(stats) : stats;

@@ -267,2 +276,3 @@ }

const bytesWritten = await this.file.write(buffer, offset, length, position);
emitChange('change', this.file.path);
return { buffer, bytesWritten };

@@ -293,2 +303,3 @@ }

await this.file.write(encodedData, 0, encodedData.length, 0);
emitChange('change', this.file.path);
}

@@ -400,5 +411,9 @@

const dst = resolveMount(newPath);
if (!(await stat(dirname(oldPath))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
try {
if (src.mountPoint == dst.mountPoint) {
await src.fs.rename(src.path, dst.path, cred);
await src.fs.rename(src.path, dst.path);
emitChange('rename', oldPath.toString());
return;

@@ -408,2 +423,3 @@ }

await unlink(oldPath);
emitChange('rename', oldPath.toString());
} catch (e) {

@@ -422,3 +438,3 @@ throw fixError(e as Error, { [src.path]: oldPath, [dst.path]: newPath });

const { fs, path: resolved } = resolveMount(await realpath(path));
return await fs.exists(resolved, cred);
return await fs.exists(resolved);
} catch (e) {

@@ -445,3 +461,6 @@ if (e instanceof ErrnoError && e.code == 'ENOENT') {

try {
const stats = await fs.stat(resolved, cred);
const stats = await fs.stat(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -467,3 +486,3 @@ } catch (e) {

try {
const stats = await fs.stat(resolved, cred);
const stats = await fs.stat(resolved);
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -497,3 +516,7 @@ } catch (e) {

try {
await fs.unlink(resolved, cred);
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');
}
await fs.unlink(resolved);
emitChange('rename', path.toString());
} catch (e) {

@@ -517,3 +540,5 @@ throw fixError(e as Error, { [resolved]: path });

if (!(await fs.exists(resolved, cred))) {
const stats = await fs.stat(resolved).catch(() => null);
if (!stats) {
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {

@@ -523,9 +548,16 @@ throw ErrnoError.With('ENOENT', path, '_open');

// Create the file
const parentStats: Stats = await fs.stat(dirname(resolved), cred);
if (parentStats && !parentStats.isDirectory()) {
const parentStats: Stats = await fs.stat(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');
}
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
return new FileHandle(await fs.createFile(resolved, flag, mode));
}
if (!stats.hasAccess(flagToMode(flag), credentials)) {
throw ErrnoError.With('EACCES', path, '_open');
}
if (isExclusive(flag)) {

@@ -535,5 +567,3 @@ throw ErrnoError.With('EEXIST', path, '_open');

if (!isTruncating(flag)) {
return new FileHandle(await fs.openFile(resolved, flag, cred));
}
const handle = new FileHandle(await fs.openFile(resolved, flag));

@@ -546,6 +576,8 @@ /*

*/
const file: File = await fs.openFile(resolved, flag, cred);
await file.truncate(0);
await file.sync();
return new FileHandle(file);
if (isTruncating(flag)) {
await handle.truncate(0);
await handle.sync();
}
return handle;
}

@@ -656,3 +688,7 @@

try {
await fs.rmdir(resolved, cred);
if (!(await fs.stat(resolved)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');
}
await fs.rmdir(resolved);
emitChange('rename', path.toString());
} catch (e) {

@@ -684,7 +720,12 @@ throw fixError(e as Error, { [resolved]: path });

if (!options?.recursive) {
await fs.mkdir(resolved, mode, cred);
if (!(await fs.stat(dirname(resolved))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');
}
await fs.mkdir(resolved, mode);
emitChange('rename', path.toString());
return;
}
const dirs: string[] = [];
for (let dir = resolved, origDir = path; !(await fs.exists(dir, cred)); dir = dirname(dir), origDir = dirname(origDir)) {
for (let dir = resolved, origDir = path; !(await fs.exists(dir)); dir = dirname(dir), origDir = dirname(origDir)) {
dirs.unshift(dir);

@@ -694,3 +735,7 @@ errorPaths[dir] = origDir;

for (const dir of dirs) {
await fs.mkdir(dir, mode, cred);
if (!(await fs.stat(dirname(dir))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');
}
await fs.mkdir(dir, mode);
emitChange('rename', dir);
}

@@ -721,2 +766,5 @@ return dirs[0];

path = normalizePath(path);
if (!(await stat(path)).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'readdir');
}
path = (await exists(path)) ? await realpath(path) : path;

@@ -726,3 +774,3 @@ const { fs, path: resolved } = resolveMount(path);

try {
entries = await fs.readdir(resolved, cred);
entries = await fs.readdir(resolved);
} catch (e) {

@@ -753,13 +801,27 @@ throw fixError(e as Error, { [resolved]: path });

* `link`.
* @param existing
* @param newpath
* @param targetPath
* @param linkPath
*/
export async function link(existing: fs.PathLike, newpath: fs.PathLike): Promise<void> {
existing = normalizePath(existing);
newpath = normalizePath(newpath);
const { fs, path: resolved } = resolveMount(newpath);
export async function link(targetPath: fs.PathLike, linkPath: fs.PathLike): Promise<void> {
targetPath = normalizePath(targetPath);
if (!(await stat(dirname(targetPath))).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!(await stat(dirname(linkPath))).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
}
const { fs, path } = resolveMount(targetPath);
const link = resolveMount(linkPath);
if (fs != link.fs) {
throw ErrnoError.With('EXDEV', linkPath, 'link');
}
try {
return await fs.link(existing, newpath, cred);
if (!(await fs.stat(path)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'link');
}
return await fs.link(path, link.path);
} catch (e) {
throw fixError(e as Error, { [resolved]: newpath });
throw fixError(e as Error, { [link.path]: linkPath, [path]: targetPath });
}

@@ -891,3 +953,3 @@ }

try {
const stats = await fs.stat(resolvedPath, cred);
const stats = await fs.stat(resolvedPath);
if (!stats.isSymbolicLink()) {

@@ -910,3 +972,3 @@ return lpath;

[Symbol.asyncIterator](): AsyncIterator<FileChangeInfo<T>> {
const watcher = new FSWatcher<T>(typeof options != 'string' ? options : { encoding: options as BufferEncoding | 'buffer' });
const watcher = new FSWatcher<T>(filename.toString(), typeof options != 'string' ? options : { encoding: options as BufferEncoding | 'buffer' });

@@ -940,3 +1002,3 @@ function withDone(done: boolean) {

const stats = await stat(path);
if (!stats.hasAccess(mode, cred)) {
if (!stats.hasAccess(mode, credentials)) {
throw new ErrnoError(Errno.EACCES);

@@ -1015,2 +1077,3 @@ }

await writeFile(dest, await readFile(src));
emitChange('rename', dest.toString());
}

@@ -1028,5 +1091,3 @@ copyFile satisfies typeof promises.copyFile;

path = normalizePath(path);
const dir = new Dir(path);
await dir._loadEntries();
return dir;
return new Dir(path);
}

@@ -1033,0 +1094,0 @@ opendir satisfies typeof promises.opendir;

@@ -5,4 +5,2 @@ // Utilities and shared data

import { InMemory } from '../backends/memory.js';
import type { Cred } from '../cred.js';
import { rootCred } from '../cred.js';
import { Errno, ErrnoError } from '../error.js';

@@ -15,8 +13,2 @@ import type { File } from '../file.js';

// credentials
export let cred: Cred = rootCred;
export function setCred(val: Cred): void {
cred = val;
}
// descriptors

@@ -23,0 +15,0 @@ export const fdMap: Map<number, File> = new Map();

@@ -5,10 +5,12 @@ import { Buffer } from 'buffer';

import type { File } from '../file.js';
import { isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
import type { FileContents } from '../filesystem.js';
import { BigIntStats, type Stats } from '../stats.js';
import { normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
import { COPYFILE_EXCL, F_OK, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK } from './constants.js';
import * as constants from './constants.js';
import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { _statfs, cred, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { credentials } from '../credentials.js';
import { emitChange } from './watchers.js';

@@ -23,8 +25,12 @@ /**

newPath = normalizePath(newPath);
const _old = resolveMount(oldPath);
const _new = resolveMount(newPath);
const paths = { [_old.path]: oldPath, [_new.path]: newPath };
const oldMount = resolveMount(oldPath);
const newMount = resolveMount(newPath);
if (!statSync(dirname(oldPath)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', oldPath, 'rename');
}
try {
if (_old === _new) {
return _old.fs.renameSync(_old.path, _new.path, cred);
if (oldMount === newMount) {
oldMount.fs.renameSync(oldMount.path, newMount.path);
emitChange('rename', oldPath.toString());
return;
}

@@ -34,4 +40,5 @@

unlinkSync(oldPath);
emitChange('rename', oldPath.toString());
} catch (e) {
throw fixError(e as Error, paths);
throw fixError(e as Error, { [oldMount.path]: oldPath, [newMount.path]: newPath });
}

@@ -49,3 +56,3 @@ }

const { fs, path: resolvedPath } = resolveMount(realpathSync(path));
return fs.existsSync(resolvedPath, cred);
return fs.existsSync(resolvedPath);
} catch (e) {

@@ -72,3 +79,6 @@ if ((e as ErrnoError).errno == Errno.ENOENT) {

try {
const stats = fs.statSync(resolved, cred);
const stats = fs.statSync(resolved);
if (!stats.hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'stat');
}
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -93,3 +103,3 @@ } catch (e) {

try {
const stats = fs.statSync(resolved, cred);
const stats = fs.statSync(resolved);
return options?.bigint ? new BigIntStats(stats) : stats;

@@ -125,3 +135,7 @@ } catch (e) {

try {
return fs.unlinkSync(resolved, cred);
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'unlink');
}
fs.unlinkSync(resolved);
emitChange('rename', path.toString());
} catch (e) {

@@ -141,3 +155,3 @@ throw fixError(e as Error, { [resolved]: path });

if (!fs.existsSync(resolved, cred)) {
if (!fs.existsSync(resolved)) {
if ((!isWriteable(flag) && !isAppendable(flag)) || flag == 'r+') {

@@ -147,12 +161,15 @@ throw ErrnoError.With('ENOENT', path, '_open');

// Create the file
const parentStats: Stats = fs.statSync(dirname(resolved), cred);
const parentStats: Stats = fs.statSync(dirname(resolved));
if (!parentStats.hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(path), '_open');
}
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return fs.createFileSync(resolved, flag, mode, cred);
return fs.createFileSync(resolved, flag, mode);
}
const stats: Stats = fs.statSync(resolved, cred);
const stats: Stats = fs.statSync(resolved);
if (!stats.hasAccess(mode, cred)) {
if (!stats.hasAccess(mode, credentials) || !stats.hasAccess(flagToMode(flag), credentials)) {
throw ErrnoError.With('EACCES', path, '_open');

@@ -165,15 +182,10 @@ }

if (!isTruncating(flag)) {
return fs.openFileSync(resolved, flag, cred);
const file = fs.openFileSync(resolved, flag);
if (isTruncating(flag)) {
file.truncateSync(0);
file.syncSync();
}
// Delete file.
fs.unlinkSync(resolved, cred);
/*
Create file. Use the same mode as the old file.
Node itself modifies the ctime when this occurs, so this action
will preserve that behavior if the underlying file system
supports those properties.
*/
return fs.createFileSync(resolved, flag, stats.mode, cred);
return file;
}

@@ -189,3 +201,3 @@

*/
export function openSync(path: fs.PathLike, flag: fs.OpenMode, mode: fs.Mode | null = F_OK): number {
export function openSync(path: fs.PathLike, flag: fs.OpenMode, mode: fs.Mode | null = constants.F_OK): number {
return file2fd(_openSync(path, flag, mode, true));

@@ -266,2 +278,3 @@ }

file.writeSync(encodedData, 0, encodedData.byteLength, 0);
emitChange('change', path.toString());
}

@@ -386,3 +399,5 @@ writeFileSync satisfies typeof fs.writeFileSync;

position ??= file.position;
return file.writeSync(buffer, offset, length, position);
const bytesWritten = file.writeSync(buffer, offset, length, position);
emitChange('change', file.path);
return bytesWritten;
}

@@ -467,3 +482,7 @@ writeSync satisfies typeof fs.writeSync;

try {
fs.rmdirSync(resolved, cred);
if (!fs.statSync(resolved).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', resolved, 'rmdir');
}
fs.rmdirSync(resolved);
emitChange('rename', path.toString());
} catch (e) {

@@ -479,3 +498,2 @@ throw fixError(e as Error, { [resolved]: path });

* @param mode defaults to o777
* @todo Implement recursion
*/

@@ -496,7 +514,10 @@ export function mkdirSync(path: fs.PathLike, options: fs.MakeDirectoryOptions & { recursive: true }): string | undefined;

if (!options?.recursive) {
return fs.mkdirSync(resolved, mode, cred);
if (!fs.statSync(dirname(resolved)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(resolved), 'mkdir');
}
return fs.mkdirSync(resolved, mode);
}
const dirs: string[] = [];
for (let dir = resolved, original = path; !fs.existsSync(dir, cred); dir = dirname(dir), original = dirname(original)) {
for (let dir = resolved, original = path; !fs.existsSync(dir); dir = dirname(dir), original = dirname(original)) {
dirs.unshift(dir);

@@ -506,3 +527,7 @@ errorPaths[dir] = original;

for (const dir of dirs) {
fs.mkdirSync(dir, mode, cred);
if (!fs.statSync(dirname(dir)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(dir), 'mkdir');
}
fs.mkdirSync(dir, mode);
emitChange('rename', dir);
}

@@ -531,4 +556,7 @@ return dirs[0];

let entries: string[];
if (!statSync(path).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'readdir');
}
try {
entries = fs.readdirSync(resolved, cred);
entries = fs.readdirSync(resolved);
} catch (e) {

@@ -566,13 +594,27 @@ throw fixError(e as Error, { [resolved]: path });

* Synchronous `link`.
* @param existing
* @param newpath
* @param targetPath
* @param linkPath
*/
export function linkSync(existing: fs.PathLike, newpath: fs.PathLike): void {
existing = normalizePath(existing);
newpath = normalizePath(newpath);
const { fs, path: resolved } = resolveMount(existing);
export function linkSync(targetPath: fs.PathLike, linkPath: fs.PathLike): void {
targetPath = normalizePath(targetPath);
if (!statSync(dirname(targetPath)).hasAccess(constants.R_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(targetPath), 'link');
}
linkPath = normalizePath(linkPath);
if (!statSync(dirname(linkPath)).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', dirname(linkPath), 'link');
}
const { fs, path } = resolveMount(targetPath);
const link = resolveMount(linkPath);
if (fs != link.fs) {
throw ErrnoError.With('EXDEV', linkPath, 'link');
}
try {
return fs.linkSync(resolved, newpath, cred);
if (!fs.statSync(path).hasAccess(constants.W_OK, credentials)) {
throw ErrnoError.With('EACCES', path, 'link');
}
return fs.linkSync(path, linkPath);
} catch (e) {
throw fixError(e as Error, { [resolved]: existing });
throw fixError(e as Error, { [path]: targetPath, [link.path]: linkPath });
}

@@ -598,3 +640,3 @@ }

const file = _openSync(path, 'r+', 0o644, false);
file._setTypeSync(S_IFLNK);
file._setTypeSync(constants.S_IFLNK);
}

@@ -715,3 +757,3 @@ symlinkSync satisfies typeof fs.symlinkSync;

try {
const stats = fs.statSync(resolvedPath, cred);
const stats = fs.statSync(resolvedPath);
if (!stats.isSymbolicLink()) {

@@ -735,3 +777,3 @@ return lpath;

const stats = statSync(path);
if (!stats.hasAccess(mode, cred)) {
if (!stats.hasAccess(mode, credentials)) {
throw new ErrnoError(Errno.EACCES);

@@ -751,4 +793,4 @@ }

switch (stats.mode & S_IFMT) {
case S_IFDIR:
switch (stats.mode & constants.S_IFMT) {
case constants.S_IFDIR:
if (options?.recursive) {

@@ -762,10 +804,10 @@ for (const entry of readdirSync(path)) {

return;
case S_IFREG:
case S_IFLNK:
case constants.S_IFREG:
case constants.S_IFLNK:
unlinkSync(path);
return;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
case S_IFSOCK:
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:

@@ -807,3 +849,3 @@ throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm');

if (flags && flags & COPYFILE_EXCL && existsSync(dest)) {
if (flags && flags & constants.COPYFILE_EXCL && existsSync(dest)) {
throw new ErrnoError(Errno.EEXIST, 'Destination file already exists.', dest, 'copyFile');

@@ -813,2 +855,3 @@ }

writeFileSync(dest, readFileSync(src));
emitChange('rename', dest.toString());
}

@@ -863,3 +906,3 @@ copyFileSync satisfies typeof fs.copyFileSync;

path = normalizePath(path);
return new Dir(path); // Re-use existing `Dir` class
return new Dir(path);
}

@@ -890,4 +933,4 @@ opendirSync satisfies typeof fs.opendirSync;

switch (srcStats.mode & S_IFMT) {
case S_IFDIR:
switch (srcStats.mode & constants.S_IFMT) {
case constants.S_IFDIR:
if (!opts?.recursive) {

@@ -904,10 +947,10 @@ throw new ErrnoError(Errno.EISDIR, source + ' is a directory (not copied)', source, 'cp');

break;
case S_IFREG:
case S_IFLNK:
case constants.S_IFREG:
case constants.S_IFLNK:
copyFileSync(source, destination);
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
case S_IFSOCK:
case constants.S_IFBLK:
case constants.S_IFCHR:
case constants.S_IFIFO:
case constants.S_IFSOCK:
default:

@@ -914,0 +957,0 @@ throw new ErrnoError(Errno.EPERM, 'File type not supported', source, 'rm');

@@ -5,3 +5,13 @@ import { EventEmitter } from 'eventemitter3';

import { ErrnoError } from '../error.js';
import { isStatsEqual, type Stats } from '../stats.js';
import { normalizePath } from '../utils.js';
import { dirname, basename } from './path.js';
import { statSync } from './sync.js';
/**
* Base class for file system watchers.
* Provides event handling capabilities for watching file system changes.
*
* @template TEvents The type of events emitted by the watcher.
*/
class Watcher<TEvents extends Record<string, unknown[]> = Record<string, unknown[]>> extends EventEmitter<TEvents> implements NodeEventEmitter {

@@ -18,20 +28,24 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

public constructor(public readonly path: string) {
super();
}
public setMaxListeners(): never {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.setMaxListeners');
}
public getMaxListeners(): never {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.getMaxListeners');
}
public prependListener(): never {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.prependListener');
}
public prependOnceListener(): never {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.prependOnceListener');
}
public rawListeners(): never {
throw ErrnoError.With('ENOTSUP');
throw ErrnoError.With('ENOSYS', this.path, 'Watcher.rawListeners');
}

@@ -49,3 +63,5 @@

/**
* @todo Actually emit events
* Watches for changes on the file system.
*
* @template T The type of the filename, either `string` or `Buffer`.
*/

@@ -60,8 +76,122 @@ export class FSWatcher<T extends string | Buffer = string | Buffer>

{
public constructor(public readonly options: fs.WatchOptions) {
super();
public constructor(
path: string,
public readonly options: fs.WatchOptions
) {
super(path);
addWatcher(path.toString(), this);
}
public close(): void {}
public close(): void {
super.emit('close');
removeWatcher(this.path.toString(), this);
}
public [Symbol.dispose](): void {
this.close();
}
}
export class StatWatcher extends Watcher implements fs.StatWatcher {}
/**
* Watches for changes to a file's stats.
*
* Instances of `StatWatcher` are used by `fs.watchFile()` to monitor changes to a file's statistics.
*/
export class StatWatcher
extends Watcher<{
change: [current: Stats, previous: Stats];
close: [];
error: [error: Error];
}>
implements fs.StatWatcher
{
private intervalId?: NodeJS.Timeout | number;
private previous?: Stats;
public constructor(
path: string,
private options: { persistent?: boolean; interval?: number }
) {
super(path);
this.start();
}
protected onInterval() {
try {
const current = statSync(this.path);
if (!isStatsEqual(this.previous!, current)) {
this.emit('change', current, this.previous!);
this.previous = current;
}
} catch (e) {
this.emit('error', e as Error);
}
}
protected start() {
const interval = this.options.interval || 5000;
try {
this.previous = statSync(this.path);
} catch (e) {
this.emit('error', e as Error);
return;
}
this.intervalId = setInterval(this.onInterval.bind(this), interval);
if (!this.options.persistent && typeof this.intervalId == 'object') {
this.intervalId.unref();
}
}
/**
* @internal
*/
public stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = undefined;
}
this.removeAllListeners();
}
}
const watchers: Map<string, Set<FSWatcher>> = new Map();
export function addWatcher(path: string, watcher: FSWatcher) {
const normalizedPath = normalizePath(path);
if (!watchers.has(normalizedPath)) {
watchers.set(normalizedPath, new Set());
}
watchers.get(normalizedPath)!.add(watcher);
}
export function removeWatcher(path: string, watcher: FSWatcher) {
const normalizedPath = normalizePath(path);
if (watchers.has(normalizedPath)) {
watchers.get(normalizedPath)!.delete(watcher);
if (watchers.get(normalizedPath)!.size === 0) {
watchers.delete(normalizedPath);
}
}
}
export function emitChange(eventType: fs.WatchEventType, filename: string) {
let normalizedFilename: string = normalizePath(filename);
// Notify watchers on the specific file
if (watchers.has(normalizedFilename)) {
for (const watcher of watchers.get(normalizedFilename)!) {
watcher.emit('change', eventType, basename(filename));
}
}
// Notify watchers on parent directories if they are watching recursively
let parent = dirname(normalizedFilename);
while (parent !== normalizedFilename && parent !== '/') {
if (watchers.has(parent)) {
for (const watcher of watchers.get(parent)!) {
watcher.emit('change', eventType, basename(filename));
}
}
normalizedFilename = parent;
parent = dirname(parent);
}
}

@@ -287,3 +287,3 @@ /**

/**
* @return A friendly error message.
* @returns A friendly error message.
*/

@@ -290,0 +290,0 @@ public toString(): string {

@@ -1,2 +0,1 @@

import type { Cred } from './cred.js';
import type { ErrnoError } from './error.js';

@@ -106,7 +105,7 @@ import type { File } from './file.js';

*/
public abstract rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
public abstract rename(oldPath: string, newPath: string): Promise<void>;
/**
* Synchronous rename.
*/
public abstract renameSync(oldPath: string, newPath: string, cred: Cred): void;
public abstract renameSync(oldPath: string, newPath: string): void;

@@ -116,3 +115,3 @@ /**

*/
public abstract stat(path: string, cred: Cred): Promise<Stats>;
public abstract stat(path: string): Promise<Stats>;

@@ -122,3 +121,3 @@ /**

*/
public abstract statSync(path: string, cred: Cred): Stats;
public abstract statSync(path: string): Stats;

@@ -130,3 +129,3 @@ /**

*/
public abstract openFile(path: string, flag: string, cred: Cred): Promise<File>;
public abstract openFile(path: string, flag: string): Promise<File>;

@@ -139,3 +138,3 @@ /**

*/
public abstract openFileSync(path: string, flag: string, cred: Cred): File;
public abstract openFileSync(path: string, flag: string): File;

@@ -145,3 +144,3 @@ /**

*/
public abstract createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
public abstract createFile(path: string, flag: string, mode: number): Promise<File>;

@@ -151,3 +150,3 @@ /**

*/
public abstract createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
public abstract createFileSync(path: string, flag: string, mode: number): File;

@@ -157,7 +156,7 @@ /**

*/
public abstract unlink(path: string, cred: Cred): Promise<void>;
public abstract unlink(path: string): Promise<void>;
/**
* Synchronous `unlink`.
*/
public abstract unlinkSync(path: string, cred: Cred): void;
public abstract unlinkSync(path: string): void;
// Directory operations

@@ -167,7 +166,7 @@ /**

*/
public abstract rmdir(path: string, cred: Cred): Promise<void>;
public abstract rmdir(path: string): Promise<void>;
/**
* Synchronous `rmdir`.
*/
public abstract rmdirSync(path: string, cred: Cred): void;
public abstract rmdirSync(path: string): void;
/**

@@ -177,3 +176,3 @@ * Asynchronous `mkdir`.

*/
public abstract mkdir(path: string, mode: number, cred: Cred): Promise<void>;
public abstract mkdir(path: string, mode: number): Promise<void>;
/**

@@ -183,11 +182,11 @@ * Synchronous `mkdir`.

*/
public abstract mkdirSync(path: string, mode: number, cred: Cred): void;
public abstract mkdirSync(path: string, mode: number): void;
/**
* Asynchronous `readdir`. Reads the contents of a directory.
*/
public abstract readdir(path: string, cred: Cred): Promise<string[]>;
public abstract readdir(path: string): Promise<string[]>;
/**
* Synchronous `readdir`. Reads the contents of a directory.
*/
public abstract readdirSync(path: string, cred: Cred): string[];
public abstract readdirSync(path: string): string[];

@@ -197,5 +196,5 @@ /**

*/
public async exists(path: string, cred: Cred): Promise<boolean> {
public async exists(path: string): Promise<boolean> {
try {
await this.stat(path, cred);
await this.stat(path);
return true;

@@ -210,5 +209,5 @@ } catch (e) {

*/
public existsSync(path: string, cred: Cred): boolean {
public existsSync(path: string): boolean {
try {
this.statSync(path, cred);
this.statSync(path);
return true;

@@ -223,3 +222,3 @@ } catch (e) {

*/
public abstract link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
public abstract link(target: string, link: string): Promise<void>;

@@ -229,3 +228,3 @@ /**

*/
public abstract linkSync(srcpath: string, dstpath: string, cred: Cred): void;
public abstract linkSync(target: string, link: string): void;

@@ -232,0 +231,0 @@ /**

@@ -12,3 +12,3 @@ export * from './error.js';

export * from './config.js';
export * from './cred.js';
export * from './credentials.js';
export * from './file.js';

@@ -15,0 +15,0 @@ export * from './filesystem.js';

@@ -1,5 +0,4 @@

import { rootCred, type Cred } from '../cred.js';
import { join } from '../emulation/path.js';
import { Errno, ErrnoError } from '../error.js';
import { PreloadFile, parseFlag, type File } from '../file.js';
import { parseFlag, PreloadFile, type File } from '../file.js';
import type { FileSystem } from '../filesystem.js';

@@ -20,7 +19,7 @@ import type { Stats } from '../stats.js';

* Implementing classes must define `_sync` for the synchronous file system used as a cache.
* Synchronous methods on an asynchronous FS are implemented by:
* - Performing operations over the in-memory copy,
* while asynchronously pipelining them to the backing store.
* - During loading, the contents of the async file system are preloaded into the synchronous store.
*
* Synchronous methods on an asynchronous FS are implemented by performing operations over the in-memory copy,
* while asynchronously pipelining them to the backing store.
* During loading, the contents of the async file system are preloaded into the synchronous store.
*
*/

@@ -38,11 +37,11 @@ export function Async<T extends typeof FileSystem>(

ready(): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
statSync(path: string, cred: Cred): Stats;
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
openFileSync(path: string, flag: string, cred: Cred): File;
unlinkSync(path: string, cred: Cred): void;
rmdirSync(path: string, cred: Cred): void;
mkdirSync(path: string, mode: number, cred: Cred): void;
readdirSync(path: string, cred: Cred): string[];
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
renameSync(oldPath: string, newPath: string): void;
statSync(path: string): Stats;
createFileSync(path: string, flag: string, mode: number): File;
openFileSync(path: string, flag: string): File;
unlinkSync(path: string): void;
rmdirSync(path: string): void;
mkdirSync(path: string, mode: number): void;
readdirSync(path: string): string[];
linkSync(srcpath: string, dstpath: string): void;
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;

@@ -98,23 +97,23 @@ }

public renameSync(oldPath: string, newPath: string, cred: Cred): void {
public renameSync(oldPath: string, newPath: string): void {
this.checkSync(oldPath, 'rename');
this._sync.renameSync(oldPath, newPath, cred);
this.queue('rename', oldPath, newPath, cred);
this._sync.renameSync(oldPath, newPath);
this.queue('rename', oldPath, newPath);
}
public statSync(path: string, cred: Cred): Stats {
public statSync(path: string): Stats {
this.checkSync(path, 'stat');
return this._sync.statSync(path, cred);
return this._sync.statSync(path);
}
public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
public createFileSync(path: string, flag: string, mode: number): PreloadFile<this> {
this.checkSync(path, 'createFile');
this._sync.createFileSync(path, flag, mode, cred);
this.queue('createFile', path, flag, mode, cred);
return this.openFileSync(path, flag, cred);
this._sync.createFileSync(path, flag, mode);
this.queue('createFile', path, flag, mode);
return this.openFileSync(path, flag);
}
public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
public openFileSync(path: string, flag: string): PreloadFile<this> {
this.checkSync(path, 'openFile');
const file = this._sync.openFileSync(path, flag, cred);
const file = this._sync.openFileSync(path, flag);
const stats = file.statSync();

@@ -126,29 +125,29 @@ const buffer = new Uint8Array(stats.size);

public unlinkSync(path: string, cred: Cred): void {
public unlinkSync(path: string): void {
this.checkSync(path, 'unlinkSync');
this._sync.unlinkSync(path, cred);
this.queue('unlink', path, cred);
this._sync.unlinkSync(path);
this.queue('unlink', path);
}
public rmdirSync(path: string, cred: Cred): void {
public rmdirSync(path: string): void {
this.checkSync(path, 'rmdir');
this._sync.rmdirSync(path, cred);
this.queue('rmdir', path, cred);
this._sync.rmdirSync(path);
this.queue('rmdir', path);
}
public mkdirSync(path: string, mode: number, cred: Cred): void {
public mkdirSync(path: string, mode: number): void {
this.checkSync(path, 'mkdir');
this._sync.mkdirSync(path, mode, cred);
this.queue('mkdir', path, mode, cred);
this._sync.mkdirSync(path, mode);
this.queue('mkdir', path, mode);
}
public readdirSync(path: string, cred: Cred): string[] {
public readdirSync(path: string): string[] {
this.checkSync(path, 'readdir');
return this._sync.readdirSync(path, cred);
return this._sync.readdirSync(path);
}
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
public linkSync(srcpath: string, dstpath: string): void {
this.checkSync(srcpath, 'link');
this._sync.linkSync(srcpath, dstpath, cred);
this.queue('link', srcpath, dstpath, cred);
this._sync.linkSync(srcpath, dstpath);
this.queue('link', srcpath, dstpath);
}

@@ -162,5 +161,5 @@

public existsSync(path: string, cred: Cred): boolean {
public existsSync(path: string): boolean {
this.checkSync(path, 'exists');
return this._sync.existsSync(path, cred);
return this._sync.existsSync(path);
}

@@ -173,19 +172,19 @@

this.checkSync(path, 'crossCopy');
const stats = await this.stat(path, rootCred);
if (stats.isDirectory()) {
if (path !== '/') {
const stats = await this.stat(path, rootCred);
this._sync.mkdirSync(path, stats.mode, stats.cred());
}
const files = await this.readdir(path, rootCred);
for (const file of files) {
await this.crossCopy(join(path, file));
}
} else {
await using asyncFile = await this.openFile(path, parseFlag('r'), rootCred);
using syncFile = this._sync.createFileSync(path, parseFlag('w'), stats.mode, stats.cred());
const stats = await this.stat(path);
if (!stats.isDirectory()) {
await using asyncFile = await this.openFile(path, parseFlag('r'));
using syncFile = this._sync.createFileSync(path, parseFlag('w'), stats.mode);
const buffer = new Uint8Array(stats.size);
await asyncFile.read(buffer);
syncFile.writeSync(buffer, 0, stats.size);
return;
}
if (path !== '/') {
const stats = await this.stat(path);
this._sync.mkdirSync(path, stats.mode);
}
const files = await this.readdir(path);
for (const file of files) {
await this.crossCopy(join(path, file));
}
}

@@ -192,0 +191,0 @@

@@ -1,2 +0,1 @@

import type { Cred } from '../cred.js';
import { ErrnoError } from '../error.js';

@@ -57,3 +56,3 @@ import type { File } from '../file.js';

{
lock(path: string): Promise<MutexLock>;
lock(path: string, syscall: string): Promise<MutexLock>;
lockSync(path: string): MutexLock;

@@ -84,5 +83,13 @@ isLocked(path: string): boolean;

*/
public async lock(path: string): Promise<MutexLock> {
public async lock(path: string, syscall: string): Promise<MutexLock> {
const previous = this.locks.get(path);
const lock = this.addLock(path);
const stack = new Error().stack;
setTimeout(() => {
if (lock.isLocked) {
const error = ErrnoError.With('EDEADLK', path, syscall);
error.stack += stack?.slice('Error'.length);
throw error;
}
}, 5000);
await previous?.done();

@@ -100,3 +107,3 @@ return lock;

// Non-null assertion: we already checked locks has path
throw ErrnoError.With('EBUSY', path, 'lockSync');
throw ErrnoError.With('EBUSY', path, 'lock');
}

@@ -116,122 +123,122 @@

/* eslint-disable @typescript-eslint/no-unused-vars */
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
using _ = await this.lock(oldPath);
public async rename(oldPath: string, newPath: string): Promise<void> {
using _ = await this.lock(oldPath, 'rename');
// @ts-expect-error 2513
await super.rename(oldPath, newPath, cred);
await super.rename(oldPath, newPath);
}
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
public renameSync(oldPath: string, newPath: string): void {
using _ = this.lockSync(oldPath);
// @ts-expect-error 2513
return super.renameSync(oldPath, newPath, cred);
return super.renameSync(oldPath, newPath);
}
public async stat(path: string, cred: Cred): Promise<Stats> {
using _ = await this.lock(path);
public async stat(path: string): Promise<Stats> {
using _ = await this.lock(path, 'stat');
// @ts-expect-error 2513
return await super.stat(path, cred);
return await super.stat(path);
}
public statSync(path: string, cred: Cred): Stats {
public statSync(path: string): Stats {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.statSync(path, cred);
return super.statSync(path);
}
public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
using _ = await this.lock(path);
public async openFile(path: string, flag: string): Promise<File> {
using _ = await this.lock(path, 'openFile');
// @ts-expect-error 2513
return await super.openFile(path, flag, cred);
return await super.openFile(path, flag);
}
public openFileSync(path: string, flag: string, cred: Cred): File {
public openFileSync(path: string, flag: string): File {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.openFileSync(path, flag, cred);
return super.openFileSync(path, flag);
}
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
using _ = await this.lock(path);
public async createFile(path: string, flag: string, mode: number): Promise<File> {
using _ = await this.lock(path, 'createFile');
// @ts-expect-error 2513
return await super.createFile(path, flag, mode, cred);
return await super.createFile(path, flag, mode);
}
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
public createFileSync(path: string, flag: string, mode: number): File {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.createFileSync(path, flag, mode, cred);
return super.createFileSync(path, flag, mode);
}
public async unlink(path: string, cred: Cred): Promise<void> {
using _ = await this.lock(path);
public async unlink(path: string): Promise<void> {
using _ = await this.lock(path, 'unlink');
// @ts-expect-error 2513
await super.unlink(path, cred);
await super.unlink(path);
}
public unlinkSync(path: string, cred: Cred): void {
public unlinkSync(path: string): void {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.unlinkSync(path, cred);
return super.unlinkSync(path);
}
public async rmdir(path: string, cred: Cred): Promise<void> {
using _ = await this.lock(path);
public async rmdir(path: string): Promise<void> {
using _ = await this.lock(path, 'rmdir');
// @ts-expect-error 2513
await super.rmdir(path, cred);
await super.rmdir(path);
}
public rmdirSync(path: string, cred: Cred): void {
public rmdirSync(path: string): void {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.rmdirSync(path, cred);
return super.rmdirSync(path);
}
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
using _ = await this.lock(path);
public async mkdir(path: string, mode: number): Promise<void> {
using _ = await this.lock(path, 'mkdir');
// @ts-expect-error 2513
await super.mkdir(path, mode, cred);
await super.mkdir(path, mode);
}
public mkdirSync(path: string, mode: number, cred: Cred): void {
public mkdirSync(path: string, mode: number): void {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.mkdirSync(path, mode, cred);
return super.mkdirSync(path, mode);
}
public async readdir(path: string, cred: Cred): Promise<string[]> {
using _ = await this.lock(path);
public async readdir(path: string): Promise<string[]> {
using _ = await this.lock(path, 'readdir');
// @ts-expect-error 2513
return await super.readdir(path, cred);
return await super.readdir(path);
}
public readdirSync(path: string, cred: Cred): string[] {
public readdirSync(path: string): string[] {
using _ = this.lockSync(path);
// @ts-expect-error 2513
return super.readdirSync(path, cred);
return super.readdirSync(path);
}
public async exists(path: string, cred: Cred): Promise<boolean> {
using _ = await this.lock(path);
return await super.exists(path, cred);
public async exists(path: string): Promise<boolean> {
using _ = await this.lock(path, 'exists');
return await super.exists(path);
}
public existsSync(path: string, cred: Cred): boolean {
public existsSync(path: string): boolean {
using _ = this.lockSync(path);
return super.existsSync(path, cred);
return super.existsSync(path);
}
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
using _ = await this.lock(srcpath);
public async link(srcpath: string, dstpath: string): Promise<void> {
using _ = await this.lock(srcpath, 'link');
// @ts-expect-error 2513
await super.link(srcpath, dstpath, cred);
await super.link(srcpath, dstpath);
}
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
public linkSync(srcpath: string, dstpath: string): void {
using _ = this.lockSync(srcpath);
// @ts-expect-error 2513
return super.linkSync(srcpath, dstpath, cred);
return super.linkSync(srcpath, dstpath);
}
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
using _ = await this.lock(path);
using _ = await this.lock(path, 'sync');
// @ts-expect-error 2513

@@ -238,0 +245,0 @@ await super.sync(path, data, stats);

@@ -1,2 +0,1 @@

import type { Cred } from '../cred.js';
import { Errno, ErrnoError } from '../error.js';

@@ -18,14 +17,14 @@ import type { File } from '../file.js';

metadata(): FileSystemMetadata;
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
renameSync(oldPath: string, newPath: string, cred: Cred): void;
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
unlink(path: string, cred: Cred): Promise<void>;
unlinkSync(path: string, cred: Cred): void;
rmdir(path: string, cred: Cred): Promise<void>;
rmdirSync(path: string, cred: Cred): void;
mkdir(path: string, mode: number, cred: Cred): Promise<void>;
mkdirSync(path: string, mode: number, cred: Cred): void;
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
linkSync(srcpath: string, dstpath: string, cred: Cred): void;
rename(oldPath: string, newPath: string): Promise<void>;
renameSync(oldPath: string, newPath: string): void;
createFile(path: string, flag: string, mode: number): Promise<File>;
createFileSync(path: string, flag: string, mode: number): File;
unlink(path: string): Promise<void>;
unlinkSync(path: string): void;
rmdir(path: string): Promise<void>;
rmdirSync(path: string): void;
mkdir(path: string, mode: number): Promise<void>;
mkdirSync(path: string, mode: number): void;
link(srcpath: string, dstpath: string): Promise<void>;
linkSync(srcpath: string, dstpath: string): void;
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;

@@ -40,47 +39,47 @@ syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;

/* eslint-disable @typescript-eslint/no-unused-vars */
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
public async rename(oldPath: string, newPath: string): Promise<void> {
throw new ErrnoError(Errno.EROFS);
}
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
public renameSync(oldPath: string, newPath: string): void {
throw new ErrnoError(Errno.EROFS);
}
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
public async createFile(path: string, flag: string, mode: number): Promise<File> {
throw new ErrnoError(Errno.EROFS);
}
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File {
public createFileSync(path: string, flag: string, mode: number): File {
throw new ErrnoError(Errno.EROFS);
}
public async unlink(path: string, cred: Cred): Promise<void> {
public async unlink(path: string): Promise<void> {
throw new ErrnoError(Errno.EROFS);
}
public unlinkSync(path: string, cred: Cred): void {
public unlinkSync(path: string): void {
throw new ErrnoError(Errno.EROFS);
}
public async rmdir(path: string, cred: Cred): Promise<void> {
public async rmdir(path: string): Promise<void> {
throw new ErrnoError(Errno.EROFS);
}
public rmdirSync(path: string, cred: Cred): void {
public rmdirSync(path: string): void {
throw new ErrnoError(Errno.EROFS);
}
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
public async mkdir(path: string, mode: number): Promise<void> {
throw new ErrnoError(Errno.EROFS);
}
public mkdirSync(path: string, mode: number, cred: Cred): void {
public mkdirSync(path: string, mode: number): void {
throw new ErrnoError(Errno.EROFS);
}
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
public async link(srcpath: string, dstpath: string): Promise<void> {
throw new ErrnoError(Errno.EROFS);
}
public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
public linkSync(srcpath: string, dstpath: string): void {
throw new ErrnoError(Errno.EROFS);

@@ -87,0 +86,0 @@ }

@@ -1,5 +0,4 @@

import type { Cred } from '../cred.js';
import type { File } from '../file.js';
import type { FileSystem } from '../filesystem.js';
import type { Stats } from '../stats.js';
import type { FileSystem } from '../filesystem.js';
import type { Mixin, _AsyncFSMethods } from './shared.js';

@@ -13,40 +12,40 @@

abstract class SyncFS extends FS implements _AsyncFSMethods {
public async exists(path: string, cred: Cred): Promise<boolean> {
return this.existsSync(path, cred);
public async exists(path: string): Promise<boolean> {
return this.existsSync(path);
}
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
return this.renameSync(oldPath, newPath, cred);
public async rename(oldPath: string, newPath: string): Promise<void> {
return this.renameSync(oldPath, newPath);
}
public async stat(path: string, cred: Cred): Promise<Stats> {
return this.statSync(path, cred);
public async stat(path: string): Promise<Stats> {
return this.statSync(path);
}
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> {
return this.createFileSync(path, flag, mode, cred);
public async createFile(path: string, flag: string, mode: number): Promise<File> {
return this.createFileSync(path, flag, mode);
}
public async openFile(path: string, flag: string, cred: Cred): Promise<File> {
return this.openFileSync(path, flag, cred);
public async openFile(path: string, flag: string): Promise<File> {
return this.openFileSync(path, flag);
}
public async unlink(path: string, cred: Cred): Promise<void> {
return this.unlinkSync(path, cred);
public async unlink(path: string): Promise<void> {
return this.unlinkSync(path);
}
public async rmdir(path: string, cred: Cred): Promise<void> {
return this.rmdirSync(path, cred);
public async rmdir(path: string): Promise<void> {
return this.rmdirSync(path);
}
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> {
return this.mkdirSync(path, mode, cred);
public async mkdir(path: string, mode: number): Promise<void> {
return this.mkdirSync(path, mode);
}
public async readdir(path: string, cred: Cred): Promise<string[]> {
return this.readdirSync(path, cred);
public async readdir(path: string): Promise<string[]> {
return this.readdirSync(path);
}
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> {
return this.linkSync(srcpath, dstpath, cred);
public async link(srcpath: string, dstpath: string): Promise<void> {
return this.linkSync(srcpath, dstpath);
}

@@ -53,0 +52,0 @@

import type * as Node from 'fs';
import type { Cred } from './cred.js';
import type { Credentials } from './credentials.js';
import { S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRWXG, S_IRWXO, S_IRWXU } from './emulation/constants.js';

@@ -240,3 +240,3 @@ import { size_max } from './inode.js';

*/
public hasAccess(mode: number, cred: Cred): boolean {
public hasAccess(mode: number, cred: Credentials): boolean {
if (cred.euid === 0 || cred.egid === 0) {

@@ -256,3 +256,3 @@ //Running as root

*/
public cred(uid: number = Number(this.uid), gid: number = Number(this.gid)): Cred {
public cred(uid: number = Number(this.uid), gid: number = Number(this.gid)): Credentials {
return {

@@ -321,4 +321,2 @@ uid,

* Stats with bigint
* @todo Implement with bigint instead of wrapping Stats
* @internal
*/

@@ -330,4 +328,16 @@ export class BigIntStats extends StatsCommon<bigint> implements Node.BigIntStats, StatsLike {

/**
* Determines if the file stats have changed by comparing relevant properties.
*
* @param left The previous stats.
* @param right The current stats.
* @returns `true` if stats have changed; otherwise, `false`.
* @internal
*/
export function isStatsEqual<T extends number | bigint>(left: StatsCommon<T>, right: StatsCommon<T>): boolean {
return left.size == right.size && +left.atime == +right.atime && +left.mtime == +right.mtime && +left.ctime == +right.ctime && left.mode == right.mode;
}
/**
* @internal
*/
export const ZenFsType = 0x7a656e6673; // 'z' 'e' 'n' 'f' 's'

@@ -334,0 +344,0 @@

@@ -1,10 +0,7 @@

/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return */
import type * as fs from 'node:fs';
import type { OptionalTuple } from 'utilium';
import { ErrnoError, Errno } from './error.js';
import type { Cred } from './cred.js';
import { dirname, resolve, type AbsolutePath } from './emulation/path.js';
import { Errno, ErrnoError } from './error.js';
import type { FileSystem } from './filesystem.js';
import type * as fs from 'node:fs';

@@ -24,6 +21,6 @@ declare global {

*/
export function mkdirpSync(path: string, mode: number, cred: Cred, fs: FileSystem): void {
if (!fs.existsSync(path, cred)) {
mkdirpSync(dirname(path), mode, cred, fs);
fs.mkdirSync(path, mode, cred);
export function mkdirpSync(path: string, mode: number, fs: FileSystem): void {
if (!fs.existsSync(path)) {
mkdirpSync(dirname(path), mode, fs);
fs.mkdirSync(path, mode);
}

@@ -154,9 +151,3 @@ }

export function decodeDirListing(data: Uint8Array): Record<string, bigint> {
return JSON.parse(decode(data), (k, v) => {
if (k == '') {
return v;
}
return BigInt(v);
});
return JSON.parse(decode(data), (k, v) => (k == '' ? v : BigInt(v as string)));
}

@@ -169,11 +160,3 @@

export function encodeDirListing(data: Record<string, bigint>): Uint8Array {
return encode(
JSON.stringify(data, (k, v) => {
if (k == '') {
return v;
}
return v.toString();
})
);
return encode(JSON.stringify(data, (k, v) => (k == '' ? v : v.toString())));
}

@@ -180,0 +163,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc