@zenfs/core
Advanced tools
Comparing version 0.11.1 to 0.11.2
@@ -74,3 +74,3 @@ import { NoSyncFile } from '../file.js'; | ||
readonly required: false; | ||
readonly description: "URL to a file index as a JSON file or the file index object itself, generated with the make_http_index script. Defaults to `index.json`."; | ||
readonly description: "URL to a file index as a JSON file or the file index object itself, generated with the make-index script. Defaults to `index.json`."; | ||
}; | ||
@@ -77,0 +77,0 @@ readonly baseUrl: { |
@@ -5,10 +5,6 @@ import { ErrnoError, Errno } from '../error.js'; | ||
import { FileIndex, AsyncIndexFS } from './Index.js'; | ||
/** | ||
* @hidden | ||
*/ | ||
function convertError(e) { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
} | ||
async function fetchFile(path, type) { | ||
const response = await fetch(path).catch(convertError); | ||
const response = await fetch(path).catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}); | ||
if (!response.ok) { | ||
@@ -19,6 +15,10 @@ throw new ErrnoError(Errno.EIO, 'fetch failed: response returned code ' + response.status); | ||
case 'buffer': | ||
const arrayBuffer = await response.arrayBuffer().catch(convertError); | ||
const arrayBuffer = await response.arrayBuffer().catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}); | ||
return new Uint8Array(arrayBuffer); | ||
case 'json': | ||
return response.json().catch(convertError); | ||
return response.json().catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}); | ||
default: | ||
@@ -33,3 +33,5 @@ throw new ErrnoError(Errno.EINVAL, 'Invalid download type: ' + type); | ||
async function fetchSize(path) { | ||
const response = await fetch(path, { method: 'HEAD' }).catch(convertError); | ||
const response = await fetch(path, { method: 'HEAD' }).catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}); | ||
if (!response.ok) { | ||
@@ -159,3 +161,3 @@ throw new ErrnoError(Errno.EIO, 'fetch failed: HEAD response returned code ' + response.status); | ||
required: false, | ||
description: 'URL to a file index as a JSON file or the file index object itself, generated with the make_http_index script. Defaults to `index.json`.', | ||
description: 'URL to a file index as a JSON file or the file index object itself, generated with the make-index script. Defaults to `index.json`.', | ||
}, | ||
@@ -162,0 +164,0 @@ baseUrl: { |
@@ -17,3 +17,3 @@ import type { Cred } from '../cred.js'; | ||
readonly fs: FS; | ||
private _mu; | ||
private mutex; | ||
constructor(fs: FS); | ||
@@ -20,0 +20,0 @@ ready(): Promise<void>; |
@@ -16,3 +16,3 @@ import { ErrnoError } from '../error.js'; | ||
this.fs = fs; | ||
this._mu = new Mutex(); | ||
this.mutex = new Mutex(); | ||
} | ||
@@ -29,8 +29,8 @@ async ready() { | ||
async rename(oldPath, newPath, cred) { | ||
await this._mu.lock(oldPath); | ||
await this.mutex.lock(oldPath); | ||
await this.fs.rename(oldPath, newPath, cred); | ||
this._mu.unlock(oldPath); | ||
this.mutex.unlock(oldPath); | ||
} | ||
renameSync(oldPath, newPath, cred) { | ||
if (this._mu.isLocked(oldPath)) { | ||
if (this.mutex.isLocked(oldPath)) { | ||
throw ErrnoError.With('EBUSY', oldPath, 'rename'); | ||
@@ -41,9 +41,9 @@ } | ||
async stat(path, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const stats = await this.fs.stat(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return stats; | ||
} | ||
statSync(path, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'stat'); | ||
@@ -54,9 +54,9 @@ } | ||
async openFile(path, flag, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const fd = await this.fs.openFile(path, flag, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return fd; | ||
} | ||
openFileSync(path, flag, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'openFile'); | ||
@@ -67,9 +67,9 @@ } | ||
async createFile(path, flag, mode, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const fd = await this.fs.createFile(path, flag, mode, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return fd; | ||
} | ||
createFileSync(path, flag, mode, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'createFile'); | ||
@@ -80,8 +80,8 @@ } | ||
async unlink(path, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.unlink(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
unlinkSync(path, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'unlink'); | ||
@@ -92,8 +92,8 @@ } | ||
async rmdir(path, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.rmdir(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
rmdirSync(path, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'rmdir'); | ||
@@ -104,8 +104,8 @@ } | ||
async mkdir(path, mode, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.mkdir(path, mode, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
mkdirSync(path, mode, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'mkdir'); | ||
@@ -116,9 +116,9 @@ } | ||
async readdir(path, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const files = await this.fs.readdir(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return files; | ||
} | ||
readdirSync(path, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'readdir'); | ||
@@ -129,9 +129,9 @@ } | ||
async exists(path, cred) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const exists = await this.fs.exists(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return exists; | ||
} | ||
existsSync(path, cred) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'exists'); | ||
@@ -142,8 +142,8 @@ } | ||
async link(srcpath, dstpath, cred) { | ||
await this._mu.lock(srcpath); | ||
await this.mutex.lock(srcpath); | ||
await this.fs.link(srcpath, dstpath, cred); | ||
this._mu.unlock(srcpath); | ||
this.mutex.unlock(srcpath); | ||
} | ||
linkSync(srcpath, dstpath, cred) { | ||
if (this._mu.isLocked(srcpath)) { | ||
if (this.mutex.isLocked(srcpath)) { | ||
throw ErrnoError.With('EBUSY', srcpath, 'link'); | ||
@@ -154,8 +154,8 @@ } | ||
async sync(path, data, stats) { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.sync(path, data, stats); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
syncSync(path, data, stats) { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'sync'); | ||
@@ -162,0 +162,0 @@ } |
@@ -30,3 +30,3 @@ import type { Ino } from '../inode.js'; | ||
name?: string; | ||
}) => StoreFS; | ||
}) => StoreFS<InMemoryStore>; | ||
}; |
@@ -82,3 +82,3 @@ /// <reference types="node" resolution-mode="require"/> | ||
*/ | ||
_sync: import("../store/fs.js").StoreFS; | ||
_sync: import("../store/fs.js").StoreFS<import("../memory.js").InMemoryStore>; | ||
/** | ||
@@ -85,0 +85,0 @@ * Constructs a new PortFS instance that connects with ZenFS running on |
@@ -42,3 +42,4 @@ import { Errno, ErrnoError } from '../../error.js'; | ||
async read(buffer, offset, length, position) { | ||
return (await this.rpc('read', buffer, offset, length, position)); | ||
const result = await this.rpc('read', buffer, offset, length, position); | ||
return result; | ||
} | ||
@@ -45,0 +46,0 @@ readSync() { |
@@ -20,6 +20,8 @@ import { ErrnoError, Errno } from '../../error.js'; | ||
port.postMessage({ ...request, _zenfs: true, id, stack }); | ||
setTimeout(() => { | ||
const _ = setTimeout(() => { | ||
const error = new ErrnoError(Errno.EIO, 'RPC Failed'); | ||
error.stack += stack; | ||
reject(error); | ||
if (typeof _ == 'object') | ||
_.unref(); | ||
}, timeout); | ||
@@ -26,0 +28,0 @@ }); |
@@ -8,3 +8,3 @@ import type { Cred } from '../../cred.js'; | ||
/** | ||
* A synchronous key-value file system. Uses a SyncStore to store the data. | ||
* A file system which uses a key-value store. | ||
* | ||
@@ -16,9 +16,9 @@ * We use a unique ID for each node in the file system. The root node has a fixed ID. | ||
*/ | ||
export declare class StoreFS extends FileSystem { | ||
export declare class StoreFS<T extends Store = Store> extends FileSystem { | ||
private $store; | ||
protected get store(): Store; | ||
protected _store?: Store; | ||
protected get store(): T; | ||
protected _store?: T; | ||
private _initialized; | ||
ready(): Promise<void>; | ||
constructor($store: Store | Promise<Store>); | ||
constructor($store: T | Promise<T>); | ||
metadata(): FileSystemMetadata; | ||
@@ -25,0 +25,0 @@ /** |
@@ -11,3 +11,3 @@ import { W_OK, R_OK } from '../../emulation/constants.js'; | ||
/** | ||
* A synchronous key-value file system. Uses a SyncStore to store the data. | ||
* A file system which uses a key-value store. | ||
* | ||
@@ -14,0 +14,0 @@ * We use a unique ID for each node in the file system. The root node has a fixed ID. |
@@ -109,4 +109,3 @@ import { Buffer } from 'buffer'; | ||
const { size } = await this.stat(); | ||
const data = new Uint8Array(size); | ||
await this.file.read(data, 0, size, 0); | ||
const { buffer: data } = await this.file.read(new Uint8Array(size), 0, size, 0); | ||
const buffer = Buffer.from(data); | ||
@@ -113,0 +112,0 @@ return options.encoding ? buffer.toString(options.encoding) : buffer; |
@@ -10,3 +10,3 @@ import { Buffer } from 'buffer'; | ||
import { cred, fd2file, fdMap, fixError, file2fd, mounts, resolveMount } from './shared.js'; | ||
function doOp(...[name, resolveSymlinks, path, ...args]) { | ||
function wrap(...[name, resolveSymlinks, path, ...args]) { | ||
path = normalizePath(path); | ||
@@ -64,3 +64,3 @@ const { fs, path: resolvedPath } = resolveMount(resolveSymlinks && existsSync(path) ? realpathSync(path) : path); | ||
export function statSync(path, options) { | ||
const stats = doOp('statSync', true, path.toString(), cred); | ||
const stats = wrap('statSync', true, path.toString(), cred); | ||
return options?.bigint ? new BigIntStats(stats) : stats; | ||
@@ -70,3 +70,3 @@ } | ||
export function lstatSync(path, options) { | ||
const stats = doOp('statSync', false, path.toString(), cred); | ||
const stats = wrap('statSync', false, path.toString(), cred); | ||
return options?.bigint ? new BigIntStats(stats) : stats; | ||
@@ -95,3 +95,3 @@ } | ||
export function unlinkSync(path) { | ||
return doOp('unlinkSync', false, path.toString(), cred); | ||
return wrap('unlinkSync', false, path.toString(), cred); | ||
} | ||
@@ -104,3 +104,3 @@ unlinkSync; | ||
try { | ||
stats = doOp('statSync', resolveSymlinks, path, cred); | ||
stats = wrap('statSync', resolveSymlinks, path, cred); | ||
} | ||
@@ -112,7 +112,7 @@ catch (e) { | ||
// Ensure parent exists. | ||
const parentStats = doOp('statSync', resolveSymlinks, dirname(path), cred); | ||
const parentStats = wrap('statSync', resolveSymlinks, dirname(path), cred); | ||
if (!parentStats.isDirectory()) { | ||
throw ErrnoError.With('ENOTDIR', dirname(path), '_open'); | ||
} | ||
return doOp('createFileSync', resolveSymlinks, path, flag, mode, cred); | ||
return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred); | ||
case ActionType.THROW: | ||
@@ -133,3 +133,3 @@ throw ErrnoError.With('ENOENT', path, '_open'); | ||
// Delete file. | ||
doOp('unlinkSync', resolveSymlinks, path, cred); | ||
wrap('unlinkSync', resolveSymlinks, path, cred); | ||
/* | ||
@@ -141,5 +141,5 @@ Create file. Use the same mode as the old file. | ||
*/ | ||
return doOp('createFileSync', resolveSymlinks, path, flag, stats.mode, cred); | ||
return wrap('createFileSync', resolveSymlinks, path, flag, stats.mode, cred); | ||
case ActionType.NOP: | ||
return doOp('openFileSync', resolveSymlinks, path, flag, cred); | ||
return wrap('openFileSync', resolveSymlinks, path, flag, cred); | ||
default: | ||
@@ -196,17 +196,2 @@ throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.'); | ||
readFileSync; | ||
/** | ||
* Synchronously writes data to a file, replacing the file | ||
* if it already exists. | ||
* | ||
* The encoding option is ignored if data is a buffer. | ||
*/ | ||
function _writeFileSync(fname, data, flag, mode, resolveSymlinks) { | ||
const file = _openSync(fname, flag, mode, resolveSymlinks); | ||
try { | ||
file.writeSync(data, 0, data.byteLength, 0); | ||
} | ||
finally { | ||
file.closeSync(); | ||
} | ||
} | ||
export function writeFileSync(path, data, _options = {}) { | ||
@@ -225,13 +210,5 @@ const options = normalizeOptions(_options, 'utf8', 'w+', 0o644); | ||
} | ||
_writeFileSync(typeof path == 'number' ? fd2file(path).path : path.toString(), encodedData, options.flag, options.mode, true); | ||
} | ||
writeFileSync; | ||
/** | ||
* Synchronously append data to a file, creating the file if | ||
* it not yet exists. | ||
*/ | ||
function _appendFileSync(fname, data, flag, mode, resolveSymlinks) { | ||
const file = _openSync(fname, flag, mode, resolveSymlinks); | ||
const file = _openSync(typeof path == 'number' ? fd2file(path).path : path.toString(), flag, options.mode, true); | ||
try { | ||
file.writeSync(data, 0, data.byteLength, null); | ||
file.writeSync(encodedData, 0, encodedData.byteLength, 0); | ||
} | ||
@@ -242,2 +219,3 @@ finally { | ||
} | ||
writeFileSync; | ||
/** | ||
@@ -264,3 +242,9 @@ * Asynchronously append data to a file, creating the file if it not yet | ||
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength); | ||
_appendFileSync(typeof filename == 'number' ? fd2file(filename).path : filename.toString(), encodedData, options.flag, options.mode, true); | ||
const file = _openSync(typeof filename == 'number' ? fd2file(filename).path : filename.toString(), flag, options.mode, true); | ||
try { | ||
file.writeSync(encodedData, 0, encodedData.byteLength, null); | ||
} | ||
finally { | ||
file.closeSync(); | ||
} | ||
} | ||
@@ -386,3 +370,3 @@ appendFileSync; | ||
export function rmdirSync(path) { | ||
return doOp('rmdirSync', true, path.toString(), cred); | ||
return wrap('rmdirSync', true, path.toString(), cred); | ||
} | ||
@@ -393,3 +377,3 @@ rmdirSync; | ||
const recursive = typeof options == 'object' && options?.recursive; | ||
doOp('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred); | ||
wrap('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred); | ||
} | ||
@@ -399,3 +383,3 @@ mkdirSync; | ||
path = normalizePath(path); | ||
const entries = doOp('readdirSync', true, path, cred); | ||
const entries = wrap('readdirSync', true, path, cred); | ||
for (const mount of mounts.keys()) { | ||
@@ -431,3 +415,3 @@ if (!mount.startsWith(path)) { | ||
newpath = normalizePath(newpath); | ||
return doOp('linkSync', false, existing.toString(), newpath.toString(), cred); | ||
return wrap('linkSync', false, existing.toString(), newpath.toString(), cred); | ||
} | ||
@@ -434,0 +418,0 @@ linkSync; |
@@ -150,3 +150,3 @@ import { type Cred } from './cred.js'; | ||
*/ | ||
declare abstract class SyncFileSystem extends FileSystem { | ||
declare abstract class SyncFS extends FileSystem { | ||
metadata(): FileSystemMetadata; | ||
@@ -169,7 +169,7 @@ ready(): Promise<void>; | ||
*/ | ||
export declare function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFileSystem) & T; | ||
export declare function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFS) & T; | ||
/** | ||
* @internal | ||
*/ | ||
declare abstract class AsyncFileSystem extends FileSystem { | ||
declare abstract class AsyncFS extends FileSystem { | ||
/** | ||
@@ -205,7 +205,7 @@ * @hidden | ||
*/ | ||
export declare function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFileSystem) & T; | ||
export declare function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFS) & T; | ||
/** | ||
* @internal | ||
*/ | ||
declare abstract class ReadonlyFileSystem extends FileSystem { | ||
declare abstract class ReadonlyFS extends FileSystem { | ||
metadata(): FileSystemMetadata; | ||
@@ -230,3 +230,3 @@ rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
*/ | ||
export declare function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFileSystem) & T; | ||
export declare function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFS) & T; | ||
export {}; |
@@ -61,3 +61,3 @@ import { ErrnoError, Errno } from './error.js'; | ||
export function Sync(FS) { | ||
class _SyncFileSystem extends FS { | ||
class _SyncFS extends FS { | ||
async exists(path, cred) { | ||
@@ -97,3 +97,3 @@ return this.existsSync(path, cred); | ||
} | ||
return _SyncFileSystem; | ||
return _SyncFS; | ||
} | ||
@@ -114,3 +114,3 @@ /** | ||
export function Async(FS) { | ||
class _AsyncFileSystem extends FS { | ||
class _AsyncFS extends FS { | ||
constructor() { | ||
@@ -156,4 +156,8 @@ super(...arguments); | ||
createFileSync(path, flag, mode, cred) { | ||
const file = this._sync.createFileSync(path, flag, mode, cred); | ||
this._sync.createFileSync(path, flag, mode, cred); | ||
this.queue('createFile', path, flag, mode, cred); | ||
return this.openFileSync(path, flag, cred); | ||
} | ||
openFileSync(path, flag, cred) { | ||
const file = this._sync.openFileSync(path, flag, cred); | ||
const stats = file.statSync(); | ||
@@ -164,5 +168,2 @@ const buffer = new Uint8Array(stats.size); | ||
} | ||
openFileSync(path, flag, cred) { | ||
return this._sync.openFileSync(path, flag, cred); | ||
} | ||
unlinkSync(path, cred) { | ||
@@ -243,3 +244,3 @@ this._sync.unlinkSync(path, cred); | ||
} | ||
return _AsyncFileSystem; | ||
return _AsyncFS; | ||
} | ||
@@ -251,3 +252,3 @@ /** | ||
export function Readonly(FS) { | ||
class _ReadonlyFileSystem extends FS { | ||
class _ReadonlyFS extends FS { | ||
metadata() { | ||
@@ -300,3 +301,3 @@ return { ...super.metadata(), readonly: true }; | ||
} | ||
return _ReadonlyFileSystem; | ||
return _ReadonlyFS; | ||
} |
@@ -6,3 +6,3 @@ /** | ||
export declare class Mutex { | ||
private _locks; | ||
protected locks: Map<string, (() => void)[]>; | ||
lock(path: string): Promise<void>; | ||
@@ -9,0 +9,0 @@ unlock(path: string): void; |
@@ -8,11 +8,11 @@ import { ErrnoError, Errno } from './error.js'; | ||
constructor() { | ||
this._locks = new Map(); | ||
this.locks = new Map(); | ||
} | ||
lock(path) { | ||
return new Promise(resolve => { | ||
if (this._locks.has(path)) { | ||
this._locks.get(path).push(resolve); | ||
if (this.locks.has(path)) { | ||
this.locks.get(path).push(resolve); | ||
} | ||
else { | ||
this._locks.set(path, [resolve]); | ||
this.locks.set(path, [resolve]); | ||
} | ||
@@ -22,6 +22,6 @@ }); | ||
unlock(path) { | ||
if (!this._locks.has(path)) { | ||
if (!this.locks.has(path)) { | ||
throw new ErrnoError(Errno.EPERM, 'Can not unlock an already unlocked path', path); | ||
} | ||
const next = this._locks.get(path)?.shift(); | ||
const next = this.locks.get(path)?.shift(); | ||
/* | ||
@@ -36,17 +36,17 @@ don't unlock - we want to queue up next for the | ||
if (next) { | ||
setTimeout(next, 0); | ||
setTimeout(next); | ||
return; | ||
} | ||
this._locks.delete(path); | ||
this.locks.delete(path); | ||
} | ||
tryLock(path) { | ||
if (this._locks.has(path)) { | ||
if (this.locks.has(path)) { | ||
return false; | ||
} | ||
this._locks.set(path, []); | ||
this.locks.set(path, []); | ||
return true; | ||
} | ||
isLocked(path) { | ||
return this._locks.has(path); | ||
return this.locks.has(path); | ||
} | ||
} |
{ | ||
"name": "@zenfs/core", | ||
"version": "0.11.1", | ||
"version": "0.11.2", | ||
"description": "A filesystem in your browser", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -19,15 +19,15 @@ #!/usr/bin/env node | ||
const root = positionals.at(-1) == 'make-index' ? '.' : positionals.at(-1); | ||
const root = positionals.at(-1) || '.'; | ||
if (options.help) { | ||
console.log(`make-index <path> [...options] | ||
path: The path to create a listing for | ||
options: | ||
--help, -h Outputs this help message | ||
--quiet, -q Do not output messages about individual files | ||
--verbose Output verbose messages | ||
path: The path to create a listing for | ||
--output, -o <path> Path to the output file. Defaults to listing. | ||
--ignore, -i <pattern> Ignores files which match the glob <pattern>. Can be passed multiple times. | ||
options: | ||
--help, -h Outputs this help message | ||
--quiet, -q The command will not generate any output, including error messages. | ||
--verbose Output verbose messages | ||
--output, -o <path> Path to the output file. Defaults to listing. | ||
--ignore, -i <pattern> Ignores files which match the glob <pattern>. Can be passed multiple times. | ||
`); | ||
@@ -70,7 +70,9 @@ process.exit(); | ||
function makeListing(path, seen = new Set()) { | ||
function listing(path, seen = new Set()) { | ||
try { | ||
if (options.verbose) console.log(`${color('blue', 'list')} ${path}`); | ||
const stats = statSync(path); | ||
if (stats.isFile()) { | ||
if (options.verbose) console.log(`${color('green', 'file')} ${path}`); | ||
return null; | ||
@@ -87,4 +89,5 @@ } | ||
entries[file] = makeListing(full, seen); | ||
entries[file] = listing(full, seen); | ||
} | ||
if (options.verbose) console.log(`${color('bright_green', ' dir')} ${path}`); | ||
return entries; | ||
@@ -98,5 +101,5 @@ } catch (e) { | ||
const listing = makeListing(pathToPosix(root)); | ||
const rootListing = listing(pathToPosix(root)); | ||
if (!options.quiet) console.log('Generated listing for ' + pathToPosix(resolve(root))); | ||
writeFileSync(options.output, JSON.stringify(listing)); | ||
writeFileSync(options.output, JSON.stringify(rootListing)); |
@@ -9,9 +9,2 @@ import { ErrnoError, Errno } from '../error.js'; | ||
/** | ||
* @hidden | ||
*/ | ||
function convertError(e: Error): never { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
} | ||
/** | ||
* Asynchronously download a file as a buffer or a JSON object. | ||
@@ -27,3 +20,5 @@ * Note that the third function signature with a non-specialized type is | ||
async function fetchFile<T extends object>(path: string, type: 'buffer' | 'json'): Promise<T | Uint8Array> { | ||
const response = await fetch(path).catch(convertError); | ||
const response = await fetch(path).catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}); | ||
if (!response.ok) { | ||
@@ -34,6 +29,10 @@ throw new ErrnoError(Errno.EIO, 'fetch failed: response returned code ' + response.status); | ||
case 'buffer': | ||
const arrayBuffer = await response.arrayBuffer().catch(convertError); | ||
const arrayBuffer = await response.arrayBuffer().catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}); | ||
return new Uint8Array(arrayBuffer); | ||
case 'json': | ||
return response.json().catch(convertError) as Promise<T>; | ||
return response.json().catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}) as Promise<T>; | ||
default: | ||
@@ -49,3 +48,5 @@ throw new ErrnoError(Errno.EINVAL, 'Invalid download type: ' + type); | ||
async function fetchSize(path: string): Promise<number> { | ||
const response = await fetch(path, { method: 'HEAD' }).catch(convertError); | ||
const response = await fetch(path, { method: 'HEAD' }).catch(e => { | ||
throw new ErrnoError(Errno.EIO, e.message); | ||
}); | ||
if (!response.ok) { | ||
@@ -217,3 +218,3 @@ throw new ErrnoError(Errno.EIO, 'fetch failed: HEAD response returned code ' + response.status); | ||
required: false, | ||
description: 'URL to a file index as a JSON file or the file index object itself, generated with the make_http_index script. Defaults to `index.json`.', | ||
description: 'URL to a file index as a JSON file or the file index object itself, generated with the make-index script. Defaults to `index.json`.', | ||
}, | ||
@@ -220,0 +221,0 @@ baseUrl: { |
@@ -19,3 +19,3 @@ import { ErrnoError } from '../error.js'; | ||
export class LockedFS<FS extends FileSystem> implements FileSystem { | ||
private _mu: Mutex = new Mutex(); | ||
private mutex: Mutex = new Mutex(); | ||
@@ -36,9 +36,9 @@ constructor(public readonly fs: FS) {} | ||
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> { | ||
await this._mu.lock(oldPath); | ||
await this.mutex.lock(oldPath); | ||
await this.fs.rename(oldPath, newPath, cred); | ||
this._mu.unlock(oldPath); | ||
this.mutex.unlock(oldPath); | ||
} | ||
public renameSync(oldPath: string, newPath: string, cred: Cred): void { | ||
if (this._mu.isLocked(oldPath)) { | ||
if (this.mutex.isLocked(oldPath)) { | ||
throw ErrnoError.With('EBUSY', oldPath, 'rename'); | ||
@@ -50,5 +50,5 @@ } | ||
public async stat(path: string, cred: Cred): Promise<Stats> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const stats = await this.fs.stat(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return stats; | ||
@@ -58,3 +58,3 @@ } | ||
public statSync(path: string, cred: Cred): Stats { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'stat'); | ||
@@ -66,5 +66,5 @@ } | ||
public async openFile(path: string, flag: string, cred: Cred): Promise<File> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const fd = await this.fs.openFile(path, flag, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return fd; | ||
@@ -74,3 +74,3 @@ } | ||
public openFileSync(path: string, flag: string, cred: Cred): File { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'openFile'); | ||
@@ -82,5 +82,5 @@ } | ||
public async createFile(path: string, flag: string, mode: number, cred: Cred): Promise<File> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const fd = await this.fs.createFile(path, flag, mode, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return fd; | ||
@@ -90,3 +90,3 @@ } | ||
public createFileSync(path: string, flag: string, mode: number, cred: Cred): File { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'createFile'); | ||
@@ -98,9 +98,9 @@ } | ||
public async unlink(path: string, cred: Cred): Promise<void> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.unlink(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
public unlinkSync(path: string, cred: Cred): void { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'unlink'); | ||
@@ -112,9 +112,9 @@ } | ||
public async rmdir(path: string, cred: Cred): Promise<void> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.rmdir(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
public rmdirSync(path: string, cred: Cred): void { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'rmdir'); | ||
@@ -126,9 +126,9 @@ } | ||
public async mkdir(path: string, mode: number, cred: Cred): Promise<void> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.mkdir(path, mode, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
public mkdirSync(path: string, mode: number, cred: Cred): void { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'mkdir'); | ||
@@ -140,5 +140,5 @@ } | ||
public async readdir(path: string, cred: Cred): Promise<string[]> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const files = await this.fs.readdir(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return files; | ||
@@ -148,3 +148,3 @@ } | ||
public readdirSync(path: string, cred: Cred): string[] { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'readdir'); | ||
@@ -156,5 +156,5 @@ } | ||
public async exists(path: string, cred: Cred): Promise<boolean> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
const exists = await this.fs.exists(path, cred); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
return exists; | ||
@@ -164,3 +164,3 @@ } | ||
public existsSync(path: string, cred: Cred): boolean { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'exists'); | ||
@@ -172,9 +172,9 @@ } | ||
public async link(srcpath: string, dstpath: string, cred: Cred): Promise<void> { | ||
await this._mu.lock(srcpath); | ||
await this.mutex.lock(srcpath); | ||
await this.fs.link(srcpath, dstpath, cred); | ||
this._mu.unlock(srcpath); | ||
this.mutex.unlock(srcpath); | ||
} | ||
public linkSync(srcpath: string, dstpath: string, cred: Cred): void { | ||
if (this._mu.isLocked(srcpath)) { | ||
if (this.mutex.isLocked(srcpath)) { | ||
throw ErrnoError.With('EBUSY', srcpath, 'link'); | ||
@@ -186,9 +186,9 @@ } | ||
public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> { | ||
await this._mu.lock(path); | ||
await this.mutex.lock(path); | ||
await this.fs.sync(path, data, stats); | ||
this._mu.unlock(path); | ||
this.mutex.unlock(path); | ||
} | ||
public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void { | ||
if (this._mu.isLocked(path)) { | ||
if (this.mutex.isLocked(path)) { | ||
throw ErrnoError.With('EBUSY', path, 'sync'); | ||
@@ -195,0 +195,0 @@ } |
@@ -44,2 +44,2 @@ import type { Ino } from '../inode.js'; | ||
}, | ||
} as const satisfies Backend<StoreFS, { name?: string }>; | ||
} as const satisfies Backend<StoreFS<InMemoryStore>, { name?: string }>; |
@@ -66,3 +66,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
public async read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>> { | ||
return (await this.rpc('read', buffer, offset, length, position)) as FileReadResult<TBuffer>; | ||
const result = await this.rpc('read', buffer, offset, length, position); | ||
return result as FileReadResult<TBuffer>; | ||
} | ||
@@ -69,0 +70,0 @@ |
@@ -88,6 +88,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
port.postMessage({ ...request, _zenfs: true, id, stack }); | ||
setTimeout(() => { | ||
const _ = setTimeout(() => { | ||
const error = new ErrnoError(Errno.EIO, 'RPC Failed'); | ||
error.stack += stack; | ||
reject(error); | ||
if (typeof _ == 'object') _.unref(); | ||
}, timeout); | ||
@@ -94,0 +95,0 @@ }); |
@@ -15,3 +15,3 @@ import type { Cred } from '../../cred.js'; | ||
/** | ||
* A synchronous key-value file system. Uses a SyncStore to store the data. | ||
* A file system which uses a key-value store. | ||
* | ||
@@ -23,4 +23,4 @@ * We use a unique ID for each node in the file system. The root node has a fixed ID. | ||
*/ | ||
export class StoreFS extends FileSystem { | ||
protected get store(): Store { | ||
export class StoreFS<T extends Store = Store> extends FileSystem { | ||
protected get store(): T { | ||
if (!this._store) { | ||
@@ -32,3 +32,3 @@ throw new ErrnoError(Errno.ENODATA, 'No store attached'); | ||
protected _store?: Store; | ||
protected _store?: T; | ||
@@ -46,3 +46,3 @@ private _initialized: boolean = false; | ||
constructor(private $store: Store | Promise<Store>) { | ||
constructor(private $store: T | Promise<T>) { | ||
super(); | ||
@@ -49,0 +49,0 @@ |
@@ -147,4 +147,3 @@ import { Buffer } from 'buffer'; | ||
const { size } = await this.stat(); | ||
const data = new Uint8Array(size); | ||
await this.file.read(data, 0, size, 0); | ||
const { buffer: data } = await this.file.read(new Uint8Array(size), 0, size, 0); | ||
const buffer = Buffer.from(data); | ||
@@ -151,0 +150,0 @@ return options.encoding ? buffer.toString(options.encoding) : buffer; |
@@ -19,3 +19,3 @@ import { Buffer } from 'buffer'; | ||
function doOp<M extends FileSystemMethod, RT extends ReturnType<M>>(...[name, resolveSymlinks, path, ...args]: Parameters<M>): RT { | ||
function wrap<M extends FileSystemMethod, RT extends ReturnType<M>>(...[name, resolveSymlinks, path, ...args]: Parameters<M>): RT { | ||
path = normalizePath(path!); | ||
@@ -82,3 +82,3 @@ const { fs, path: resolvedPath } = resolveMount(resolveSymlinks && existsSync(path) ? realpathSync(path) : path); | ||
export function statSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats { | ||
const stats: Stats = doOp('statSync', true, path.toString(), cred); | ||
const stats: Stats = wrap('statSync', true, path.toString(), cred); | ||
return options?.bigint ? new BigIntStats(stats) : stats; | ||
@@ -97,3 +97,3 @@ } | ||
export function lstatSync(path: fs.PathLike, options?: fs.StatOptions): Stats | BigIntStats { | ||
const stats: Stats = doOp('statSync', false, path.toString(), cred); | ||
const stats: Stats = wrap('statSync', false, path.toString(), cred); | ||
return options?.bigint ? new BigIntStats(stats) : stats; | ||
@@ -123,3 +123,3 @@ } | ||
export function unlinkSync(path: fs.PathLike): void { | ||
return doOp('unlinkSync', false, path.toString(), cred); | ||
return wrap('unlinkSync', false, path.toString(), cred); | ||
} | ||
@@ -135,3 +135,3 @@ unlinkSync satisfies typeof fs.unlinkSync; | ||
try { | ||
stats = doOp('statSync', resolveSymlinks, path, cred); | ||
stats = wrap('statSync', resolveSymlinks, path, cred); | ||
} catch (e) { | ||
@@ -142,7 +142,7 @@ // File does not exist. | ||
// Ensure parent exists. | ||
const parentStats: Stats = doOp('statSync', resolveSymlinks, dirname(path), cred); | ||
const parentStats: Stats = wrap('statSync', resolveSymlinks, dirname(path), cred); | ||
if (!parentStats.isDirectory()) { | ||
throw ErrnoError.With('ENOTDIR', dirname(path), '_open'); | ||
} | ||
return doOp('createFileSync', resolveSymlinks, path, flag, mode, cred); | ||
return wrap('createFileSync', resolveSymlinks, path, flag, mode, cred); | ||
case ActionType.THROW: | ||
@@ -164,3 +164,3 @@ throw ErrnoError.With('ENOENT', path, '_open'); | ||
// Delete file. | ||
doOp('unlinkSync', resolveSymlinks, path, cred); | ||
wrap('unlinkSync', resolveSymlinks, path, cred); | ||
/* | ||
@@ -172,5 +172,5 @@ Create file. Use the same mode as the old file. | ||
*/ | ||
return doOp('createFileSync', resolveSymlinks, path, flag, stats.mode, cred); | ||
return wrap('createFileSync', resolveSymlinks, path, flag, stats.mode, cred); | ||
case ActionType.NOP: | ||
return doOp('openFileSync', resolveSymlinks, path, flag, cred); | ||
return wrap('openFileSync', resolveSymlinks, path, flag, cred); | ||
default: | ||
@@ -242,17 +242,2 @@ throw new ErrnoError(Errno.EINVAL, 'Invalid FileFlag object.'); | ||
/** | ||
* Synchronously writes data to a file, replacing the file | ||
* if it already exists. | ||
* | ||
* The encoding option is ignored if data is a buffer. | ||
*/ | ||
function _writeFileSync(fname: string, data: Uint8Array, flag: string, mode: number, resolveSymlinks: boolean): void { | ||
const file = _openSync(fname, flag, mode, resolveSymlinks); | ||
try { | ||
file.writeSync(data, 0, data.byteLength, 0); | ||
} finally { | ||
file.closeSync(); | ||
} | ||
} | ||
/** | ||
* Synchronously writes data to a file, replacing the file if it already | ||
@@ -284,14 +269,5 @@ * exists. | ||
} | ||
_writeFileSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), encodedData, options.flag, options.mode, true); | ||
} | ||
writeFileSync satisfies typeof fs.writeFileSync; | ||
/** | ||
* Synchronously append data to a file, creating the file if | ||
* it not yet exists. | ||
*/ | ||
function _appendFileSync(fname: string, data: Uint8Array, flag: string, mode: number, resolveSymlinks: boolean): void { | ||
const file = _openSync(fname, flag, mode, resolveSymlinks); | ||
const file = _openSync(typeof path == 'number' ? fd2file(path).path! : path.toString(), flag, options.mode, true); | ||
try { | ||
file.writeSync(data, 0, data.byteLength, null); | ||
file.writeSync(encodedData, 0, encodedData.byteLength, 0); | ||
} finally { | ||
@@ -301,2 +277,3 @@ file.closeSync(); | ||
} | ||
writeFileSync satisfies typeof fs.writeFileSync; | ||
@@ -324,3 +301,8 @@ /** | ||
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength); | ||
_appendFileSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), encodedData, options.flag, options.mode, true); | ||
const file = _openSync(typeof filename == 'number' ? fd2file(filename).path! : filename.toString(), flag, options.mode, true); | ||
try { | ||
file.writeSync(encodedData, 0, encodedData.byteLength, null); | ||
} finally { | ||
file.closeSync(); | ||
} | ||
} | ||
@@ -496,3 +478,3 @@ appendFileSync satisfies typeof fs.appendFileSync; | ||
export function rmdirSync(path: fs.PathLike): void { | ||
return doOp('rmdirSync', true, path.toString(), cred); | ||
return wrap('rmdirSync', true, path.toString(), cred); | ||
} | ||
@@ -513,3 +495,3 @@ rmdirSync satisfies typeof fs.rmdirSync; | ||
const recursive = typeof options == 'object' && options?.recursive; | ||
doOp('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred); | ||
wrap('mkdirSync', true, path.toString(), normalizeMode(mode, 0o777), cred); | ||
} | ||
@@ -531,3 +513,3 @@ mkdirSync satisfies typeof fs.mkdirSync; | ||
path = normalizePath(path); | ||
const entries: string[] = doOp('readdirSync', true, path, cred); | ||
const entries: string[] = wrap('readdirSync', true, path, cred); | ||
for (const mount of mounts.keys()) { | ||
@@ -567,3 +549,3 @@ if (!mount.startsWith(path)) { | ||
newpath = normalizePath(newpath); | ||
return doOp('linkSync', false, existing.toString(), newpath.toString(), cred); | ||
return wrap('linkSync', false, existing.toString(), newpath.toString(), cred); | ||
} | ||
@@ -570,0 +552,0 @@ linkSync satisfies typeof fs.linkSync; |
@@ -200,3 +200,3 @@ import type { ExtractProperties } from 'utilium'; | ||
*/ | ||
declare abstract class SyncFileSystem extends FileSystem { | ||
declare abstract class SyncFS extends FileSystem { | ||
metadata(): FileSystemMetadata; | ||
@@ -221,4 +221,4 @@ ready(): Promise<void>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFileSystem) & T { | ||
abstract class _SyncFileSystem extends FS implements SyncFileSystem { | ||
export function Sync<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => SyncFS) & T { | ||
abstract class _SyncFS extends FS implements SyncFS { | ||
public async exists(path: string, cred: Cred): Promise<boolean> { | ||
@@ -268,3 +268,3 @@ return this.existsSync(path, cred); | ||
} | ||
return _SyncFileSystem; | ||
return _SyncFS; | ||
} | ||
@@ -275,3 +275,3 @@ | ||
*/ | ||
declare abstract class AsyncFileSystem extends FileSystem { | ||
declare abstract class AsyncFS extends FileSystem { | ||
/** | ||
@@ -319,4 +319,4 @@ * @hidden | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFileSystem) & T { | ||
abstract class _AsyncFileSystem extends FS implements AsyncFileSystem { | ||
export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => AsyncFS) & T { | ||
abstract class _AsyncFS extends FS implements AsyncFS { | ||
/** | ||
@@ -367,4 +367,9 @@ * Queue of pending asynchronous operations. | ||
public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> { | ||
const file = this._sync.createFileSync(path, flag, mode, cred); | ||
this._sync.createFileSync(path, flag, mode, cred); | ||
this.queue('createFile', path, flag, mode, cred); | ||
return this.openFileSync(path, flag, cred); | ||
} | ||
public openFileSync(path: string, flag: string, cred: Cred): PreloadFile<this> { | ||
const file = this._sync.openFileSync(path, flag, cred); | ||
const stats = file.statSync(); | ||
@@ -376,6 +381,2 @@ const buffer = new Uint8Array(stats.size); | ||
public openFileSync(path: string, flag: string, cred: Cred): File { | ||
return this._sync.openFileSync(path, flag, cred); | ||
} | ||
public unlinkSync(path: string, cred: Cred): void { | ||
@@ -465,3 +466,3 @@ this._sync.unlinkSync(path, cred); | ||
return _AsyncFileSystem; | ||
return _AsyncFS; | ||
} | ||
@@ -472,3 +473,3 @@ | ||
*/ | ||
declare abstract class ReadonlyFileSystem extends FileSystem { | ||
declare abstract class ReadonlyFS extends FileSystem { | ||
metadata(): FileSystemMetadata; | ||
@@ -495,4 +496,4 @@ rename(oldPath: string, newPath: string, cred: Cred): Promise<void>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFileSystem) & T { | ||
abstract class _ReadonlyFileSystem extends FS implements ReadonlyFileSystem { | ||
export function Readonly<T extends abstract new (...args: any[]) => FileSystem>(FS: T): (abstract new (...args: any[]) => ReadonlyFS) & T { | ||
abstract class _ReadonlyFS extends FS implements ReadonlyFS { | ||
public metadata(): FileSystemMetadata { | ||
@@ -559,3 +560,3 @@ return { ...super.metadata(), readonly: true }; | ||
} | ||
return _ReadonlyFileSystem; | ||
return _ReadonlyFS; | ||
} |
@@ -8,10 +8,10 @@ import { ErrnoError, Errno } from './error.js'; | ||
export class Mutex { | ||
private _locks: Map<string, (() => void)[]> = new Map(); | ||
protected locks: Map<string, (() => void)[]> = new Map(); | ||
public lock(path: string): Promise<void> { | ||
return new Promise(resolve => { | ||
if (this._locks.has(path)) { | ||
this._locks.get(path)!.push(resolve); | ||
if (this.locks.has(path)) { | ||
this.locks.get(path)!.push(resolve); | ||
} else { | ||
this._locks.set(path, [resolve]); | ||
this.locks.set(path, [resolve]); | ||
} | ||
@@ -22,7 +22,7 @@ }); | ||
public unlock(path: string): void { | ||
if (!this._locks.has(path)) { | ||
if (!this.locks.has(path)) { | ||
throw new ErrnoError(Errno.EPERM, 'Can not unlock an already unlocked path', path); | ||
} | ||
const next = this._locks.get(path)?.shift(); | ||
const next = this.locks.get(path)?.shift(); | ||
/* | ||
@@ -37,15 +37,15 @@ don't unlock - we want to queue up next for the | ||
if (next) { | ||
setTimeout(next, 0); | ||
setTimeout(next); | ||
return; | ||
} | ||
this._locks.delete(path); | ||
this.locks.delete(path); | ||
} | ||
public tryLock(path: string): boolean { | ||
if (this._locks.has(path)) { | ||
if (this.locks.has(path)) { | ||
return false; | ||
} | ||
this._locks.set(path, []); | ||
this.locks.set(path, []); | ||
return true; | ||
@@ -55,4 +55,4 @@ } | ||
public isLocked(path: string): boolean { | ||
return this._locks.has(path); | ||
return this.locks.has(path); | ||
} | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1880927
21009