import type { FilehandleOptions, GenericFilehandle, ReadFileOptions, ReadFileTextOptions, Stats } from './filehandle.ts'; export interface LocalFileOptions { /** * Keep the file descriptor open between reads instead of opening and closing * one per read. Default true. * * Worth ~1.9x on small reads (47 -> 25 us on 64KB reads of a warm file), which * matters because an indexed reader issues a lot of them: a single BAM query * is 6-20 reads plus the index. * * The hazard a held descriptor introduces is that it can go stale under you — * EBADF on a Samba mount, ESTALE on NFS — where open-per-read cannot. That is * handled rather than avoided: a failed read drops the descriptor, reopens, * and retries once, so a stale one costs a reopen instead of an error. A file * that is genuinely gone fails on the second attempt with its real error. * * Set false to go back to open-per-read. */ cacheFd?: boolean; /** * Close a held descriptor once nothing has read from it for this many * milliseconds. Default 30s; `0` holds it until {@link LocalFile.close}. * * Without this, one descriptor is retained per instance for the life of the * object, and consumers do not reliably close filehandles — JBrowse opens one * per track file and never does. Two reasons that matters. Descriptors are a * per-process limit, so "one per file object, forever" is a slow leak in a * long session; and node deprecated closing a `FileHandle` by garbage * collection (DEP0137) and intends to make it an error, so a held descriptor * that is only ever collected is a future crash rather than a tidy-up. * * Releasing on idle keeps the win — reads during a query are milliseconds * apart and never see it — while making retention self-limiting for a caller * that forgets. The timer is `unref`'d, so it never keeps a process alive. */ fdIdleTimeoutMs?: number; } export default class LocalFile implements GenericFilehandle { private filename; /** the path this handle reads — see {@link GenericFilehandle.source} */ get source(): string; private cacheFd; private fdIdleTimeoutMs; private fh; private opening; private idleTimer; constructor(source: string, opts?: LocalFileOptions); /** * Restart the idle countdown. Called after every read, so the clock measures * time since the last read rather than time since the descriptor opened. */ private touch; /** * The open descriptor, opening one if needed. Concurrent callers share a * single `open()` — an indexed reader routinely fires six reads at once and * they should not race to open six descriptors. */ private handle; private dropHandle; read(length: number, position?: number, opts?: FilehandleOptions): Promise>; private readFrom; private readWithOwnHandle; readFile(options?: ReadFileOptions): Promise>; readFile(options: ReadFileTextOptions): Promise; stat(): Promise; /** * Release the held descriptor, if there is one. Reading again reopens, so * this is safe to call at any point — it is a hint that the caller is done, * not a teardown that invalidates the object. */ close(): Promise; }