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
1
Versions
165
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.12.0 to 0.12.1

20

dist/backends/backend.d.ts

@@ -7,3 +7,3 @@ import type { RequiredKeys } from 'utilium';

*/
type OptionsConfig<T> = {
export type OptionsConfig<T> = {
[K in keyof T]: {

@@ -58,6 +58,15 @@ /**

}
type OptionsOf<T extends Backend> = T extends Backend<FileSystem, infer TOptions> ? TOptions : never;
/**
* Gets the options type of a backend
* @internal
*/
export type OptionsOf<T extends Backend> = T extends Backend<FileSystem, infer TOptions> ? TOptions : never;
/**
* Gets the FileSystem type for a backend
* @internal
*/
export type FilesystemOf<T extends Backend> = T extends Backend<infer FS> ? FS : never;
/**
* @internal
*/
export declare function isBackend(arg: unknown): arg is Backend;

@@ -68,3 +77,3 @@ /**

*/
export declare function checkOptions<T extends Backend>(backend: T, opts: Partial<OptionsOf<T>> & Record<string, unknown>): Promise<void>;
export declare function checkOptions<T extends Backend>(backend: T, opts: Record<string, unknown>): Promise<void>;
/**

@@ -78,4 +87,5 @@ * Specifies a file system backend type and its options.

*/
export type BackendConfiguration<T extends Backend = Backend> = OptionsOf<T> & {
export type BackendConfiguration<T extends Backend> = OptionsOf<T> & {
backend: T;
disableAsyncCache?: boolean;
};

@@ -85,3 +95,3 @@ /**

*/
export declare function isBackendConfig(arg: unknown): arg is BackendConfiguration;
export declare function isBackendConfig<T extends Backend>(arg: unknown): arg is BackendConfiguration<T>;
export {};
import { Stats, StatsLike } from '../../stats.js';
/**
* An Index in JSON form
* @internal
*/
export interface IndexData {

@@ -9,2 +13,3 @@ version: 1;

* An index of files
* @internal
*/

@@ -11,0 +16,0 @@ export declare class Index extends Map<string, Stats> {

@@ -9,2 +9,3 @@ import { isJSON } from 'utilium';

* An index of files
* @internal
*/

@@ -11,0 +12,0 @@ export class Index extends Map {

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

declare const PortFS_base: (abstract new (...args: any[]) => {
_sync: FileSystem;
_disableSync: boolean;
_sync?: FileSystem | undefined;
queueDone(): Promise<void>;

@@ -46,0 +47,0 @@ metadata(): FileSystemMetadata;

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

import type { Backend, BackendConfiguration } from './backends/backend.js';
import { FileSystem } from './filesystem.js';
import type { Backend, BackendConfiguration, FilesystemOf } from './backends/backend.js';
import type { AbsolutePath } from './emulation/path.js';

@@ -7,3 +6,3 @@ /**

*/
export type MountConfiguration<FS extends FileSystem = FileSystem, TOptions extends object = object> = FS | BackendConfiguration<Backend<FS, TOptions>> | Backend<FS, TOptions>;
export type MountConfiguration<T extends Backend> = FilesystemOf<T> | BackendConfiguration<T> | T;
/**

@@ -13,15 +12,38 @@ * Retrieve a file system with the given configuration.

*/
export declare function resolveMountConfig<FS extends FileSystem, TOptions extends object = object>(config: MountConfiguration<FS, TOptions>, _depth?: number): Promise<FS>;
export declare function resolveMountConfig<T extends Backend>(config: MountConfiguration<T>, _depth?: number): Promise<FilesystemOf<T>>;
type ConfigMounts = {
[K in AbsolutePath]: Backend;
};
/**
* Configuration
*/
export interface Configuration {
mounts: Record<AbsolutePath, MountConfiguration>;
uid?: number;
gid?: number;
export interface Configuration<T extends ConfigMounts> {
/**
* An object mapping mount points to mount configuration
*/
mounts: {
[K in keyof T & AbsolutePath]: MountConfiguration<T[K]>;
};
/**
* The uid to use
*/
uid: number;
/**
* The gid to use
*/
gid: number;
/**
* If set, disables the sync cache and sync operations on async file systems.
*/
disableAsyncCache: boolean;
}
/**
* Creates filesystems with the given configuration, and initializes ZenFS with it.
* @see Configuration for more info on the configuration object.
* Configures ZenFS with single mount point /
*/
export declare function configure<T extends MountConfiguration | Configuration>(config: T | Configuration): Promise<void>;
export declare function configure<T extends Backend>(config: MountConfiguration<T>): Promise<void>;
/**
* Configures ZenFS with the given configuration
* @see Configuration
*/
export declare function configure<T extends ConfigMounts>(config: Partial<Configuration<T>>): Promise<void>;
export {};

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

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

@@ -43,3 +43,6 @@ function isMountConfig(arg) {

checkOptions(backend, config);
const mount = await backend.create(config);
const mount = (await backend.create(config));
if ('_disableSync' in mount) {
mount._disableSync = config.disableAsyncCache || false;
}
await mount.ready();

@@ -49,4 +52,4 @@ return mount;

/**
* Creates filesystems with the given configuration, and initializes ZenFS with it.
* @see Configuration for more info on the configuration object.
* Configures ZenFS with the given configuration
* @see Configuration
*/

@@ -60,10 +63,16 @@ export async function configure(config) {

}
for (const [point, value] of Object.entries(config.mounts)) {
setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
if (!config.mounts) {
return;
}
for (const [point, mountConfig] of Object.entries(config.mounts)) {
if (!point.startsWith('/')) {
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
}
config.mounts[point] = await resolveMountConfig(value);
if (isBackendConfig(mountConfig)) {
mountConfig.disableAsyncCache ?? (mountConfig.disableAsyncCache = config.disableAsyncCache || false);
}
config.mounts[point] = await resolveMountConfig(mountConfig);
}
fs.mountObject(config.mounts);
setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
}
/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
/// <reference types="node" resolution-mode="require"/>
import { Buffer } from 'buffer';
import type * as fs from 'node:fs';

@@ -5,0 +6,0 @@ import type { FileContents } from '../filesystem.js';

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

import { Buffer } from 'buffer';
import { ErrnoError, Errno } from '../error.js';

@@ -176,3 +177,3 @@ import { BigIntStats } from '../stats.js';

position = cbPosOff;
encoding = (typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8');
encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
cb = typeof cbPos === 'function' ? cbPos : cb;

@@ -201,3 +202,3 @@ break;

position = typeof cbPos === 'number' ? cbPos : null;
const _cb = (typeof cbPos === 'function' ? cbPos : cb);
const _cb = typeof cbPos === 'function' ? cbPos : cb;
handle

@@ -204,0 +205,0 @@ .write(buffer, offset, length, position)

/// <reference types="node" resolution-mode="require"/>
import type { ParsedPath } from 'node:path';
/**
* An absolute path
*/
export type AbsolutePath = `/${string}`;

@@ -11,3 +14,3 @@ export declare let cwd: string;

export declare function normalize(path: string): string;
export declare function isAbsolute(path: string): boolean;
export declare function isAbsolute(path: string): path is AbsolutePath;
export declare function join(...parts: string[]): string;

@@ -14,0 +17,0 @@ export declare function relative(from: string, to: string): string;

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

position = typeof posOrOff === 'number' ? posOrOff : null;
const encoding = (typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8';
offset = 0;

@@ -412,16 +412,28 @@ buffer = Buffer.from(data, encoding);

}
catch (e) {
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats = await fs.stat(dirname(resolved), cred);
if (parentStats && !parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
catch (_) {
const original = _;
if (original.code != 'ENOENT') {
throw original;
}
try {
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats = await fs.stat(dirname(resolved), cred);
if (parentStats && !parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
}
}
catch (_) {
const ex = _;
ex.stack += '\n<original>\n';
ex.stack += original.stack;
throw ex;
}
}

@@ -428,0 +440,0 @@ }

@@ -102,17 +102,29 @@ import { Buffer } from 'buffer';

}
catch (e) {
// File does not exist.
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats = wrap('statSync', resolveSymlinks, dirname(path), cred);
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred);
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
catch (_) {
const original = _;
if (original.code != 'ENOENT') {
throw original;
}
try {
// File does not exist.
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats = wrap('statSync', resolveSymlinks, dirname(path), cred);
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred);
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
}
}
catch (_) {
const ex = _;
ex.stack += '\n<original>\n';
ex.stack += original.stack;
throw ex;
}
}

@@ -289,3 +301,3 @@ if (!stats.hasAccess(mode, cred)) {

position = typeof posOrOff === 'number' ? posOrOff : null;
const encoding = (typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8';
offset = 0;

@@ -292,0 +304,0 @@ buffer = Buffer.from(data, encoding);

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

*/
path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, { maxByteLength: size_max }))) {
path, flag, stats, _buffer = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))) {
super();

@@ -341,12 +341,12 @@ this.fs = fs;

}
const endFp = position + length;
if (endFp > this.stats.size) {
this.stats.size = endFp;
if (endFp > this._buffer.byteLength) {
if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength <= endFp) {
this._buffer.buffer.resize(endFp);
const end = position + length;
if (end > this.stats.size) {
this.stats.size = end;
if (end > this._buffer.byteLength) {
if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength <= end) {
this._buffer.buffer.resize(end);
}
else {
// Extend the buffer!
const newBuffer = new Uint8Array(new ArrayBuffer(endFp, { maxByteLength: size_max }));
const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }));
newBuffer.set(this._buffer);

@@ -353,0 +353,0 @@ this._buffer = newBuffer;

@@ -25,2 +25,14 @@ import { type Cred } from './cred.js';

freeSpace: number;
/**
* If set, disables File from using a resizable array buffer.
* @default false
*/
noResizableBuffers: boolean;
/**
* If set, disables caching on async file systems.
* This means *sync operations will not work*.
* It has no affect on sync file systems.
* @default false
*/
noAsyncCache: boolean;
}

@@ -39,3 +51,3 @@ /**

/**
* Get metadata about the current file syste,
* Get metadata about the current file system
*/

@@ -173,8 +185,16 @@ metadata(): FileSystemMetadata;

* @internal
* Note: `_*` should be treated like protected.
* Protected can't be used because of TS quirks however.
*/
declare abstract class AsyncFS extends FileSystem {
/**
* @access protected
* @hidden
*/
abstract _sync: FileSystem;
_disableSync: boolean;
/**
* @access protected
* @hidden
*/
abstract _sync?: FileSystem;
queueDone(): Promise<void>;

@@ -181,0 +201,0 @@ metadata(): FileSystemMetadata;

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

/**
* Get metadata about the current file syste,
* Get metadata about the current file system
*/

@@ -26,7 +26,8 @@ metadata() {

freeSpace: 0,
noResizableBuffers: false,
noAsyncCache: false,
};
}
constructor(options) {
// unused
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
constructor(options) { }
async ready() { }

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

catch (e) {
return false;
return e.code != 'ENOENT';
}

@@ -55,3 +56,3 @@ }

catch (e) {
return false;
return e.code != 'ENOENT';
}

@@ -124,2 +125,3 @@ }

this._isInitialized = false;
this._disableSync = false;
}

@@ -136,7 +138,8 @@ get _queueRunning() {

async ready() {
await this._sync.ready();
await super.ready();
if (this._isInitialized) {
if (this._isInitialized || this._disableSync) {
return;
}
this.checkSync();
await this._sync.ready();
try {

@@ -151,3 +154,18 @@ await this.crossCopy('/');

}
metadata() {
return {
...super.metadata(),
noAsyncCache: this._disableSync,
};
}
checkSync(path, syscall) {
if (this._disableSync) {
throw new ErrnoError(Errno.ENOTSUP, 'Sync caching has been disabled for this async file system', path, syscall);
}
if (!this._sync) {
throw new ErrnoError(Errno.ENOTSUP, 'No sync cache is attached to this async file system', path, syscall);
}
}
renameSync(oldPath, newPath, cred) {
this.checkSync(oldPath, 'rename');
this._sync.renameSync(oldPath, newPath, cred);

@@ -157,5 +175,7 @@ this.queue('rename', oldPath, newPath, cred);

statSync(path, cred) {
this.checkSync(path, 'stat');
return this._sync.statSync(path, cred);
}
createFileSync(path, flag, mode, cred) {
this.checkSync(path, 'createFile');
this._sync.createFileSync(path, flag, mode, cred);

@@ -166,2 +186,3 @@ this.queue('createFile', path, flag, mode, cred);

openFileSync(path, flag, cred) {
this.checkSync(path, 'openFile');
const file = this._sync.openFileSync(path, flag, cred);

@@ -174,2 +195,3 @@ const stats = file.statSync();

unlinkSync(path, cred) {
this.checkSync(path, 'unlinkSync');
this._sync.unlinkSync(path, cred);

@@ -179,2 +201,3 @@ this.queue('unlink', path, cred);

rmdirSync(path, cred) {
this.checkSync(path, 'rmdir');
this._sync.rmdirSync(path, cred);

@@ -184,2 +207,3 @@ this.queue('rmdir', path, cred);

mkdirSync(path, mode, cred) {
this.checkSync(path, 'mkdir');
this._sync.mkdirSync(path, mode, cred);

@@ -189,5 +213,7 @@ this.queue('mkdir', path, mode, cred);

readdirSync(path, cred) {
this.checkSync(path, 'readdir');
return this._sync.readdirSync(path, cred);
}
linkSync(srcpath, dstpath, cred) {
this.checkSync(srcpath, 'link');
this._sync.linkSync(srcpath, dstpath, cred);

@@ -197,2 +223,3 @@ this.queue('link', srcpath, dstpath, cred);

syncSync(path, data, stats) {
this.checkSync(path, 'sync');
this._sync.syncSync(path, data, stats);

@@ -202,2 +229,3 @@ this.queue('sync', path, data, stats);

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

@@ -209,2 +237,3 @@ }

async crossCopy(path) {
this.checkSync(path, 'crossCopy');
const stats = await this.stat(path, rootCred);

@@ -211,0 +240,0 @@ if (stats.isDirectory()) {

@@ -82,3 +82,3 @@ import { S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, S_IRWXG, S_IRWXO, S_IRWXU } from './emulation/constants.js';

const currentTime = Date.now();
const resolveT = (val, _default) => (typeof val == this._typename ? val : this._convert(typeof val == this._typename_inverse ? val : _default));
const resolveT = (val, _default) => typeof val == this._typename ? val : this._convert(typeof val == this._typename_inverse ? val : _default);
this.atimeMs = resolveT(atimeMs, currentTime);

@@ -85,0 +85,0 @@ this.mtimeMs = resolveT(mtimeMs, currentTime);

{
"name": "@zenfs/core",
"version": "0.12.0",
"version": "0.12.1",
"description": "A filesystem in your browser",

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

"readable-stream": "^4.5.2",
"utilium": "^0.2.1"
"utilium": "^0.4.0"
},

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

@@ -11,3 +11,3 @@ import type { RequiredKeys } from 'utilium';

*/
type OptionsConfig<T> = {
export type OptionsConfig<T> = {
[K in keyof T]: {

@@ -70,5 +70,13 @@ /**

type OptionsOf<T extends Backend> = T extends Backend<FileSystem, infer TOptions> ? TOptions : never;
/**
* Gets the options type of a backend
* @internal
*/
export type OptionsOf<T extends Backend> = T extends Backend<FileSystem, infer TOptions> ? TOptions : never;
type FilesystemOf<T extends Backend> = T extends Backend<infer FS> ? FS : never;
/**
* Gets the FileSystem type for a backend
* @internal
*/
export type FilesystemOf<T extends Backend> = T extends Backend<infer FS> ? FS : never;

@@ -86,3 +94,3 @@ /**

*/
export async function checkOptions<T extends Backend>(backend: T, opts: Partial<OptionsOf<T>> & Record<string, unknown>): Promise<void> {
export async function checkOptions<T extends Backend>(backend: T, opts: Record<string, unknown>): Promise<void> {
if (typeof opts != 'object' || opts === null) {

@@ -144,4 +152,5 @@ throw new ErrnoError(Errno.EINVAL, 'Invalid options');

*/
export type BackendConfiguration<T extends Backend = Backend> = OptionsOf<T> & {
export type BackendConfiguration<T extends Backend> = OptionsOf<T> & {
backend: T;
disableAsyncCache?: boolean;
};

@@ -152,4 +161,4 @@

*/
export function isBackendConfig(arg: unknown): arg is BackendConfiguration {
export function isBackendConfig<T extends Backend>(arg: unknown): arg is BackendConfiguration<T> {
return arg != null && typeof arg == 'object' && 'backend' in arg && isBackend(arg.backend);
}

@@ -7,2 +7,6 @@ import { isJSON } from 'utilium';

/**
* An Index in JSON form
* @internal
*/
export interface IndexData {

@@ -17,2 +21,3 @@ version: 1;

* An index of files
* @internal
*/

@@ -19,0 +24,0 @@ export class Index extends Map<string, Stats> {

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

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

@@ -12,5 +13,5 @@ /**

*/
export type MountConfiguration<FS extends FileSystem = FileSystem, TOptions extends object = object> = FS | BackendConfiguration<Backend<FS, TOptions>> | Backend<FS, TOptions>;
export type MountConfiguration<T extends Backend> = FilesystemOf<T> | BackendConfiguration<T> | T;
function isMountConfig(arg: unknown): arg is MountConfiguration {
function isMountConfig<T extends Backend>(arg: unknown): arg is MountConfiguration<T> {
return isBackendConfig(arg) || isBackend(arg) || arg instanceof FileSystem;

@@ -23,3 +24,3 @@ }

*/
export async function resolveMountConfig<FS extends FileSystem, TOptions extends object = object>(config: MountConfiguration<FS, TOptions>, _depth = 0): Promise<FS> {
export async function resolveMountConfig<T extends Backend>(config: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
if (typeof config !== 'object' || config == null) {

@@ -38,3 +39,3 @@ throw new ErrnoError(Errno.EINVAL, 'Invalid options on mount configuration');

if (isBackend(config)) {
config = { backend: config } as BackendConfiguration<Backend<FS, TOptions>>;
config = { backend: config } as BackendConfiguration<T>;
}

@@ -55,3 +56,3 @@

(<Record<string, FileSystem>>config)[key] = await resolveMountConfig(value, ++_depth);
(config as Record<string, FileSystem>)[key] = await resolveMountConfig(value, ++_depth);
}

@@ -65,3 +66,7 @@

checkOptions(backend, config);
const mount = await backend.create(config);
const mount = (await backend.create(config)) as FilesystemOf<T>;
if ('_disableSync' in mount) {
type AsyncFS = InstanceType<ReturnType<typeof Async<new () => FilesystemOf<T>>>>;
(mount as AsyncFS)._disableSync = config.disableAsyncCache || false;
}
await mount.ready();

@@ -71,16 +76,42 @@ return mount;

type ConfigMounts = { [K in AbsolutePath]: Backend };
/**
* Configuration
*/
export interface Configuration {
mounts: Record<AbsolutePath, MountConfiguration>;
uid?: number;
gid?: number;
export interface Configuration<T extends ConfigMounts> {
/**
* An object mapping mount points to mount configuration
*/
mounts: { [K in keyof T & AbsolutePath]: MountConfiguration<T[K]> };
/**
* The uid to use
*/
uid: number;
/**
* The gid to use
*/
gid: number;
/**
* If set, disables the sync cache and sync operations on async file systems.
*/
disableAsyncCache: boolean;
}
/**
* Creates filesystems with the given configuration, and initializes ZenFS with it.
* @see Configuration for more info on the configuration object.
* Configures ZenFS with single mount point /
*/
export async function configure<T extends MountConfiguration | Configuration>(config: T | Configuration): Promise<void> {
export async function configure<T extends Backend>(config: MountConfiguration<T>): Promise<void>;
/**
* Configures ZenFS with the given configuration
* @see Configuration
*/
export async function configure<T extends ConfigMounts>(config: Partial<Configuration<T>>): Promise<void>;
/**
* Configures ZenFS with the given configuration
* @see Configuration
*/
export async function configure(config: MountConfiguration<Backend> | Partial<Configuration<ConfigMounts>>): Promise<void> {
const uid = 'uid' in config ? config.uid || 0 : 0;

@@ -91,14 +122,24 @@ const gid = 'gid' in config ? config.gid || 0 : 0;

// single FS
config = { mounts: { '/': config } };
config = { mounts: { '/': config } } as Partial<Configuration<ConfigMounts>>;
}
for (const [point, value] of Object.entries(config.mounts) as [AbsolutePath, MountConfiguration][]) {
setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
if (!config.mounts) {
return;
}
for (const [point, mountConfig] of Object.entries(config.mounts) as Entries<typeof config.mounts>) {
if (!point.startsWith('/')) {
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
}
config.mounts[point] = await resolveMountConfig(value);
if (isBackendConfig(mountConfig)) {
mountConfig.disableAsyncCache ??= config.disableAsyncCache || false;
}
config.mounts[point] = await resolveMountConfig(mountConfig);
}
fs.mountObject(config.mounts as MountObject);
setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
}

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

import { Buffer } from 'buffer';
import type * as fs from 'node:fs';

@@ -55,3 +56,3 @@ import { ErrnoError, Errno } from '../error.js';

.stat(path, typeof options != 'function' ? options : {})
.then(stats => (<Callback<[Stats] | [BigIntStats]>>callback)(undefined, <any>stats))
.then(stats => (callback as Callback<[Stats] | [BigIntStats]>)(undefined, stats as any))
.catch(callback);

@@ -75,4 +76,4 @@ }

promises
.lstat(path, typeof options != 'function' ? options : <object>{})
.then(stats => (<Callback<[Stats] | [BigIntStats]>>callback)(undefined, stats))
.lstat(path, typeof options != 'function' ? options : ({} as object))
.then(stats => (callback as Callback<[Stats] | [BigIntStats]>)(undefined, stats))
.catch(callback);

@@ -166,3 +167,3 @@ }

.readFile(filename, typeof options === 'function' ? null : options)
.then(data => (<Callback<[string | Uint8Array]>>cb)(undefined, data))
.then(data => (cb as Callback<[string | Uint8Array]>)(undefined, data))
.catch(cb);

@@ -237,3 +238,3 @@ }

.stat()
.then(stats => (<Callback<[Stats | BigIntStats]>>cb)(undefined, typeof options == 'object' && options?.bigint ? new BigIntStats(stats) : stats))
.then(stats => (cb as Callback<[Stats | BigIntStats]>)(undefined, typeof options == 'object' && options?.bigint ? new BigIntStats(stats) : stats))
.catch(cb);

@@ -336,3 +337,3 @@ }

position = cbPosOff;
encoding = <BufferEncoding>(typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8');
encoding = typeof cbLenEnc === 'string' ? (cbLenEnc as BufferEncoding) : 'utf8';
cb = typeof cbPos === 'function' ? cbPos : cb;

@@ -343,3 +344,3 @@ break;

cb = typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPos === 'function' ? cbPos : cb;
(<Callback<[number, Uint8Array | string]>>cb)(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
(cb as Callback<[number, Uint8Array | string]>)(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
return;

@@ -351,3 +352,3 @@ }

const _cb = <Callback<[number, string]>>cb;
const _cb = cb as Callback<[number, string]>;

@@ -364,3 +365,3 @@ handle

position = typeof cbPos === 'number' ? cbPos : null;
const _cb = <Callback<[number, Uint8Array]>>(typeof cbPos === 'function' ? cbPos : cb);
const _cb = typeof cbPos === 'function' ? cbPos : (cb as Callback<[number, Uint8Array]>);
handle

@@ -537,3 +538,3 @@ .write(buffer, offset, length, position)

.readlink(path)
.then(result => (<Callback<[string | Uint8Array]>>callback)(undefined, result))
.then(result => (callback as Callback<[string | Uint8Array]>)(undefined, result))
.catch(callback);

@@ -828,4 +829,4 @@ }

promises
.mkdtemp(prefix, typeof options != 'function' ? <fs.EncodingOption>options : null)
.then(result => (<Callback<[string | Buffer]>>callback)(undefined, result))
.mkdtemp(prefix, typeof options != 'function' ? (options as fs.EncodingOption) : null)
.then(result => (callback as Callback<[string | Buffer]>)(undefined, result))
.catch(callback);

@@ -901,3 +902,3 @@ }

.statfs(path, typeof options === 'function' ? undefined : options)
.then(result => (<Callback<[StatsFs | BigIntStatsFs]>>callback)(undefined, result))
.then(result => (callback as Callback<[StatsFs | BigIntStatsFs]>)(undefined, result))
.catch(callback);

@@ -904,0 +905,0 @@ }

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

/**
* An absolute path
*/
export type AbsolutePath = `/${string}`;

@@ -160,3 +163,3 @@

export function isAbsolute(path: string): boolean {
export function isAbsolute(path: string): path is AbsolutePath {
return path.startsWith('/');

@@ -163,0 +166,0 @@ }

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

position = typeof posOrOff === 'number' ? posOrOff : null;
const encoding = <BufferEncoding>(typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : ('utf8' as BufferEncoding);
offset = 0;

@@ -516,16 +516,27 @@ buffer = Buffer.from(data, encoding);

}
} catch (e) {
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats: Stats = await fs.stat(dirname(resolved), cred);
if (parentStats && !parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
} catch (_) {
const original = _ as ErrnoError;
if (original.code != 'ENOENT') {
throw original;
}
try {
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats: Stats = await fs.stat(dirname(resolved), cred);
if (parentStats && !parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return new FileHandle(await fs.createFile(resolved, flag, mode, cred));
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid file flag');
}
} catch (_) {
const ex = _ as ErrnoError;
ex.stack += '\n<original>\n';
ex.stack += (original as Error).stack;
throw ex;
}
}

@@ -532,0 +543,0 @@ }

@@ -132,17 +132,28 @@ import { Buffer } from 'buffer';

stats = wrap('statSync', resolveSymlinks, path, cred);
} catch (e) {
// File does not exist.
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats: Stats = wrap('statSync', resolveSymlinks, dirname(path), cred);
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred);
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
} catch (_) {
const original = _ as ErrnoError;
if (original.code != 'ENOENT') {
throw original;
}
try {
// File does not exist.
switch (pathNotExistsAction(flag)) {
case ActionType.CREATE:
// Ensure parent exists.
const parentStats: Stats = wrap('statSync', resolveSymlinks, dirname(path), cred);
if (!parentStats.isDirectory()) {
throw ErrnoError.With('ENOTDIR', dirname(path), '_open');
}
return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred);
case ActionType.THROW:
throw ErrnoError.With('ENOENT', path, '_open');
default:
throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.');
}
} catch (_) {
const ex = _ as ErrnoError;
ex.stack += '\n<original>\n';
ex.stack += (original as Error).stack;
throw ex;
}
}

@@ -376,3 +387,3 @@ if (!stats.hasAccess(mode, cred)) {

position = typeof posOrOff === 'number' ? posOrOff : null;
const encoding = <BufferEncoding>(typeof lenOrEnc === 'string' ? lenOrEnc : 'utf8');
const encoding = typeof lenOrEnc === 'string' ? lenOrEnc : ('utf8' as BufferEncoding);
offset = 0;

@@ -385,3 +396,3 @@ buffer = Buffer.from(data, encoding);

offset = posOrOff!;
length = <number>lenOrEnc;
length = lenOrEnc as number;
position = typeof pos === 'number' ? pos : null;

@@ -514,3 +525,3 @@ }

}
return <string[] | Dirent[] | Buffer[]>entries.map((entry: string) => {
return entries.map((entry: string) => {
if (typeof options == 'object' && options?.withFileTypes) {

@@ -525,3 +536,3 @@ return new Dirent(entry, statSync(join(path.toString(), entry)));

return entry;
});
}) as string[] | Dirent[] | Buffer[];
}

@@ -528,0 +539,0 @@ readdirSync satisfies typeof fs.readdirSync;

@@ -282,3 +282,3 @@ /**

super(message);
this.code = <keyof typeof Errno>Errno[errno];
this.code = Errno[errno] as keyof typeof Errno;
this.message = `${this.code}: ${message}${this.path ? `, '${this.path}'` : ''}`;

@@ -285,0 +285,0 @@ }

@@ -386,3 +386,3 @@ import type { FileReadResult } from 'node:fs/promises';

public readonly stats: Stats,
protected _buffer: Uint8Array = new Uint8Array(new ArrayBuffer(0, { maxByteLength: size_max }))
protected _buffer: Uint8Array = new Uint8Array(new ArrayBuffer(0, fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }))
) {

@@ -551,11 +551,12 @@ super();

}
const endFp = position + length;
if (endFp > this.stats.size) {
this.stats.size = endFp;
if (endFp > this._buffer.byteLength) {
if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength! <= endFp) {
this._buffer.buffer.resize(endFp);
const end = position + length;
if (end > this.stats.size) {
this.stats.size = end;
if (end > this._buffer.byteLength) {
if (this._buffer.buffer.resizable && this._buffer.buffer.maxByteLength! <= end) {
this._buffer.buffer.resize(end);
} else {
// Extend the buffer!
const newBuffer = new Uint8Array(new ArrayBuffer(endFp, { maxByteLength: size_max }));
const newBuffer = new Uint8Array(new ArrayBuffer(end, this.fs.metadata().noResizableBuffers ? {} : { maxByteLength: size_max }));
newBuffer.set(this._buffer);

@@ -562,0 +563,0 @@ this._buffer = newBuffer;

@@ -33,2 +33,16 @@ import type { ExtractProperties } from 'utilium';

freeSpace: number;
/**
* If set, disables File from using a resizable array buffer.
* @default false
*/
noResizableBuffers: boolean;
/**
* If set, disables caching on async file systems.
* This means *sync operations will not work*.
* It has no affect on sync file systems.
* @default false
*/
noAsyncCache: boolean;
}

@@ -48,3 +62,3 @@

/**
* Get metadata about the current file syste,
* Get metadata about the current file system
*/

@@ -57,8 +71,9 @@ public metadata(): FileSystemMetadata {

freeSpace: 0,
noResizableBuffers: false,
noAsyncCache: false,
};
}
public constructor(options?: object) {
// unused
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public constructor(options?: object) {}

@@ -163,3 +178,3 @@ public async ready(): Promise<void> {}

} catch (e) {
return false;
return (e as ErrnoError).code != 'ENOENT';
}

@@ -176,3 +191,3 @@ }

} catch (e) {
return false;
return (e as ErrnoError).code != 'ENOENT';
}

@@ -206,15 +221,15 @@ }

declare abstract class SyncFS extends FileSystem {
metadata(): FileSystemMetadata;
ready(): Promise<void>;
exists(path: string, cred: Cred): Promise<boolean>;
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
stat(path: string, cred: Cred): Promise<Stats>;
createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
openFile(path: string, flag: string, 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[]>;
link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
public metadata(): FileSystemMetadata;
public ready(): Promise<void>;
public exists(path: string, cred: Cred): Promise<boolean>;
public rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
public stat(path: string, cred: Cred): Promise<Stats>;
public createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
public openFile(path: string, flag: string, cred: Cred): Promise<File>;
public unlink(path: string, cred: Cred): Promise<void>;
public rmdir(path: string, cred: Cred): Promise<void>;
public mkdir(path: string, mode: number, cred: Cred): Promise<void>;
public readdir(path: string, cred: Cred): Promise<string[]>;
public link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
public sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
}

@@ -277,21 +292,29 @@

* @internal
* Note: `_*` should be treated like protected.
* Protected can't be used because of TS quirks however.
*/
declare abstract class AsyncFS extends FileSystem {
/**
* @access protected
* @hidden
*/
abstract _sync: FileSystem;
queueDone(): Promise<void>;
metadata(): FileSystemMetadata;
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;
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
_disableSync: boolean;
/**
* @access protected
* @hidden
*/
abstract _sync?: FileSystem;
public queueDone(): Promise<void>;
public metadata(): FileSystemMetadata;
public ready(): Promise<void>;
public renameSync(oldPath: string, newPath: string, cred: Cred): void;
public statSync(path: string, cred: Cred): Stats;
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
public openFileSync(path: string, flag: string, cred: Cred): File;
public unlinkSync(path: string, cred: Cred): void;
public rmdirSync(path: string, cred: Cred): void;
public mkdirSync(path: string, mode: number, cred: Cred): void;
public readdirSync(path: string, cred: Cred): string[];
public linkSync(srcpath: string, dstpath: string, cred: Cred): void;
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
}

@@ -341,11 +364,15 @@

abstract _sync: FileSystem;
_disableSync: boolean = false;
abstract _sync?: FileSystem;
public async ready(): Promise<void> {
await this._sync.ready();
await super.ready();
if (this._isInitialized) {
if (this._isInitialized || this._disableSync) {
return;
}
this.checkSync();
await this._sync.ready();
try {

@@ -360,3 +387,20 @@ await this.crossCopy('/');

public metadata(): FileSystemMetadata {
return {
...super.metadata(),
noAsyncCache: this._disableSync,
};
}
protected checkSync(path?: string, syscall?: string): asserts this is { _sync: FileSystem } {
if (this._disableSync) {
throw new ErrnoError(Errno.ENOTSUP, 'Sync caching has been disabled for this async file system', path, syscall);
}
if (!this._sync) {
throw new ErrnoError(Errno.ENOTSUP, 'No sync cache is attached to this async file system', path, syscall);
}
}
public renameSync(oldPath: string, newPath: string, cred: Cred): void {
this.checkSync(oldPath, 'rename');
this._sync.renameSync(oldPath, newPath, cred);

@@ -367,2 +411,3 @@ this.queue('rename', oldPath, newPath, cred);

public statSync(path: string, cred: Cred): Stats {
this.checkSync(path, 'stat');
return this._sync.statSync(path, cred);

@@ -372,2 +417,3 @@ }

public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
this.checkSync(path, 'createFile');
this._sync.createFileSync(path, flag, mode, cred);

@@ -379,2 +425,3 @@ this.queue('createFile', path, flag, mode, cred);

public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> {
this.checkSync(path, 'openFile');
const file = this._sync.openFileSync(path, flag, cred);

@@ -388,2 +435,3 @@ const stats = file.statSync();

public unlinkSync(path: string, cred: Cred): void {
this.checkSync(path, 'unlinkSync');
this._sync.unlinkSync(path, cred);

@@ -394,2 +442,3 @@ this.queue('unlink', path, cred);

public rmdirSync(path: string, cred: Cred): void {
this.checkSync(path, 'rmdir');
this._sync.rmdirSync(path, cred);

@@ -400,2 +449,3 @@ this.queue('rmdir', path, cred);

public mkdirSync(path: string, mode: number, cred: Cred): void {
this.checkSync(path, 'mkdir');
this._sync.mkdirSync(path, mode, cred);

@@ -406,2 +456,3 @@ this.queue('mkdir', path, mode, cred);

public readdirSync(path: string, cred: Cred): string[] {
this.checkSync(path, 'readdir');
return this._sync.readdirSync(path, cred);

@@ -411,2 +462,3 @@ }

public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
this.checkSync(srcpath, 'link');
this._sync.linkSync(srcpath, dstpath, cred);

@@ -417,2 +469,3 @@ this.queue('link', srcpath, dstpath, cred);

public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
this.checkSync(path, 'sync');
this._sync.syncSync(path, data, stats);

@@ -423,2 +476,3 @@ this.queue('sync', path, data, stats);

public existsSync(path: string, cred: Cred): boolean {
this.checkSync(path, 'exists');
return this._sync.existsSync(path, cred);

@@ -431,2 +485,3 @@ }

protected async crossCopy(path: string): Promise<void> {
this.checkSync(path, 'crossCopy');
const stats = await this.stat(path, rootCred);

@@ -486,17 +541,17 @@ if (stats.isDirectory()) {

declare abstract class ReadonlyFS extends FileSystem {
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;
sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
public metadata(): FileSystemMetadata;
public rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
public renameSync(oldPath: string, newPath: string, cred: Cred): void;
public createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File>;
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
public unlink(path: string, cred: Cred): Promise<void>;
public unlinkSync(path: string, cred: Cred): void;
public rmdir(path: string, cred: Cred): Promise<void>;
public rmdirSync(path: string, cred: Cred): void;
public mkdir(path: string, mode: number, cred: Cred): Promise<void>;
public mkdirSync(path: string, mode: number, cred: Cred): void;
public link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
public linkSync(srcpath: string, dstpath: string, cred: Cred): void;
public sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
}

@@ -503,0 +558,0 @@

@@ -74,3 +74,3 @@ import type * as Node from 'fs';

protected _convert(arg: number | bigint | string | boolean): T {
return <T>(this._isBigint ? BigInt(arg) : Number(arg));
return (this._isBigint ? BigInt(arg) : Number(arg)) as T;
}

@@ -190,3 +190,3 @@

const resolveT = (val: number | bigint | undefined, _default: number) =>
<T>(typeof val == this._typename ? val : this._convert(typeof val == this._typename_inverse ? val! : _default));
typeof val == this._typename ? (val as T) : this._convert(typeof val == this._typename_inverse ? val! : _default);
this.atimeMs = resolveT(atimeMs, currentTime);

@@ -218,3 +218,3 @@ this.mtimeMs = resolveT(mtimeMs, currentTime);

if ((this.mode & S_IFMT) == 0) {
this.mode = <T>(this.mode | this._convert(itemType));
this.mode = (this.mode | this._convert(itemType)) as T;
}

@@ -221,0 +221,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