Skip to content

Commit

Permalink
*Fixed eslint issues*
Browse files Browse the repository at this point in the history
Fixed unavailable backends throwing the backend object instead of the name
Fixed `resolveMountConfig` not `await`ing `checkOptions`
Changed internal `Offset` enum to `offsets` object (for `Inode`)
`UnlockedOverlayFS.restoreDeletionLog` and `deletePath` are now `async`
Added runtime checks for `IndexFS` directory contents
Fixed some types being `any` in callbacks API
Added a runtime check for `globalThis.ReadableStream` to `FileHandle.readableWebStream`
Fixed sync tests being `async` functions
  • Loading branch information
james-pre committed Aug 2, 2024
1 parent 723bf25 commit 358f333
Show file tree
Hide file tree
Showing 24 changed files with 177 additions and 113 deletions.
10 changes: 10 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ export default tseslint.config(
files: ['src/emulation/{sync,async,promises}.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
},
},
{
name: 'Tests any overrides',
files: ['tests/**/*.ts'],
rules: {
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off'
},
},
{ name: 'Ignore test fixtures', ignores: ['tests/fixtures'] }
Expand Down
8 changes: 8 additions & 0 deletions eslint.shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,13 @@ export default {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/consistent-type-assertions': 'warn',
'@typescript-eslint/consistent-type-imports': 'warn',
'@typescript-eslint/no-unnecessary-type-assertion': 'warn',
'@typescript-eslint/require-await': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',
'@typescript-eslint/no-redundant-type-constituents': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',
},
};
11 changes: 6 additions & 5 deletions src/backends/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,22 @@ import type { IndexData } from './index/index.js';
async function fetchFile(path: string, type: 'buffer'): Promise<Uint8Array>;
async function fetchFile<T extends object>(path: string, type: 'json'): Promise<T>;
async function fetchFile<T extends object>(path: string, type: 'buffer' | 'json'): Promise<T | Uint8Array>;
async function fetchFile<T extends object>(path: string, type: 'buffer' | 'json'): Promise<T | Uint8Array> {
const response = await fetch(path).catch(e => {
async function fetchFile<T extends object>(path: string, type: string): Promise<T | Uint8Array> {
const response = await fetch(path).catch((e: Error) => {
throw new ErrnoError(Errno.EIO, e.message);
});
if (!response.ok) {
throw new ErrnoError(Errno.EIO, 'fetch failed: response returned code ' + response.status);
}
switch (type) {
case 'buffer':
const arrayBuffer = await response.arrayBuffer().catch(e => {
case 'buffer': {
const arrayBuffer = await response.arrayBuffer().catch((e: Error) => {
throw new ErrnoError(Errno.EIO, e.message);
});
return new Uint8Array(arrayBuffer);
}
case 'json':
return response.json().catch(e => {
return response.json().catch((e: Error) => {
throw new ErrnoError(Errno.EIO, e.message);
}) as Promise<T>;
default:
Expand Down
17 changes: 12 additions & 5 deletions src/backends/index/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export abstract class IndexFS extends Readonly(FileSystem) {
}
}

public async stat(path: string): Promise<Stats> {
return this.statSync(path);
public stat(path: string): Promise<Stats> {
return Promise.resolve(this.statSync(path));
}

public statSync(path: string): Stats {
Expand Down Expand Up @@ -91,8 +91,8 @@ export abstract class IndexFS extends Readonly(FileSystem) {
return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));
}

public async readdir(path: string): Promise<string[]> {
return this.readdirSync(path);
public readdir(path: string): Promise<string[]> {
return Promise.resolve(this.readdirSync(path));
}

public readdirSync(path: string): string[] {
Expand All @@ -106,7 +106,14 @@ export abstract class IndexFS extends Readonly(FileSystem) {
throw ErrnoError.With('ENOTDIR', path, 'readdir');
}

return JSON.parse(decode(stats.fileData));
const content: unknown = JSON.parse(decode(stats.fileData));
if(!Array.isArray(content)) {
throw ErrnoError.With('ENODATA', path, 'readdir');
}
if(!content.every(item => typeof item == 'string')) {
throw ErrnoError.With('ENODATA', path, 'readdir');
}
return content as string[];
}

protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
Expand Down
2 changes: 1 addition & 1 deletion src/backends/index/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export class Index extends Map<string, Stats> {
throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
}

const json = JSON.parse(data);
const json = JSON.parse(data) as IndexData;
const index = new Index();
index.fromJSON(json);
return index;
Expand Down
18 changes: 9 additions & 9 deletions src/backends/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ export class UnlockedOverlayFS extends FileSystem {
return this._deleteLog;
}

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

public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
Expand Down Expand Up @@ -248,7 +248,7 @@ export class UnlockedOverlayFS extends FileSystem {

// if it still exists add to the delete log
if (await this.exists(path, cred)) {
this.deletePath(path, cred);
await this.deletePath(path, cred);
}
}

Expand All @@ -265,7 +265,7 @@ export class UnlockedOverlayFS extends FileSystem {

// if it still exists add to the delete log
if (this.existsSync(path, cred)) {
this.deletePath(path, cred);
void this.deletePath(path, cred);
}
}

Expand All @@ -282,7 +282,7 @@ export class UnlockedOverlayFS extends FileSystem {
if ((await this.readdir(path, cred)).length > 0) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
} else {
this.deletePath(path, cred);
await this.deletePath(path, cred);
}
}
}
Expand All @@ -300,7 +300,7 @@ export class UnlockedOverlayFS extends FileSystem {
if (this.readdirSync(path, cred).length > 0) {
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
} else {
this.deletePath(path, cred);
void this.deletePath(path, cred);
}
}
}
Expand Down Expand Up @@ -379,9 +379,9 @@ export class UnlockedOverlayFS extends FileSystem {
});
}

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

private async updateLog(addition: string, cred: Cred) {
Expand All @@ -396,7 +396,7 @@ export class UnlockedOverlayFS extends FileSystem {
await log.write(encode(this._deleteLog));
if (this._deleteLogUpdateNeeded) {
this._deleteLogUpdateNeeded = false;
this.updateLog('', cred);
await this.updateLog('', cred);
}
} catch (e) {
this._deleteLogError = e as ErrnoError;
Expand Down
2 changes: 1 addition & 1 deletion src/backends/port/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export function catchMessages<T extends Backend>(port: Port): (fs: FilesystemOf<
detach(port, handler);
for (const event of events) {
const request: FileOrFSRequest = 'data' in event ? event.data : event;
handleRequest(port, fs, request);
void handleRequest(port, fs, request);
}
};
}
4 changes: 2 additions & 2 deletions src/backends/store/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
*/
private async findINode(tx: Transaction, path: string, visited: Set<string> = new Set()): Promise<Inode> {
const id = await this._findINode(tx, dirname(path), basename(path), visited);
return this.getINode(tx, id!, path);
return this.getINode(tx, id, path);
}

/**
Expand Down Expand Up @@ -698,7 +698,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
await tx.commit();
return inode;
} catch (e) {
tx.abort();
await tx.abort();
throw e;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export async function resolveMountConfig<T extends Backend>(config: MountConfigu
const { backend } = config;

if (!(await backend.isAvailable())) {
throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend.name);
}
checkOptions(backend, config);
await checkOptions(backend, config);
const mount = (await backend.create(config)) as FilesystemOf<T>;
if ('_disableSync' in mount) {
type AsyncFS = InstanceType<ReturnType<typeof Async<new () => FilesystemOf<T>>>>;
Expand Down
44 changes: 26 additions & 18 deletions src/emulation/async.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,13 @@ writeFile satisfies Omit<typeof fs.writeFile, '__promisify__'>;
* @param callback
*/
export function appendFile(filename: fs.PathLike, data: FileContents, cb?: Callback): void;
export function appendFile(filename: fs.PathLike, data: FileContents, options?: { encoding?: string; mode?: number | string; flag?: string }, cb?: Callback): void;
export function appendFile(filename: fs.PathLike, data: FileContents, encoding?: string, cb?: Callback): void;
export function appendFile(filename: fs.PathLike, data: FileContents, cbEncOpts?: any, cb: Callback = nop): void {
export function appendFile(filename: fs.PathLike, data: FileContents, options?: fs.EncodingOption & { mode?: fs.Mode; flag?: fs.OpenMode; }, cb?: Callback): void;
export function appendFile(filename: fs.PathLike, data: FileContents, encoding?: BufferEncoding, cb?: Callback): void;
export function appendFile(filename: fs.PathLike, data: FileContents, cbEncOpts?: fs.EncodingOption & { mode?: fs.Mode; flag?: fs.OpenMode; } | Callback, cb: Callback = nop): void {
const optionsOrEncoding = typeof cbEncOpts != 'function' ? cbEncOpts : undefined;
cb = typeof cbEncOpts === 'function' ? cbEncOpts : cb;
promises
.appendFile(filename, data, typeof cbEncOpts === 'function' ? null : cbEncOpts)
.appendFile(filename, data, optionsOrEncoding)
.then(() => cb())
.catch(cb);
}
Expand Down Expand Up @@ -259,7 +260,7 @@ close satisfies Omit<typeof fs.close, '__promisify__'>;
*/
export function ftruncate(fd: number, cb?: Callback): void;
export function ftruncate(fd: number, len?: number, cb?: Callback): void;
export function ftruncate(fd: number, lenOrCB?: any, cb: Callback = nop): void {
export function ftruncate(fd: number, lenOrCB?: number | Callback, cb: Callback = nop): void {
const length = typeof lenOrCB === 'number' ? lenOrCB : 0;
cb = typeof lenOrCB === 'function' ? lenOrCB : cb;
const file = fd2file(fd);
Expand Down Expand Up @@ -317,8 +318,15 @@ export function write(fd: number, buffer: Uint8Array, offset: number, length: nu
export function write(fd: number, data: FileContents, cb?: Callback<[number, string]>): void;
export function write(fd: number, data: FileContents, position?: number, cb?: Callback<[number, string]>): void;
export function write(fd: number, data: FileContents, position: number | null, encoding: BufferEncoding, cb?: Callback<[number, string]>): void;
export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?: any, cbPos?: any, cb: Callback<[number, Uint8Array]> | Callback<[number, string]> = nop): void {
let buffer: Buffer, offset: number, length: number, position: number | undefined | null, encoding: BufferEncoding;
export function write(
fd: number,
data: FileContents,
cbPosOff?: number | Callback<[number, string]> | null,
cbLenEnc?: number | BufferEncoding | Callback<[number, string]>,
cbPosEnc?: number | BufferEncoding | Callback<[number, Uint8Array]> | Callback<[number, string]>,
cb: Callback<[number, Uint8Array]> | Callback<[number, string]> = nop
): void {
let buffer: Buffer, offset: number | undefined, length: number | undefined, position: number | undefined | null, encoding: BufferEncoding;
const handle = new promises.FileHandle(fd);
if (typeof data === 'string') {
// Signature 1: (fd, string, [position?, [encoding?]], cb?)
Expand All @@ -331,12 +339,12 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
case 'number':
// (fd, string, position, encoding?, cb?)
position = cbPosOff;
encoding = typeof cbLenEnc === 'string' ? (cbLenEnc as BufferEncoding) : 'utf8';
cb = typeof cbPos === 'function' ? cbPos : cb;
encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
cb = typeof cbPosEnc === 'function' ? cbPosEnc : cb;
break;
default:
// ...try to find the callback and get out of here!
cb = typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPos === 'function' ? cbPos : cb;
cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb) as Callback<[number, Uint8Array | string]>;
(cb as Callback<[number, Uint8Array | string]>)(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
return;
}
Expand All @@ -353,11 +361,11 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
} else {
// Signature 2: (fd, buffer, offset, length, position?, cb?)
buffer = Buffer.from(data.buffer);
offset = cbPosOff;
length = cbLenEnc;
position = typeof cbPos === 'number' ? cbPos : null;
const _cb = typeof cbPos === 'function' ? cbPos : (cb as Callback<[number, Uint8Array]>);
handle
offset = cbPosOff as number;
length = cbLenEnc as number;
position = typeof cbPosEnc === 'number' ? cbPosEnc : null;
const _cb = (typeof cbPosEnc === 'function' ? cbPosEnc : cb) as Callback<[number, Uint8Array]>;
void handle
.write(buffer, offset, length, position)
.then(({ bytesWritten }) => _cb(undefined, bytesWritten, buffer))
.catch(_cb);
Expand Down Expand Up @@ -472,7 +480,7 @@ export function readdir(path: fs.PathLike, _options: { withFileTypes?: boolean }
const options = typeof _options != 'function' ? _options : {};
promises
.readdir(path, options as object)
// eslint-disable-next-line @typescript-eslint/no-explicit-any

.then(entries => cb(undefined, entries as any))
.catch(cb);
}
Expand Down Expand Up @@ -647,11 +655,11 @@ realpath satisfies Omit<typeof fs.realpath, '__promisify__' | 'native'>;
*/
export function access(path: fs.PathLike, cb: Callback): void;
export function access(path: fs.PathLike, mode: number, cb: Callback): void;
export function access(path: fs.PathLike, cbMode: any, cb: Callback = nop): void {
export function access(path: fs.PathLike, cbMode: number | Callback, cb: Callback = nop): void {
const mode = typeof cbMode === 'number' ? cbMode : R_OK;
cb = typeof cbMode === 'function' ? cbMode : cb;
promises
.access(path, typeof cbMode === 'function' ? null : cbMode)
.access(path, mode)
.then(() => cb())
.catch(cb);
}
Expand Down
2 changes: 1 addition & 1 deletion src/emulation/dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class Dir implements _Dir {
return this._read();
}

this._read().then(value => cb(undefined, value));
void this._read().then(value => cb(undefined, value));
}

/**
Expand Down
17 changes: 11 additions & 6 deletions src/emulation/promises.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export class FileHandle implements promises.FileHandle {
*/
public readableWebStream(options: promises.ReadableWebStreamOptions = {}): TReadableStream<Uint8Array> {
// Note: using an arrow function to preserve `this`
const start = async ({ close, enqueue, error }: ReadableStreamController<Uint8Array>) => {
const start = async (controller: ReadableStreamController<Uint8Array>) => {
try {
const chunkSize = 64 * 1024,
maxChunks = 1e7;
Expand All @@ -175,22 +175,26 @@ export class FileHandle implements promises.FileHandle {
while (bytesRead > 0) {
const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize, position);
if (!result.bytesRead) {
close();
controller.close();
return;
}
enqueue(result.buffer.slice(0, result.bytesRead));
controller.enqueue(result.buffer.slice(0, result.bytesRead));
position += result.bytesRead;
if (++i >= maxChunks) {
throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
}
bytesRead = result.bytesRead;
}
} catch (e) {
error(e);
controller.error(e);
}
};

return new (globalThis as any).ReadableStream({ start, type: options.type });
const _gt = globalThis;
if(!('ReadableStream' in _gt)) {
throw new ErrnoError(Errno.ENOSYS, 'ReadableStream is missing on globalThis');
}
return new (_gt as { ReadableStream: new (...args: unknown[]) => TReadableStream<Uint8Array>}).ReadableStream({ start, type: options.type });
}

public readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface {
Expand Down Expand Up @@ -326,6 +330,7 @@ export class FileHandle implements promises.FileHandle {
highWaterMark: options?.highWaterMark || 64 * 1024,
encoding: options!.encoding!,

// eslint-disable-next-line @typescript-eslint/no-misused-promises
read: async (size: number) => {
try {
const result = await this.read(new Uint8Array(size), 0, size, this.file.position);
Expand Down Expand Up @@ -573,7 +578,7 @@ export async function writeFile(
_options?: (fs.ObjectEncodingOptions & { mode?: fs.Mode; flag?: fs.OpenMode; flush?: boolean }) | BufferEncoding | null
): Promise<void> {
const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
await using handle = path instanceof FileHandle ? path : await open(path.toString(), options.flag, options.mode);
await using handle = path instanceof FileHandle ? path : await open((path as fs.PathLike).toString(), options.flag, options.mode);

const _data = typeof data == 'string' ? data : data;
if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
Expand Down
Loading

0 comments on commit 358f333

Please sign in to comment.