@zenfs/core
Advanced tools
Comparing version 0.12.0 to 0.12.1
@@ -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
1865460
20542
+ Addedeventemitter3@5.0.1(transitive)
+ Addedutilium@0.4.4(transitive)
- Removedutilium@0.2.1(transitive)
Updatedutilium@^0.4.0