Read CRAM files in node or the browser. Handles CRAM 2.x and 3.x, .crai
indexes, and every v3 and v3.1 codec, including fqzcomp and tok3. The codecs are
the htscodecs C library that samtools uses, compiled to WebAssembly and inlined
in the bundle, so there is no .wasm file to serve.
npm install @gmod/cramimport { IndexedCramFile, CraiIndex } from '@gmod/cram'
import { IndexedFasta } from '@gmod/indexedfasta'
import { RemoteFile } from 'generic-filehandle2'
const fasta = new IndexedFasta({
fasta: new RemoteFile('https://example.com/reference.fa'),
fai: new RemoteFile('https://example.com/reference.fa.fai'),
})
const indexedFile = new IndexedCramFile({
cramUrl: 'https://example.com/file.cram',
index: new CraiIndex({ url: 'https://example.com/file.cram.crai' }),
fetchReferenceSequence: (seqId, start, end, refName) =>
fasta.getSequence(refName, start, end),
})
const records = await indexedFile.getRecordsForRange('chr1', 10000, 20000)
for (const record of records) {
console.log(record.readName, record.start, record.getCigarString())
}URLs work in node and the browser. For local files, pass cramPath and
CraiIndex({ path }), and IndexedFasta({ path }).
fetchReferenceSequence supplies the reference bases that CRAM stores reads
against. Without it, records still have positions and CIGAR strings, but not
read bases. docs/api.md has the details.
For each query, the library reads one byte range per slice and two small ones
per container. With cramUrl, it sends a separate HTTP request for each range.
@gmod/range-cache-filehandle
keeps the bytes it fetched and answers each range from them, so it fetches
neighboring ranges in one request:
import { RemoteFileWithRangeCache } from '@gmod/range-cache-filehandle'
const indexedFile = new IndexedCramFile({
cramFilehandle: new RemoteFileWithRangeCache(url),
index: new CraiIndex({
filehandle: new RemoteFileWithRangeCache(`${url}.crai`),
}),
})Query a reference by its name in the file's header, such as chr1, 1 or
CHROMOSOME_I. A query for a name the file lacks returns no records, so the
same query can run across files that cover different references:
await indexedFile.getRecordsForRange('chrY', 0, 50000) // [] if the file has no chrY
await indexedFile.hasDataForReferenceSequence('chrY') // false, likewise
await indexedFile.cram.getReferenceInfo() // [{ name, length, md5 }, ...]Coordinates are 0-based half-open, as in @gmod/bam. Add 1 to convert to a
1-based format such as SAM's POS.
record.getReadBases() // 'ACGT…', the read sequence
record.getCigarString() // '50M2I48M'
record.getMismatches() // every difference from the reference
record.getTag('NM') // one auxiliary tag
record.qualityScoreAt(0) // one base quality
record.isReverseComplemented() // the usual SAM flagsdocs/api.md lists every property and method.
The package ships a standalone browser build that puts the same exports on
window.gmodCRAM. The example directory has a working page.
<script src="https://unpkg.com/@gmod/cram/dist/cram-bundle.js"></script>- docs/api.md: every option, property and method, including
cancelling a query with an
AbortSignal - docs/workers.md: the worker pool that decodes slices in parallel in a browser
- MIGRATION.md: breaking changes, newest first
- docs/memory.md: the memory a decoded slice uses
- docs/read-features.md: the raw alignment encoding
- docs/codec-support.md: the codecs this decoder handles
- docs/dataflow.md: a diagram of a query from index to records
- docs/optimizations.md: the measurements behind the read path's design
- docs/wasm.md: the inlined wasm build
- docs/adr/: design decisions, with their measurements
- TODO.md: open work
- CONTRIBUTING.md: development, release and publishing
Written with NHGRI funding as part of JBrowse. If you use this in a publication, please cite the most recent JBrowse paper at jbrowse.org.
MIT