Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@zenfs/core

Package Overview
Dependencies
Maintainers
0
Versions
156
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 1.2.8 to 1.2.9

tests/mounts.test.ts

20

dist/backends/store/fs.js

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

try {
const tx = __addDisposableResource(env_15, this.store.transaction(), true);
const parentPath = dirname(path), parent = await this.findINode(tx, parentPath);
const fname = basename(path), listing = await this.getDirListing(tx, parent, parentPath);
/*
The root always exists.
If we don't check this prior to taking steps below,
we will create a file with name '' in root should path == '/'.
we will create a file with name '' in root if path is '/'.
*/
if (path === '/') {
if (path == '/') {
throw ErrnoError.With('EEXIST', path, 'commitNew');
}
const tx = __addDisposableResource(env_15, this.store.transaction(), true);
const parentPath = dirname(path), parent = await this.findINode(tx, parentPath);
const fname = basename(path), listing = await this.getDirListing(tx, parent, parentPath);
// Check if file already exists.

@@ -697,13 +697,13 @@ if (listing[fname]) {

try {
const tx = __addDisposableResource(env_16, this.store.transaction(), false);
const parentPath = dirname(path), parent = this.findINodeSync(tx, parentPath);
const fname = basename(path), listing = this.getDirListingSync(tx, parent, parentPath);
/*
The root always exists.
If we don't check this prior to taking steps below,
we will create a file with name '' in root should p == '/'.
we will create a file with name '' in root if path is '/'.
*/
if (path === '/') {
if (path == '/') {
throw ErrnoError.With('EEXIST', path, 'commitNew');
}
const tx = __addDisposableResource(env_16, this.store.transaction(), false);
const parentPath = dirname(path), parent = this.findINodeSync(tx, parentPath);
const fname = basename(path), listing = this.getDirListingSync(tx, parent, parentPath);
// Check if file already exists.

@@ -710,0 +710,0 @@ if (listing[fname]) {

import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
import { credentials } from './credentials.js';
import { DeviceFS, fullDevice, nullDevice, randomDevice, zeroDevice } from './devices.js';
import { DeviceFS } from './devices.js';
import * as cache from './emulation/cache.js';
import { config } from './emulation/config.js';
import * as fs from './emulation/index.js';
import { config } from './emulation/config.js';
import { Errno, ErrnoError } from './error.js';

@@ -64,2 +64,22 @@ import { FileSystem } from './filesystem.js';

/**
* Like `fs.mount`, but it also creates missing directories.
* @privateRemarks
* This is implemented as a separate function to avoid a circular dependency between emulation/shared.ts and other emulation layer files.
* @internal
*/
async function mount(path, mount) {
if (path == '/') {
fs.mount(path, mount);
return;
}
const stats = await fs.promises.stat(path).catch(() => null);
if (!stats) {
await fs.promises.mkdir(path, { recursive: true });
}
else if (!stats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', path, 'configure');
}
fs.mount(path, mount);
}
/**
* Configures ZenFS with `configuration`

@@ -78,8 +98,5 @@ * @see Configuration

const devfs = new DeviceFS();
devfs.createDevice('/null', nullDevice);
devfs.createDevice('/zero', zeroDevice);
devfs.createDevice('/full', fullDevice);
devfs.createDevice('/random', randomDevice);
devfs.addDefaults();
await devfs.ready();
fs.mount('/dev', devfs);
await mount('/dev', devfs);
}

@@ -89,2 +106,4 @@ if (!configuration.mounts) {

}
const toMount = [];
let unmountRoot = false;
for (const [point, mountConfig] of Object.entries(configuration.mounts)) {

@@ -97,5 +116,9 @@ if (!point.startsWith('/')) {

}
configuration.mounts[point] = await resolveMountConfig(mountConfig);
if (point == '/')
unmountRoot = true;
toMount.push([point, await resolveMountConfig(mountConfig)]);
}
fs.mountObject(configuration.mounts);
if (unmountRoot)
fs.umount('/');
await Promise.all(toMount.map(([point, fs]) => mount(point, fs)));
}

@@ -15,2 +15,5 @@ /**

export declare const credentials: Credentials;
/**
* @deprecated
*/
export declare const rootCredentials: Credentials;

@@ -9,2 +9,5 @@ export const credentials = {

};
/**
* @deprecated
*/
export const rootCredentials = {

@@ -11,0 +14,0 @@ uid: 0,

@@ -127,2 +127,6 @@ import type { FileReadResult } from 'node:fs/promises';

createDevice<TData = any>(path: string, driver: DeviceDriver<TData>): Device<TData | Record<string, never>>;
/**
* Adds default devices
*/
addDefaults(): void;
constructor();

@@ -129,0 +133,0 @@ rename(oldPath: string, newPath: string): Promise<void>;

@@ -171,2 +171,11 @@ /*

}
/**
* Adds default devices
*/
addDefaults() {
this.createDevice('/null', nullDevice);
this.createDevice('/zero', zeroDevice);
this.createDevice('/full', fullDevice);
this.createDevice('/random', randomDevice);
}
constructor() {

@@ -173,0 +182,0 @@ super(new InMemoryStore('devfs'));

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

import * as cache from './cache.js';
import { config } from './config.js';
import * as constants from './constants.js';
import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { config } from './config.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
import { ReadStream, WriteStream } from './streams.js';

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

if (!stats) {
throw ErrnoError.With('ENOENT', path, 'readdir');
throw ErrnoError.With('ENOENT', path, 'rmdir');
}

@@ -660,12 +660,2 @@ if (!stats.isDirectory()) {

const entries = await fs.readdir(resolved).catch(handleError);
for (const point of mounts.keys()) {
if (point.startsWith(path)) {
const entry = point.slice(path.length);
if (entry.includes('/') || entry.length == 0) {
// ignore FSs mounted in subdirectories and any FS mounted to `path`.
continue;
}
entries.push(entry);
}
}
const values = [];

@@ -672,0 +662,0 @@ const addEntry = async (entry) => {

@@ -41,2 +41,5 @@ import type { BigIntStatsFs, StatsFs } from 'node:fs';

export declare function fixError<E extends ErrnoError>(e: E, paths: Record<string, string>): E;
/**
* @deprecated
*/
export declare function mountObject(mounts: MountObject): void;

@@ -43,0 +46,0 @@ /**

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

}
/**
* @deprecated
*/
export function mountObject(mounts) {

@@ -95,0 +98,0 @@ if ('/' in mounts) {

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

import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
import * as cache from './cache.js';
import { config } from './config.js';
import * as constants from './constants.js';
import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount } from './shared.js';
import { config } from './config.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
import { emitChange } from './watchers.js';
import * as cache from './cache.js';
export function renameSync(oldPath, newPath) {

@@ -463,13 +463,2 @@ oldPath = normalizePath(oldPath);

}
for (const mount of mounts.keys()) {
if (!mount.startsWith(path)) {
continue;
}
const entry = mount.slice(path.length);
if (entry.includes('/') || entry.length == 0) {
// ignore FSs mounted in subdirectories and any FS mounted to `path`.
continue;
}
entries.push(entry);
}
// Iterate over entries and handle recursive case if needed

@@ -476,0 +465,0 @@ const values = [];

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

const promises = [];
for (const key of sync.keysSync()) {
promises.push(async.set(key, sync.getSync(key)));
for (const key of await async.keys()) {
promises.push(async.get(key).then(data => sync.setSync(key, data)));
}

@@ -97,0 +97,0 @@ await Promise.all(promises);

{
"name": "@zenfs/core",
"version": "1.2.8",
"version": "1.2.9",
"description": "A filesystem, anywhere",

@@ -61,2 +61,3 @@ "funding": {

"test": "tsx --test --experimental-test-coverage",
"pretest": "npm run build",
"build": "tsc -p tsconfig.json",

@@ -63,0 +64,0 @@ "build:docs": "typedoc",

@@ -537,2 +537,11 @@ import { credentials } from '../../credentials.js';

private async commitNew(path: string, type: FileType, mode: number, data: Uint8Array): Promise<Inode> {
/*
The root always exists.
If we don't check this prior to taking steps below,
we will create a file with name '' in root if path is '/'.
*/
if (path == '/') {
throw ErrnoError.With('EEXIST', path, 'commitNew');
}
await using tx = this.store.transaction();

@@ -545,11 +554,2 @@ const parentPath = dirname(path),

/*
The root always exists.
If we don't check this prior to taking steps below,
we will create a file with name '' in root should path == '/'.
*/
if (path === '/') {
throw ErrnoError.With('EEXIST', path, 'commitNew');
}
// Check if file already exists.

@@ -586,2 +586,11 @@ if (listing[fname]) {

protected commitNewSync(path: string, type: FileType, mode: number, data: Uint8Array = new Uint8Array()): Inode {
/*
The root always exists.
If we don't check this prior to taking steps below,
we will create a file with name '' in root if path is '/'.
*/
if (path == '/') {
throw ErrnoError.With('EEXIST', path, 'commitNew');
}
using tx = this.store.transaction();

@@ -594,11 +603,2 @@ const parentPath = dirname(path),

/*
The root always exists.
If we don't check this prior to taking steps below,
we will create a file with name '' in root should p == '/'.
*/
if (path === '/') {
throw ErrnoError.With('EEXIST', path, 'commitNew');
}
// Check if file already exists.

@@ -605,0 +605,0 @@ if (listing[fname]) {

import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
import { credentials } from './credentials.js';
import { DeviceFS, fullDevice, nullDevice, randomDevice, zeroDevice } from './devices.js';
import { DeviceFS } from './devices.js';
import * as cache from './emulation/cache.js';
import { config } from './emulation/config.js';
import * as fs from './emulation/index.js';
import type { AbsolutePath } from './emulation/path.js';
import { type MountObject } from './emulation/shared.js';
import { config } from './emulation/config.js';
import { Errno, ErrnoError } from './error.js';

@@ -155,2 +154,23 @@ import { FileSystem } from './filesystem.js';

/**
* Like `fs.mount`, but it also creates missing directories.
* @privateRemarks
* This is implemented as a separate function to avoid a circular dependency between emulation/shared.ts and other emulation layer files.
* @internal
*/
async function mount(path: string, mount: FileSystem): Promise<void> {
if (path == '/') {
fs.mount(path, mount);
return;
}
const stats = await fs.promises.stat(path).catch(() => null);
if (!stats) {
await fs.promises.mkdir(path, { recursive: true });
} else if (!stats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', path, 'configure');
}
fs.mount(path, mount);
}
/**
* Configures ZenFS with `configuration`

@@ -172,8 +192,5 @@ * @see Configuration

const devfs = new DeviceFS();
devfs.createDevice('/null', nullDevice);
devfs.createDevice('/zero', zeroDevice);
devfs.createDevice('/full', fullDevice);
devfs.createDevice('/random', randomDevice);
devfs.addDefaults();
await devfs.ready();
fs.mount('/dev', devfs);
await mount('/dev', devfs);
}

@@ -185,2 +202,5 @@

const toMount: [string, FileSystem][] = [];
let unmountRoot = false;
for (const [point, mountConfig] of Object.entries(configuration.mounts)) {

@@ -195,6 +215,9 @@ if (!point.startsWith('/')) {

configuration.mounts[point as keyof T & `/${string}`] = await resolveMountConfig(mountConfig);
if (point == '/') unmountRoot = true;
toMount.push([point, await resolveMountConfig(mountConfig)]);
}
fs.mountObject(configuration.mounts as MountObject);
if (unmountRoot) fs.umount('/');
await Promise.all(toMount.map(([point, fs]) => mount(point, fs)));
}

@@ -24,2 +24,5 @@ /**

/**
* @deprecated
*/
export const rootCredentials: Credentials = {

@@ -26,0 +29,0 @@ uid: 0,

@@ -249,2 +249,12 @@ /*

/**
* Adds default devices
*/
public addDefaults(): void {
this.createDevice('/null', nullDevice);
this.createDevice('/zero', zeroDevice);
this.createDevice('/full', fullDevice);
this.createDevice('/random', randomDevice);
}
public constructor() {

@@ -251,0 +261,0 @@ super(new InMemoryStore('devfs'));

@@ -16,7 +16,7 @@ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */

import * as cache from './cache.js';
import { config } from './config.js';
import * as constants from './constants.js';
import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js';
import { config } from './config.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js';
import { ReadStream, WriteStream } from './streams.js';

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

if (!stats) {
throw ErrnoError.With('ENOENT', path, 'readdir');
throw ErrnoError.With('ENOENT', path, 'rmdir');
}

@@ -741,13 +741,2 @@ if (!stats.isDirectory()) {

for (const point of mounts.keys()) {
if (point.startsWith(path)) {
const entry = point.slice(path.length);
if (entry.includes('/') || entry.length == 0) {
// ignore FSs mounted in subdirectories and any FS mounted to `path`.
continue;
}
entries.push(entry);
}
}
const values: (string | Dirent | Buffer)[] = [];

@@ -754,0 +743,0 @@ const addEntry = async (entry: string) => {

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

/**
* @deprecated
*/
export function mountObject(mounts: MountObject): void {

@@ -111,0 +114,0 @@ if ('/' in mounts) {

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

import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
import * as cache from './cache.js';
import { config } from './config.js';
import * as constants from './constants.js';
import { Dir, Dirent } from './dir.js';
import { dirname, join, parse } from './path.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, mounts, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js';
import { config } from './config.js';
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount, type InternalOptions, type ReaddirOptions } from './shared.js';
import { emitChange } from './watchers.js';
import * as cache from './cache.js';

@@ -476,14 +476,2 @@ export function renameSync(oldPath: fs.PathLike, newPath: fs.PathLike): void {

for (const mount of mounts.keys()) {
if (!mount.startsWith(path)) {
continue;
}
const entry = mount.slice(path.length);
if (entry.includes('/') || entry.length == 0) {
// ignore FSs mounted in subdirectories and any FS mounted to `path`.
continue;
}
entries.push(entry);
}
// Iterate over entries and handle recursive case if needed

@@ -490,0 +478,0 @@ const values: (string | Dirent | Buffer)[] = [];

@@ -83,4 +83,4 @@ import { StoreFS } from '../backends/store/fs.js';

const promises = [];
for (const key of sync.keysSync()) {
promises.push(async.set(key, sync.getSync(key)));
for (const key of await async.keys()) {
promises.push(async.get(key).then(data => sync.setSync(key, data)));
}

@@ -87,0 +87,0 @@

@@ -6,3 +6,18 @@ import assert from 'node:assert';

suite('Directory', () => {
const testDir = 'test-dir';
const testFiles = ['file1.txt', 'file2.txt', 'file3.txt'];
const testDirectories = ['subdir1', 'subdir2'];
await fs.promises.mkdir(testDir);
for (const file of testFiles) {
await fs.promises.writeFile(`${testDir}/${file}`, 'Sample content');
}
for (const dir of testDirectories) {
await fs.promises.mkdir(`${testDir}/${dir}`);
for (const file of ['file4.txt', 'file5.txt']) {
await fs.promises.writeFile(`${testDir}/${dir}/${file}`, 'Sample content');
}
}
suite('Directories', () => {
test('mkdir', async () => {

@@ -130,2 +145,69 @@ await fs.promises.mkdir('/one', 0o755);

});
test('readdir returns files and directories', async () => {
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true });
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name);
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name);
assert(testFiles.every(file => files.includes(file)));
assert(testDirectories.every(dir => dirs.includes(dir)));
});
test('readdirSync returns files and directories', () => {
const dirents = fs.readdirSync(testDir, { withFileTypes: true });
const files = dirents.filter(dirent => dirent.isFile()).map(dirent => dirent.name);
const dirs = dirents.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name);
assert(testFiles.every(file => files.includes(file)));
assert(testDirectories.every(dir => dirs.includes(dir)));
});
test('readdir returns Dirent objects', async () => {
const dirents = await fs.promises.readdir(testDir, { withFileTypes: true });
assert(dirents[0] instanceof fs.Dirent);
});
test('readdirSync returns Dirent objects', () => {
const dirents = fs.readdirSync(testDir, { withFileTypes: true });
assert(dirents[0] instanceof fs.Dirent);
});
test('readdir works without withFileTypes option', async () => {
const files = await fs.promises.readdir(testDir);
assert(testFiles.every(entry => files.includes(entry)));
assert(testDirectories.every(entry => files.includes(entry)));
});
test('readdirSync works without withFileTypes option', () => {
const files = fs.readdirSync(testDir);
assert(testFiles.every(entry => files.includes(entry)));
assert(testDirectories.every(entry => files.includes(entry)));
});
test('readdir returns files recursively', async () => {
const entries = await fs.promises.readdir(testDir, { recursive: true });
assert(entries.includes('file1.txt'));
assert(entries.includes('subdir1/file4.txt'));
assert(entries.includes('subdir2/file5.txt'));
});
test('readdir returns Dirent recursively', async () => {
const entries = await fs.promises.readdir(testDir, { recursive: true, withFileTypes: true });
assert(entries.find(entry => entry.path === 'file1.txt'));
assert(entries.find(entry => entry.path === 'subdir1/file4.txt'));
assert(entries.find(entry => entry.path === 'subdir2/file5.txt'));
});
// New test for readdirSync with recursive: true
test('readdirSync returns files recursively', () => {
const entries = fs.readdirSync(testDir, { recursive: true });
assert(entries.includes('file1.txt'));
assert(entries.includes('subdir1/file4.txt'));
assert(entries.includes('subdir2/file5.txt'));
});
test('Cyrillic file names', () => {
fs.writeFileSync('/мой-файл.txt', 'HELLO!', 'utf-8');
assert(fs.readdirSync('/').includes('мой-файл.txt'));
});
});
import assert from 'node:assert';
import { suite, test } from 'node:test';
import { credentials } from '../../dist/credentials.js';
import { R_OK, W_OK, X_OK } from '../../dist/emulation/constants.js';

@@ -9,3 +10,42 @@ import { join } from '../../dist/emulation/path.js';

const asyncMode = 0o777;
const syncMode = 0o644;
const file = 'a.js';
suite('Permissions', () => {
test('chmod', async () => {
await fs.promises.chmod(file, asyncMode.toString(8));
const stats = await fs.promises.stat(file);
assert.equal(stats.mode & 0o777, asyncMode);
fs.chmodSync(file, syncMode);
assert.equal(fs.statSync(file).mode & 0o777, syncMode);
});
test('fchmod', async () => {
const handle = await fs.promises.open(file, 'a', 0o644);
await handle.chmod(asyncMode);
const stats = await handle.stat();
assert.equal(stats.mode & 0o777, asyncMode);
fs.fchmodSync(handle.fd, syncMode);
assert.equal(fs.statSync(file).mode & 0o777, syncMode);
});
test('lchmod', async () => {
const link = 'symbolic-link';
await fs.promises.symlink(file, link);
await fs.promises.lchmod(link, asyncMode);
const stats = await fs.promises.lstat(link);
assert.equal(stats.mode & 0o777, asyncMode);
fs.lchmodSync(link, syncMode);
assert.equal(fs.lstatSync(link).mode & 0o777, syncMode);
});
async function test_item(path: string): Promise<void> {

@@ -31,3 +71,3 @@ const stats = await fs.promises.stat(path).catch((error: ErrnoError) => {

for (const dir of await fs.promises.readdir(path)) {
await test_item(join(path, dir));
await test('Access controls: ' + join(path, dir), () => test_item(join(path, dir)));
}

@@ -53,3 +93,6 @@ } else {

test('recursive', () => test_item('/'));
const copy = { ...credentials };
Object.assign(credentials, { uid: 1000, gid: 1000, euid: 1000, egid: 1000 });
test('Access controls: /', () => test_item('/'));
Object.assign(credentials, copy);
});
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