Sidecar index
The index is a sparse table of byte offsets. It is not a copy of the file. Building it is a full sequential read. Skip it if you will seek once.
A 50 million line file with stride: 64 needs about 6 MB on disk. A later getLine reads 8 bytes from the sidecar, preads that offset, then walks at most stride lines. It does not make find() / --grep sublinear.
0..3 magic "LSK1"
4 version 2
5 flags bit0 = ended with newline
6..9 stride uint32 LE
10..41 lineCount, fileSize, mtimeMs, entryCount uint64 LE
42..49 lastLineOffset uint64 LE
50..57 prefix/suffix checksums of the indexed bytes
64.. offsets uint64 LE (line 1, 1+stride, 1+2*stride, ...)Default path: {file}.lseek. Override with indexPath, or set cacheIndex: true to write under ~/.cache/line-seeker.
Incremental refresh
refreshIndex() compares prefix and suffix fingerprints.
- Append — only the new bytes are scanned; existing offsets stay valid.
- Truncate or rewrite — the sidecar is discarded and rebuilt.
- LineSeeker edit (
replaceLine/insert/delete) — the sidecar is rebuilt if one existed. - Read-only volume —
buildIndex()falls back to the cache dir onEACCES. - Failed build — a failed index build removes its
.tmppartial; no half-written sidecar survives.
Low-level helpers
These are exported if you want the index without a full seeker:
js
import {
LineIndex,
buildIndexFile,
refreshIndexFile,
indexPathFor,
cacheIndexPath,
} from "line-seeker";LineIndex.locate(lineNumber) returns { offset, indexedLine } or null. Only the 8-byte entry needed for that lookup is read into memory.