Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@gmod/bam

Package Overview
Dependencies
Maintainers
6
Versions
77
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@gmod/bam - npm Package Compare versions

Comparing version 4.0.1 to 5.0.0

10

CHANGELOG.md

@@ -1,17 +0,13 @@

## [4.0.1](https://github.com/GMOD/bam-js/compare/v4.0.0...v4.0.1) (2024-11-12)
# [5.0.0](https://github.com/GMOD/bam-js/compare/v4.0.1...v5.0.0) (2024-12-12)
## [4.0.1](https://github.com/GMOD/bam-js/compare/v4.0.0...v4.0.1) (2024-11-12)
# [4.0.0](https://github.com/GMOD/bam-js/compare/v3.0.3...v4.0.0) (2024-11-12)
## [3.0.3](https://github.com/GMOD/bam-js/compare/v3.0.0...v3.0.3) (2024-11-11)
## [3.0.2](https://github.com/GMOD/bam-js/compare/v3.0.0...v3.0.2) (2024-11-11)
- republish v3.0.1 since it got tagged on a deleted branch

@@ -18,0 +14,0 @@

@@ -8,3 +8,3 @@ import VirtualOffset from './virtualOffset';

lineCount(refId: number, opts?: BaseOpts): Promise<number>;
_parse(opts?: BaseOpts): Promise<{
_parse(_opts?: BaseOpts): Promise<{
bai: boolean;

@@ -11,0 +11,0 @@ firstDataLine: VirtualOffset | undefined;

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -38,177 +29,168 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

class BAI extends indexFile_1.default {
lineCount(refId, opts) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const indexData = yield this.parse(opts);
return ((_b = (_a = indexData.indices[refId]) === null || _a === void 0 ? void 0 : _a.stats) === null || _b === void 0 ? void 0 : _b.lineCount) || 0;
});
setupP;
async lineCount(refId, opts) {
const indexData = await this.parse(opts);
return indexData.indices[refId]?.stats?.lineCount || 0;
}
// fetch and parse the index
_parse(opts) {
return __awaiter(this, void 0, void 0, function* () {
const bytes = (yield this.filehandle.readFile(opts));
// check BAI magic numbers
if (bytes.readUInt32LE(0) !== BAI_MAGIC) {
throw new Error('Not a BAI file');
}
const refCount = bytes.readInt32LE(4);
const depth = 5;
const binLimit = ((1 << ((depth + 1) * 3)) - 1) / 7;
// read the indexes for each reference sequence
let curr = 8;
let firstDataLine;
const indices = new Array(refCount);
for (let i = 0; i < refCount; i++) {
// the binning index
const binCount = bytes.readInt32LE(curr);
let stats;
async _parse(_opts) {
const bytes = await this.filehandle.readFile();
const dataView = new DataView(bytes.buffer);
// check BAI magic numbers
if (dataView.getUint32(0, true) !== BAI_MAGIC) {
throw new Error('Not a BAI file');
}
const refCount = dataView.getInt32(4, true);
const depth = 5;
const binLimit = ((1 << ((depth + 1) * 3)) - 1) / 7;
// read the indexes for each reference sequence
let curr = 8;
let firstDataLine;
const indices = new Array(refCount);
for (let i = 0; i < refCount; i++) {
// the binning index
const binCount = dataView.getInt32(curr, true);
let stats;
curr += 4;
const binIndex = {};
for (let j = 0; j < binCount; j += 1) {
const bin = dataView.getUint32(curr, true);
curr += 4;
const binIndex = {};
for (let j = 0; j < binCount; j += 1) {
const bin = bytes.readUInt32LE(curr);
if (bin === binLimit + 1) {
curr += 4;
if (bin === binLimit + 1) {
curr += 4;
stats = (0, util_1.parsePseudoBin)(bytes, curr + 16);
curr += 32;
stats = (0, util_1.parsePseudoBin)(bytes, curr + 16);
curr += 32;
}
else if (bin > binLimit + 1) {
throw new Error('bai index contains too many bins, please use CSI');
}
else {
const chunkCount = dataView.getInt32(curr, true);
curr += 4;
const chunks = new Array(chunkCount);
for (let k = 0; k < chunkCount; k++) {
const u = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
const v = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
firstDataLine = (0, util_1.findFirstData)(firstDataLine, u);
chunks[k] = new chunk_1.default(u, v, bin);
}
else if (bin > binLimit + 1) {
throw new Error('bai index contains too many bins, please use CSI');
}
else {
const chunkCount = bytes.readInt32LE(curr);
curr += 4;
const chunks = new Array(chunkCount);
for (let k = 0; k < chunkCount; k++) {
const u = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
const v = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
firstDataLine = (0, util_1.findFirstData)(firstDataLine, u);
chunks[k] = new chunk_1.default(u, v, bin);
}
binIndex[bin] = chunks;
}
binIndex[bin] = chunks;
}
const linearCount = bytes.readInt32LE(curr);
curr += 4;
// as we're going through the linear index, figure out the smallest
// virtual offset in the indexes, which tells us where the BAM header
// ends
const linearIndex = new Array(linearCount);
for (let j = 0; j < linearCount; j++) {
const offset = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
firstDataLine = (0, util_1.findFirstData)(firstDataLine, offset);
linearIndex[j] = offset;
}
indices[i] = { binIndex, linearIndex, stats };
}
return {
bai: true,
firstDataLine,
maxBlockSize: 1 << 16,
indices,
refCount,
const linearCount = dataView.getInt32(curr, true);
curr += 4;
// as we're going through the linear index, figure out the smallest
// virtual offset in the indexes, which tells us where the BAM header
// ends
const linearIndex = new Array(linearCount);
for (let j = 0; j < linearCount; j++) {
const offset = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
firstDataLine = (0, util_1.findFirstData)(firstDataLine, offset);
linearIndex[j] = offset;
}
indices[i] = { binIndex, linearIndex, stats };
}
return {
bai: true,
firstDataLine,
maxBlockSize: 1 << 16,
indices,
refCount,
};
}
async indexCov(seqId, start, end, opts = {}) {
const v = 16384;
const range = start !== undefined;
const indexData = await this.parse(opts);
const seqIdx = indexData.indices[seqId];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!seqIdx) {
return [];
}
const { linearIndex = [], stats } = seqIdx;
if (linearIndex.length === 0) {
return [];
}
const e = end === undefined ? (linearIndex.length - 1) * v : roundUp(end, v);
const s = start === undefined ? 0 : roundDown(start, v);
const depths = range
? new Array((e - s) / v)
: new Array(linearIndex.length - 1);
const totalSize = linearIndex[linearIndex.length - 1].blockPosition;
if (e > (linearIndex.length - 1) * v) {
throw new Error('query outside of range of linear index');
}
let currentPos = linearIndex[s / v].blockPosition;
for (let i = s / v, j = 0; i < e / v; i++, j++) {
depths[j] = {
score: linearIndex[i + 1].blockPosition - currentPos,
start: i * v,
end: i * v + v,
};
});
currentPos = linearIndex[i + 1].blockPosition;
}
return depths.map(d => ({
...d,
score: (d.score * (stats?.lineCount || 0)) / totalSize,
}));
}
indexCov(seqId_1, start_1, end_1) {
return __awaiter(this, arguments, void 0, function* (seqId, start, end, opts = {}) {
const v = 16384;
const range = start !== undefined;
const indexData = yield this.parse(opts);
const seqIdx = indexData.indices[seqId];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!seqIdx) {
return [];
}
const { linearIndex = [], stats } = seqIdx;
if (linearIndex.length === 0) {
return [];
}
const e = end === undefined ? (linearIndex.length - 1) * v : roundUp(end, v);
const s = start === undefined ? 0 : roundDown(start, v);
const depths = range
? new Array((e - s) / v)
: new Array(linearIndex.length - 1);
const totalSize = linearIndex[linearIndex.length - 1].blockPosition;
if (e > (linearIndex.length - 1) * v) {
throw new Error('query outside of range of linear index');
}
let currentPos = linearIndex[s / v].blockPosition;
for (let i = s / v, j = 0; i < e / v; i++, j++) {
depths[j] = {
score: linearIndex[i + 1].blockPosition - currentPos,
start: i * v,
end: i * v + v,
};
currentPos = linearIndex[i + 1].blockPosition;
}
return depths.map(d => (Object.assign(Object.assign({}, d), { score: (d.score * ((stats === null || stats === void 0 ? void 0 : stats.lineCount) || 0)) / totalSize })));
});
}
blocksForRange(refId_1, min_1, max_1) {
return __awaiter(this, arguments, void 0, function* (refId, min, max, opts = {}) {
if (min < 0) {
min = 0;
}
const indexData = yield this.parse(opts);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!indexData) {
return [];
}
const ba = indexData.indices[refId];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!ba) {
return [];
}
// List of bin #s that overlap min, max
const overlappingBins = reg2bins(min, max);
const chunks = [];
// Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
for (const [start, end] of overlappingBins) {
for (let bin = start; bin <= end; bin++) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ba.binIndex[bin]) {
const binChunks = ba.binIndex[bin];
for (const binChunk of binChunks) {
chunks.push(new chunk_1.default(binChunk.minv, binChunk.maxv, bin));
}
async blocksForRange(refId, min, max, opts = {}) {
if (min < 0) {
min = 0;
}
const indexData = await this.parse(opts);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!indexData) {
return [];
}
const ba = indexData.indices[refId];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!ba) {
return [];
}
// List of bin #s that overlap min, max
const overlappingBins = reg2bins(min, max);
const chunks = [];
// Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
for (const [start, end] of overlappingBins) {
for (let bin = start; bin <= end; bin++) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ba.binIndex[bin]) {
const binChunks = ba.binIndex[bin];
for (const binChunk of binChunks) {
chunks.push(new chunk_1.default(binChunk.minv, binChunk.maxv, bin));
}
}
}
// Use the linear index to find minimum file position of chunks that could
// contain alignments in the region
const nintv = ba.linearIndex.length;
let lowest;
const minLin = Math.min(min >> 14, nintv - 1);
const maxLin = Math.min(max >> 14, nintv - 1);
for (let i = minLin; i <= maxLin; ++i) {
const vp = ba.linearIndex[i];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (vp && (!lowest || vp.compareTo(lowest) < 0)) {
lowest = vp;
}
}
// Use the linear index to find minimum file position of chunks that could
// contain alignments in the region
const nintv = ba.linearIndex.length;
let lowest;
const minLin = Math.min(min >> 14, nintv - 1);
const maxLin = Math.min(max >> 14, nintv - 1);
for (let i = minLin; i <= maxLin; ++i) {
const vp = ba.linearIndex[i];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (vp && (!lowest || vp.compareTo(lowest) < 0)) {
lowest = vp;
}
return (0, util_1.optimizeChunks)(chunks, lowest);
});
}
return (0, util_1.optimizeChunks)(chunks, lowest);
}
parse() {
return __awaiter(this, arguments, void 0, function* (opts = {}) {
if (!this.setupP) {
this.setupP = this._parse(opts).catch((e) => {
this.setupP = undefined;
throw e;
});
}
return this.setupP;
});
async parse(opts = {}) {
if (!this.setupP) {
this.setupP = this._parse(opts).catch((e) => {
this.setupP = undefined;
throw e;
});
}
return this.setupP;
}
hasRefSeq(seqId_1) {
return __awaiter(this, arguments, void 0, function* (seqId, opts = {}) {
var _a;
const header = yield this.parse(opts);
return !!((_a = header.indices[seqId]) === null || _a === void 0 ? void 0 : _a.binIndex);
});
async hasRefSeq(seqId, opts = {}) {
const header = await this.parse(opts);
return !!header.indices[seqId]?.binIndex;
}

@@ -215,0 +197,0 @@ }

@@ -1,3 +0,2 @@

import { Buffer } from 'buffer';
import { GenericFilehandle } from 'generic-filehandle';
import { GenericFilehandle } from 'generic-filehandle2';
import BAI from './bai';

@@ -63,3 +62,3 @@ import CSI from './csi';

fetchPairs(chrId: number, feats: BAMFeature[][], opts: BamOpts): Promise<BAMFeature[]>;
_readRegion(position: number, size: number, opts?: BaseOpts): Promise<Buffer>;
_readRegion(position: number, size: number, opts?: BaseOpts): Promise<Uint8Array<ArrayBuffer>>;
_readChunk({ chunk, opts }: {

@@ -69,3 +68,3 @@ chunk: Chunk;

}): Promise<{
data: Buffer;
data: Uint8Array<ArrayBuffer>;
cpositions: number[];

@@ -75,3 +74,3 @@ dpositions: number[];

}>;
readBamFeatures(ba: Buffer, cpositions: number[], dpositions: number[], chunk: Chunk): Promise<BAMFeature[]>;
readBamFeatures(ba: Uint8Array, cpositions: number[], dpositions: number[], chunk: Chunk): Promise<BAMFeature[]>;
hasRefSeq(seqName: string): Promise<boolean | undefined>;

@@ -78,0 +77,0 @@ lineCount(seqName: string): Promise<number>;

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncDelegator = (this && this.__asyncDelegator) || function (o) {
var i, p;
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }
};
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -41,6 +7,5 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

exports.BAM_MAGIC = void 0;
const buffer_1 = require("buffer");
const crc32_1 = __importDefault(require("crc/crc32"));
const bgzf_filehandle_1 = require("@gmod/bgzf-filehandle");
const generic_filehandle_1 = require("generic-filehandle");
const generic_filehandle2_1 = require("generic-filehandle2");
const abortable_promise_cache_1 = __importDefault(require("@gmod/abortable-promise-cache"));

@@ -56,24 +21,8 @@ const quick_lru_1 = __importDefault(require("quick-lru"));

const blockLen = 1 << 16;
function gen2array(gen) {
return __awaiter(this, void 0, void 0, function* () {
var _a, gen_1, gen_1_1;
var _b, e_1, _c, _d;
let out = [];
try {
for (_a = true, gen_1 = __asyncValues(gen); gen_1_1 = yield gen_1.next(), _b = gen_1_1.done, !_b; _a = true) {
_d = gen_1_1.value;
_a = false;
const x = _d;
out = out.concat(x);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_a && !_b && (_c = gen_1.return)) yield _c.call(gen_1);
}
finally { if (e_1) throw e_1.error; }
}
return out;
});
async function gen2array(gen) {
let out = [];
for await (const x of gen) {
out = out.concat(x);
}
return out;
}

@@ -95,17 +44,25 @@ class NullFilehandle {

class BamFile {
renameRefSeq;
bam;
header;
chrToIndex;
indexToChr;
yieldThreadTime;
index;
htsget = false;
headerP;
featureCache = new abortable_promise_cache_1.default({
cache: new quick_lru_1.default({
maxSize: 50,
}),
fill: async (args, signal) => {
const { chunk, opts } = args;
const { data, cpositions, dpositions } = await this._readChunk({
chunk,
opts: { ...opts, signal },
});
return this.readBamFeatures(data, cpositions, dpositions, chunk);
},
});
constructor({ bamFilehandle, bamPath, bamUrl, baiPath, baiFilehandle, baiUrl, csiPath, csiFilehandle, csiUrl, htsget, yieldThreadTime = 100, renameRefSeqs = n => n, }) {
this.htsget = false;
this.featureCache = new abortable_promise_cache_1.default({
cache: new quick_lru_1.default({
maxSize: 50,
}),
fill: (args, signal) => __awaiter(this, void 0, void 0, function* () {
const { chunk, opts } = args;
const { data, cpositions, dpositions } = yield this._readChunk({
chunk,
opts: Object.assign(Object.assign({}, opts), { signal }),
});
return this.readBamFeatures(data, cpositions, dpositions, chunk);
}),
});
this.renameRefSeq = renameRefSeqs;

@@ -116,6 +73,6 @@ if (bamFilehandle) {

else if (bamPath) {
this.bam = new generic_filehandle_1.LocalFile(bamPath);
this.bam = new generic_filehandle2_1.LocalFile(bamPath);
}
else if (bamUrl) {
this.bam = new generic_filehandle_1.RemoteFile(bamUrl);
this.bam = new generic_filehandle2_1.RemoteFile(bamUrl);
}

@@ -133,6 +90,6 @@ else if (htsget) {

else if (csiPath) {
this.index = new csi_1.default({ filehandle: new generic_filehandle_1.LocalFile(csiPath) });
this.index = new csi_1.default({ filehandle: new generic_filehandle2_1.LocalFile(csiPath) });
}
else if (csiUrl) {
this.index = new csi_1.default({ filehandle: new generic_filehandle_1.RemoteFile(csiUrl) });
this.index = new csi_1.default({ filehandle: new generic_filehandle2_1.RemoteFile(csiUrl) });
}

@@ -143,12 +100,12 @@ else if (baiFilehandle) {

else if (baiPath) {
this.index = new bai_1.default({ filehandle: new generic_filehandle_1.LocalFile(baiPath) });
this.index = new bai_1.default({ filehandle: new generic_filehandle2_1.LocalFile(baiPath) });
}
else if (baiUrl) {
this.index = new bai_1.default({ filehandle: new generic_filehandle_1.RemoteFile(baiUrl) });
this.index = new bai_1.default({ filehandle: new generic_filehandle2_1.RemoteFile(baiUrl) });
}
else if (bamPath) {
this.index = new bai_1.default({ filehandle: new generic_filehandle_1.LocalFile(`${bamPath}.bai`) });
this.index = new bai_1.default({ filehandle: new generic_filehandle2_1.LocalFile(`${bamPath}.bai`) });
}
else if (bamUrl) {
this.index = new bai_1.default({ filehandle: new generic_filehandle_1.RemoteFile(`${bamUrl}.bai`) });
this.index = new bai_1.default({ filehandle: new generic_filehandle2_1.RemoteFile(`${bamUrl}.bai`) });
}

@@ -163,35 +120,31 @@ else if (htsget) {

}
getHeaderPre(origOpts) {
return __awaiter(this, void 0, void 0, function* () {
const opts = (0, util_1.makeOpts)(origOpts);
if (!this.index) {
return;
}
const indexData = yield this.index.parse(opts);
const ret = indexData.firstDataLine
? indexData.firstDataLine.blockPosition + 65535
: undefined;
let buffer;
if (ret) {
const s = ret + blockLen;
const res = yield this.bam.read(buffer_1.Buffer.alloc(s), 0, s, 0, opts);
if (!res.bytesRead) {
throw new Error('Error reading header');
}
buffer = res.buffer.subarray(0, Math.min(res.bytesRead, ret));
}
else {
buffer = yield this.bam.readFile(opts);
}
const uncba = yield (0, bgzf_filehandle_1.unzip)(buffer);
if (uncba.readInt32LE(0) !== exports.BAM_MAGIC) {
throw new Error('Not a BAM file');
}
const headLen = uncba.readInt32LE(4);
this.header = uncba.toString('utf8', 8, 8 + headLen);
const { chrToIndex, indexToChr } = yield this._readRefSeqs(headLen + 8, 65535, opts);
this.chrToIndex = chrToIndex;
this.indexToChr = indexToChr;
return (0, sam_1.parseHeaderText)(this.header);
});
async getHeaderPre(origOpts) {
const opts = (0, util_1.makeOpts)(origOpts);
if (!this.index) {
return;
}
const indexData = await this.index.parse(opts);
const ret = indexData.firstDataLine
? indexData.firstDataLine.blockPosition + 65535
: undefined;
let buffer;
if (ret) {
const s = ret + blockLen;
buffer = await this.bam.read(s, 0);
}
else {
buffer = await this.bam.readFile(opts);
}
const uncba = await (0, bgzf_filehandle_1.unzip)(buffer);
const dataView = new DataView(uncba.buffer);
if (dataView.getInt32(0, true) !== exports.BAM_MAGIC) {
throw new Error('Not a BAM file');
}
const headLen = dataView.getInt32(4, true);
const decoder = new TextDecoder('utf8');
this.header = decoder.decode(uncba.subarray(8, 8 + headLen));
const { chrToIndex, indexToChr } = await this._readRefSeqs(headLen + 8, 65535, opts);
this.chrToIndex = chrToIndex;
this.indexToChr = indexToChr;
return (0, sam_1.parseHeaderText)(this.header);
}

@@ -207,267 +160,235 @@ getHeader(opts) {

}
getHeaderText() {
return __awaiter(this, arguments, void 0, function* (opts = {}) {
yield this.getHeader(opts);
return this.header;
});
async getHeaderText(opts = {}) {
await this.getHeader(opts);
return this.header;
}
// the full length of the refseq block is not given in advance so this grabs
// a chunk and doubles it if all refseqs haven't been processed
_readRefSeqs(start, refSeqBytes, opts) {
return __awaiter(this, void 0, void 0, function* () {
if (start > refSeqBytes) {
async _readRefSeqs(start, refSeqBytes, opts) {
if (start > refSeqBytes) {
return this._readRefSeqs(start, refSeqBytes * 2, opts);
}
// const size = refSeqBytes + blockLen <-- use this?
const buffer = await this.bam.read(refSeqBytes, 0, opts);
const uncba = await (0, bgzf_filehandle_1.unzip)(buffer);
const dataView = new DataView(uncba.buffer);
const nRef = dataView.getInt32(start, true);
let p = start + 4;
const chrToIndex = {};
const indexToChr = [];
const decoder = new TextDecoder('utf8');
for (let i = 0; i < nRef; i += 1) {
const lName = dataView.getInt32(p, true);
const refName = this.renameRefSeq(decoder.decode(uncba.subarray(p + 4, p + 4 + lName - 1)));
const lRef = dataView.getInt32(p + lName + 4, true);
chrToIndex[refName] = i;
indexToChr.push({ refName, length: lRef });
p = p + 8 + lName;
if (p > uncba.length) {
console.warn(`BAM header is very big. Re-fetching ${refSeqBytes} bytes.`);
return this._readRefSeqs(start, refSeqBytes * 2, opts);
}
const size = refSeqBytes + blockLen;
const { bytesRead, buffer } = yield this.bam.read(buffer_1.Buffer.alloc(size), 0, refSeqBytes, 0, opts);
if (!bytesRead) {
throw new Error('Error reading refseqs from header');
}
const uncba = yield (0, bgzf_filehandle_1.unzip)(buffer.subarray(0, Math.min(bytesRead, refSeqBytes)));
const nRef = uncba.readInt32LE(start);
let p = start + 4;
const chrToIndex = {};
const indexToChr = [];
for (let i = 0; i < nRef; i += 1) {
const lName = uncba.readInt32LE(p);
const refName = this.renameRefSeq(uncba.toString('utf8', p + 4, p + 4 + lName - 1));
const lRef = uncba.readInt32LE(p + lName + 4);
chrToIndex[refName] = i;
indexToChr.push({ refName, length: lRef });
p = p + 8 + lName;
if (p > uncba.length) {
console.warn(`BAM header is very big. Re-fetching ${refSeqBytes} bytes.`);
return this._readRefSeqs(start, refSeqBytes * 2, opts);
}
}
return { chrToIndex, indexToChr };
});
}
return { chrToIndex, indexToChr };
}
getRecordsForRange(chr, min, max, opts) {
return __awaiter(this, void 0, void 0, function* () {
return gen2array(this.streamRecordsForRange(chr, min, max, opts));
});
async getRecordsForRange(chr, min, max, opts) {
return gen2array(this.streamRecordsForRange(chr, min, max, opts));
}
streamRecordsForRange(chr, min, max, opts) {
return __asyncGenerator(this, arguments, function* streamRecordsForRange_1() {
var _a;
yield __await(this.getHeader(opts));
const chrId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[chr];
if (chrId === undefined || !this.index) {
yield yield __await([]);
}
else {
const chunks = yield __await(this.index.blocksForRange(chrId, min - 1, max, opts));
yield __await(yield* __asyncDelegator(__asyncValues(this._fetchChunkFeatures(chunks, chrId, min, max, opts))));
}
});
async *streamRecordsForRange(chr, min, max, opts) {
await this.getHeader(opts);
const chrId = this.chrToIndex?.[chr];
if (chrId === undefined || !this.index) {
yield [];
}
else {
const chunks = await this.index.blocksForRange(chrId, min - 1, max, opts);
yield* this._fetchChunkFeatures(chunks, chrId, min, max, opts);
}
}
_fetchChunkFeatures(chunks_1, chrId_1, min_1, max_1) {
return __asyncGenerator(this, arguments, function* _fetchChunkFeatures_1(chunks, chrId, min, max, opts = {}) {
const { viewAsPairs } = opts;
const feats = [];
let done = false;
for (const chunk of chunks) {
const records = yield __await(this.featureCache.get(chunk.toString(), { chunk, opts }, opts.signal));
const recs = [];
for (const feature of records) {
if (feature.ref_id === chrId) {
if (feature.start >= max) {
// past end of range, can stop iterating
done = true;
break;
}
else if (feature.end >= min) {
// must be in range
recs.push(feature);
}
async *_fetchChunkFeatures(chunks, chrId, min, max, opts = {}) {
const { viewAsPairs } = opts;
const feats = [];
let done = false;
for (const chunk of chunks) {
const records = await this.featureCache.get(chunk.toString(), { chunk, opts }, opts.signal);
const recs = [];
for (const feature of records) {
if (feature.ref_id === chrId) {
if (feature.start >= max) {
// past end of range, can stop iterating
done = true;
break;
}
else if (feature.end >= min) {
// must be in range
recs.push(feature);
}
}
feats.push(recs);
yield yield __await(recs);
if (done) {
break;
}
}
(0, util_1.checkAbortSignal)(opts.signal);
if (viewAsPairs) {
yield yield __await(this.fetchPairs(chrId, feats, opts));
feats.push(recs);
yield recs;
if (done) {
break;
}
});
}
(0, util_1.checkAbortSignal)(opts.signal);
if (viewAsPairs) {
yield this.fetchPairs(chrId, feats, opts);
}
}
fetchPairs(chrId, feats, opts) {
return __awaiter(this, void 0, void 0, function* () {
const { pairAcrossChr, maxInsertSize = 200000 } = opts;
const unmatedPairs = {};
const readIds = {};
feats.map(ret => {
const readNames = {};
for (const element of ret) {
const name = element.name;
const id = element.id;
if (!readNames[name]) {
readNames[name] = 0;
}
readNames[name]++;
readIds[id] = 1;
async fetchPairs(chrId, feats, opts) {
const { pairAcrossChr, maxInsertSize = 200000 } = opts;
const unmatedPairs = {};
const readIds = {};
feats.map(ret => {
const readNames = {};
for (const element of ret) {
const name = element.name;
const id = element.id;
if (!readNames[name]) {
readNames[name] = 0;
}
for (const [k, v] of Object.entries(readNames)) {
if (v === 1) {
unmatedPairs[k] = true;
}
readNames[name]++;
readIds[id] = 1;
}
for (const [k, v] of Object.entries(readNames)) {
if (v === 1) {
unmatedPairs[k] = true;
}
});
const matePromises = [];
feats.map(ret => {
for (const f of ret) {
const name = f.name;
const start = f.start;
const pnext = f.next_pos;
const rnext = f.next_refid;
if (this.index &&
unmatedPairs[name] &&
(pairAcrossChr ||
(rnext === chrId && Math.abs(start - pnext) < maxInsertSize))) {
matePromises.push(this.index.blocksForRange(rnext, pnext, pnext + 1, opts));
}
}
});
const matePromises = [];
feats.map(ret => {
for (const f of ret) {
const name = f.name;
const start = f.start;
const pnext = f.next_pos;
const rnext = f.next_refid;
if (this.index &&
unmatedPairs[name] &&
(pairAcrossChr ||
(rnext === chrId && Math.abs(start - pnext) < maxInsertSize))) {
matePromises.push(this.index.blocksForRange(rnext, pnext, pnext + 1, opts));
}
}
});
// filter out duplicate chunks (the blocks are lists of chunks, blocks are
// concatenated, then filter dup chunks)
const map = new Map();
const res = await Promise.all(matePromises);
for (const m of res.flat()) {
if (!map.has(m.toString())) {
map.set(m.toString(), m);
}
}
const mateFeatPromises = await Promise.all([...map.values()].map(async (c) => {
const { data, cpositions, dpositions, chunk } = await this._readChunk({
chunk: c,
opts,
});
// filter out duplicate chunks (the blocks are lists of chunks, blocks are
// concatenated, then filter dup chunks)
const map = new Map();
const res = yield Promise.all(matePromises);
for (const m of res.flat()) {
if (!map.has(m.toString())) {
map.set(m.toString(), m);
const mateRecs = [];
for (const feature of await this.readBamFeatures(data, cpositions, dpositions, chunk)) {
if (unmatedPairs[feature.name] && !readIds[feature.id]) {
mateRecs.push(feature);
}
}
const mateFeatPromises = yield Promise.all([...map.values()].map((c) => __awaiter(this, void 0, void 0, function* () {
const { data, cpositions, dpositions, chunk } = yield this._readChunk({
chunk: c,
opts,
});
const mateRecs = [];
for (const feature of yield this.readBamFeatures(data, cpositions, dpositions, chunk)) {
if (unmatedPairs[feature.name] && !readIds[feature.id]) {
mateRecs.push(feature);
}
}
return mateRecs;
})));
return mateFeatPromises.flat();
});
return mateRecs;
}));
return mateFeatPromises.flat();
}
_readRegion(position_1, size_1) {
return __awaiter(this, arguments, void 0, function* (position, size, opts = {}) {
const { bytesRead, buffer } = yield this.bam.read(buffer_1.Buffer.alloc(size), 0, size, position, opts);
return buffer.subarray(0, Math.min(bytesRead, size));
});
async _readRegion(position, size, opts = {}) {
return this.bam.read(size, position, opts);
}
_readChunk(_a) {
return __awaiter(this, arguments, void 0, function* ({ chunk, opts }) {
const buffer = yield this._readRegion(chunk.minv.blockPosition, chunk.fetchedSize(), opts);
const { buffer: data, cpositions, dpositions, } = yield (0, bgzf_filehandle_1.unzipChunkSlice)(buffer, chunk);
return { data, cpositions, dpositions, chunk };
});
async _readChunk({ chunk, opts }) {
const buffer = await this._readRegion(chunk.minv.blockPosition, chunk.fetchedSize(), opts);
const { buffer: data, cpositions, dpositions, } = await (0, bgzf_filehandle_1.unzipChunkSlice)(buffer, chunk);
return { data, cpositions, dpositions, chunk };
}
readBamFeatures(ba, cpositions, dpositions, chunk) {
return __awaiter(this, void 0, void 0, function* () {
let blockStart = 0;
const sink = [];
let pos = 0;
let last = +Date.now();
while (blockStart + 4 < ba.length) {
const blockSize = ba.readInt32LE(blockStart);
const blockEnd = blockStart + 4 + blockSize - 1;
// increment position to the current decompressed status
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (dpositions) {
while (blockStart + chunk.minv.dataPosition >= dpositions[pos++]) { }
pos--;
async readBamFeatures(ba, cpositions, dpositions, chunk) {
let blockStart = 0;
const sink = [];
let pos = 0;
let last = +Date.now();
const dataView = new DataView(ba.buffer);
while (blockStart + 4 < ba.length) {
const blockSize = dataView.getInt32(blockStart, true);
const blockEnd = blockStart + 4 + blockSize - 1;
// increment position to the current decompressed status
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (dpositions) {
while (blockStart + chunk.minv.dataPosition >= dpositions[pos++]) { }
pos--;
}
// only try to read the feature if we have all the bytes for it
if (blockEnd < ba.length) {
const feature = new record_1.default({
bytes: {
byteArray: ba,
start: blockStart,
end: blockEnd,
},
// the below results in an automatically calculated file-offset based
// ID if the info for that is available, otherwise crc32 of the
// features
//
// cpositions[pos] refers to actual file offset of a bgzip block
// boundaries
//
// we multiply by (1 <<8) in order to make sure each block has a
// "unique" address space so that data in that block could never
// overlap
//
// then the blockStart-dpositions is an uncompressed file offset from
// that bgzip block boundary, and since the cpositions are multiplied
// by (1 << 8) these uncompressed offsets get a unique space
//
// this has an extra chunk.minv.dataPosition added on because it
// blockStart starts at 0 instead of chunk.minv.dataPosition
//
// the +1 is just to avoid any possible uniqueId 0 but this does not
// realistically happen
fileOffset: cpositions.length > 0
? cpositions[pos] * (1 << 8) +
(blockStart - dpositions[pos]) +
chunk.minv.dataPosition +
1
: // must be slice, not subarray for buffer polyfill on web
// @ts-expect-error
crc32_1.default.signed(ba.subarray(blockStart, blockEnd)),
});
sink.push(feature);
if (this.yieldThreadTime && +Date.now() - last > this.yieldThreadTime) {
await (0, util_1.timeout)(1);
last = +Date.now();
}
// only try to read the feature if we have all the bytes for it
if (blockEnd < ba.length) {
const feature = new record_1.default({
bytes: {
byteArray: ba,
start: blockStart,
end: blockEnd,
},
// the below results in an automatically calculated file-offset based
// ID if the info for that is available, otherwise crc32 of the
// features
//
// cpositions[pos] refers to actual file offset of a bgzip block
// boundaries
//
// we multiply by (1 <<8) in order to make sure each block has a
// "unique" address space so that data in that block could never
// overlap
//
// then the blockStart-dpositions is an uncompressed file offset from
// that bgzip block boundary, and since the cpositions are multiplied
// by (1 << 8) these uncompressed offsets get a unique space
//
// this has an extra chunk.minv.dataPosition added on because it
// blockStart starts at 0 instead of chunk.minv.dataPosition
//
// the +1 is just to avoid any possible uniqueId 0 but this does not
// realistically happen
fileOffset: cpositions.length > 0
? cpositions[pos] * (1 << 8) +
(blockStart - dpositions[pos]) +
chunk.minv.dataPosition +
1
: // must be slice, not subarray for buffer polyfill on web
// eslint-disable-next-line @typescript-eslint/no-deprecated
crc32_1.default.signed(ba.slice(blockStart, blockEnd)),
});
sink.push(feature);
if (this.yieldThreadTime && +Date.now() - last > this.yieldThreadTime) {
yield (0, util_1.timeout)(1);
last = +Date.now();
}
}
blockStart = blockEnd + 1;
}
return sink;
});
blockStart = blockEnd + 1;
}
return sink;
}
hasRefSeq(seqName) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
return seqId === undefined ? false : (_b = this.index) === null || _b === void 0 ? void 0 : _b.hasRefSeq(seqId);
});
async hasRefSeq(seqName) {
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined ? false : this.index?.hasRefSeq(seqId);
}
lineCount(seqName) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
return seqId === undefined || !this.index ? 0 : this.index.lineCount(seqId);
});
async lineCount(seqName) {
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined || !this.index ? 0 : this.index.lineCount(seqId);
}
indexCov(seqName, start, end) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (!this.index) {
return [];
}
yield this.index.parse();
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
return seqId === undefined ? [] : this.index.indexCov(seqId, start, end);
});
async indexCov(seqName, start, end) {
if (!this.index) {
return [];
}
await this.index.parse();
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined ? [] : this.index.indexCov(seqId, start, end);
}
blocksForRange(seqName, start, end, opts) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (!this.index) {
return [];
}
yield this.index.parse();
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
return seqId === undefined
? []
: this.index.blocksForRange(seqId, start, end, opts);
});
async blocksForRange(seqName, start, end, opts) {
if (!this.index) {
return [];
}
await this.index.parse();
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined
? []
: this.index.blocksForRange(seqId, start, end, opts);
}

@@ -474,0 +395,0 @@ }

@@ -7,3 +7,3 @@ import VirtualOffset from './virtualOffset';

_fetchedSize?: number | undefined;
buffer?: Buffer;
buffer?: Uint8Array;
constructor(minv: VirtualOffset, maxv: VirtualOffset, bin: number, _fetchedSize?: number | undefined);

@@ -10,0 +10,0 @@ toUniqueString(): string;

@@ -5,2 +5,7 @@ "use strict";

class Chunk {
minv;
maxv;
bin;
_fetchedSize;
buffer;
constructor(minv, maxv, bin, _fetchedSize) {

@@ -7,0 +12,0 @@ this.minv = minv;

@@ -12,3 +12,3 @@ import VirtualOffset from './virtualOffset';

indexCov(): Promise<never[]>;
parseAuxData(bytes: Buffer, offset: number): {
parseAuxData(bytes: Uint8Array, offset: number): {
refNameToId: Record<string, number>;

@@ -15,0 +15,0 @@ refIdToName: string[];

@@ -18,18 +18,19 @@ "use strict";

});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -47,28 +48,22 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

function lshift(num, bits) {
return num * Math.pow(2, bits);
return num * 2 ** bits;
}
function rshift(num, bits) {
return Math.floor(num / Math.pow(2, bits));
return Math.floor(num / 2 ** bits);
}
class CSI extends indexFile_1.default {
constructor() {
super(...arguments);
this.maxBinNumber = 0;
this.depth = 0;
this.minShift = 0;
maxBinNumber = 0;
depth = 0;
minShift = 0;
setupP;
async lineCount(refId, opts) {
const indexData = await this.parse(opts);
return indexData.indices[refId]?.stats?.lineCount || 0;
}
lineCount(refId, opts) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const indexData = yield this.parse(opts);
return ((_b = (_a = indexData.indices[refId]) === null || _a === void 0 ? void 0 : _a.stats) === null || _b === void 0 ? void 0 : _b.lineCount) || 0;
});
async indexCov() {
return [];
}
indexCov() {
return __awaiter(this, void 0, void 0, function* () {
return [];
});
}
parseAuxData(bytes, offset) {
const formatFlags = bytes.readInt32LE(offset);
const dataView = new DataView(bytes.buffer);
const formatFlags = dataView.getUint32(offset, true);
const coordinateType = formatFlags & 0x10000 ? 'zero-based-half-open' : '1-based-closed';

@@ -80,11 +75,12 @@ const format = { 0: 'generic', 1: 'SAM', 2: 'VCF' }[formatFlags & 0xf];

const columnNumbers = {
ref: bytes.readInt32LE(offset + 4),
start: bytes.readInt32LE(offset + 8),
end: bytes.readInt32LE(offset + 12),
ref: dataView.getInt32(offset + 4, true),
start: dataView.getInt32(offset + 8, true),
end: dataView.getInt32(offset + 12, true),
};
const metaValue = bytes.readInt32LE(offset + 16);
const metaValue = dataView.getInt32(offset + 16, true);
const metaChar = metaValue ? String.fromCharCode(metaValue) : '';
const skipLines = bytes.readInt32LE(offset + 20);
const nameSectionLength = bytes.readInt32LE(offset + 24);
return Object.assign({ columnNumbers,
const skipLines = dataView.getInt32(offset + 20, true);
const nameSectionLength = dataView.getInt32(offset + 24, true);
return {
columnNumbers,
coordinateType,

@@ -95,99 +91,103 @@ metaValue,

format,
formatFlags }, (0, util_1.parseNameBytes)(bytes.subarray(offset + 28, offset + 28 + nameSectionLength), this.renameRefSeq));
formatFlags,
...(0, util_1.parseNameBytes)(bytes.subarray(offset + 28, offset + 28 + nameSectionLength), this.renameRefSeq),
};
}
// fetch and parse the index
_parse(opts) {
return __awaiter(this, void 0, void 0, function* () {
const buffer = yield this.filehandle.readFile(opts);
const bytes = yield (0, bgzf_filehandle_1.unzip)(buffer);
let csiVersion;
// check TBI magic numbers
if (bytes.readUInt32LE(0) === CSI1_MAGIC) {
csiVersion = 1;
}
else if (bytes.readUInt32LE(0) === CSI2_MAGIC) {
csiVersion = 2;
}
else {
throw new Error('Not a CSI file');
// TODO: do we need to support big-endian CSI files?
}
this.minShift = bytes.readInt32LE(4);
this.depth = bytes.readInt32LE(8);
this.maxBinNumber = ((1 << ((this.depth + 1) * 3)) - 1) / 7;
const auxLength = bytes.readInt32LE(12);
const aux = auxLength >= 30 ? this.parseAuxData(bytes, 16) : undefined;
const refCount = bytes.readInt32LE(16 + auxLength);
// read the indexes for each reference sequence
let curr = 16 + auxLength + 4;
let firstDataLine;
const indices = new Array(refCount);
for (let i = 0; i < refCount; i++) {
// the binning index
const binCount = bytes.readInt32LE(curr);
async _parse(opts) {
const buffer = await this.filehandle.readFile(opts);
const bytes = await (0, bgzf_filehandle_1.unzip)(buffer);
const dataView = new DataView(bytes.buffer);
let csiVersion;
const magic = dataView.getUint32(0, true);
if (magic === CSI1_MAGIC) {
csiVersion = 1;
}
else if (magic === CSI2_MAGIC) {
csiVersion = 2;
}
else {
throw new Error(`Not a CSI file ${magic}`);
// TODO: do we need to support big-endian CSI files?
}
this.minShift = dataView.getInt32(4, true);
this.depth = dataView.getInt32(8, true);
this.maxBinNumber = ((1 << ((this.depth + 1) * 3)) - 1) / 7;
const auxLength = dataView.getInt32(12, true);
const aux = auxLength >= 30 ? this.parseAuxData(bytes, 16) : undefined;
const refCount = dataView.getInt32(16 + auxLength, true);
// read the indexes for each reference sequence
let curr = 16 + auxLength + 4;
let firstDataLine;
const indices = new Array(refCount);
for (let i = 0; i < refCount; i++) {
// the binning index
const binCount = dataView.getInt32(curr, true);
curr += 4;
const binIndex = {};
let stats; // < provided by parsing a pseudo-bin, if present
for (let j = 0; j < binCount; j++) {
const bin = dataView.getUint32(curr, true);
curr += 4;
const binIndex = {};
let stats; // < provided by parsing a pseudo-bin, if present
for (let j = 0; j < binCount; j++) {
const bin = bytes.readUInt32LE(curr);
if (bin > this.maxBinNumber) {
stats = (0, util_1.parsePseudoBin)(bytes, curr + 28);
curr += 28 + 16;
}
else {
firstDataLine = (0, util_1.findFirstData)(firstDataLine, (0, virtualOffset_1.fromBytes)(bytes, curr));
curr += 8;
const chunkCount = dataView.getInt32(curr, true);
curr += 4;
if (bin > this.maxBinNumber) {
stats = (0, util_1.parsePseudoBin)(bytes, curr + 28);
curr += 28 + 16;
}
else {
firstDataLine = (0, util_1.findFirstData)(firstDataLine, (0, virtualOffset_1.fromBytes)(bytes, curr));
const chunks = new Array(chunkCount);
for (let k = 0; k < chunkCount; k += 1) {
const u = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
const chunkCount = bytes.readInt32LE(curr);
curr += 4;
const chunks = new Array(chunkCount);
for (let k = 0; k < chunkCount; k += 1) {
const u = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
const v = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
firstDataLine = (0, util_1.findFirstData)(firstDataLine, u);
chunks[k] = new chunk_1.default(u, v, bin);
}
binIndex[bin] = chunks;
const v = (0, virtualOffset_1.fromBytes)(bytes, curr);
curr += 8;
firstDataLine = (0, util_1.findFirstData)(firstDataLine, u);
chunks[k] = new chunk_1.default(u, v, bin);
}
binIndex[bin] = chunks;
}
indices[i] = { binIndex, stats };
}
return Object.assign({ csiVersion,
firstDataLine,
indices,
refCount, csi: true, maxBlockSize: 1 << 16 }, aux);
});
indices[i] = { binIndex, stats };
}
return {
csiVersion,
firstDataLine,
indices,
refCount,
csi: true,
maxBlockSize: 1 << 16,
...aux,
};
}
blocksForRange(refId_1, min_1, max_1) {
return __awaiter(this, arguments, void 0, function* (refId, min, max, opts = {}) {
if (min < 0) {
min = 0;
}
const indexData = yield this.parse(opts);
const ba = indexData.indices[refId];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!ba) {
return [];
}
const overlappingBins = this.reg2bins(min, max);
if (overlappingBins.length === 0) {
return [];
}
const chunks = [];
// Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
for (const [start, end] of overlappingBins) {
for (let bin = start; bin <= end; bin++) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ba.binIndex[bin]) {
const binChunks = ba.binIndex[bin];
for (const c of binChunks) {
chunks.push(c);
}
async blocksForRange(refId, min, max, opts = {}) {
if (min < 0) {
min = 0;
}
const indexData = await this.parse(opts);
const ba = indexData.indices[refId];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!ba) {
return [];
}
const overlappingBins = this.reg2bins(min, max);
if (overlappingBins.length === 0) {
return [];
}
const chunks = [];
// Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
for (const [start, end] of overlappingBins) {
for (let bin = start; bin <= end; bin++) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ba.binIndex[bin]) {
const binChunks = ba.binIndex[bin];
for (const c of binChunks) {
chunks.push(c);
}
}
}
return (0, util_1.optimizeChunks)(chunks, new virtualOffset_1.default(0, 0));
});
}
return (0, util_1.optimizeChunks)(chunks, new virtualOffset_1.default(0, 0));
}

@@ -203,4 +203,4 @@ /**

}
if (end > Math.pow(2, 50)) {
end = Math.pow(2, 34);
if (end > 2 ** 50) {
end = 2 ** 34;
} // 17 GiB ought to be enough for anybody

@@ -222,19 +222,14 @@ end -= 1;

}
parse() {
return __awaiter(this, arguments, void 0, function* (opts = {}) {
if (!this.setupP) {
this.setupP = this._parse(opts).catch((e) => {
this.setupP = undefined;
throw e;
});
}
return this.setupP;
});
async parse(opts = {}) {
if (!this.setupP) {
this.setupP = this._parse(opts).catch((e) => {
this.setupP = undefined;
throw e;
});
}
return this.setupP;
}
hasRefSeq(seqId_1) {
return __awaiter(this, arguments, void 0, function* (seqId, opts = {}) {
var _a;
const header = yield this.parse(opts);
return !!((_a = header.indices[seqId]) === null || _a === void 0 ? void 0 : _a.binIndex);
});
async hasRefSeq(seqId, opts = {}) {
const header = await this.parse(opts);
return !!header.indices[seqId]?.binIndex;
}

@@ -241,0 +236,0 @@ }

@@ -1,2 +0,1 @@

import { Buffer } from 'buffer';
import { BaseOpts, BamOpts } from './util';

@@ -17,3 +16,3 @@ import BamFile from './bamFile';

}): Promise<{
data: Buffer;
data: Uint8Array<ArrayBufferLike>;
cpositions: never[];

@@ -20,0 +19,0 @@ dpositions: never[];

@@ -18,81 +18,50 @@ "use strict";

});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __asyncDelegator = (this && this.__asyncDelegator) || function (o) {
var i, p;
return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i;
function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }
};
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const bgzf_filehandle_1 = require("@gmod/bgzf-filehandle");
const buffer_1 = require("buffer");
const util_1 = require("./util");
const bamFile_1 = __importStar(require("./bamFile"));
const sam_1 = require("./sam");
function concat(arr, opts) {
return __awaiter(this, void 0, void 0, function* () {
const res = yield Promise.all(arr.map((chunk) => __awaiter(this, void 0, void 0, function* () {
const { url, headers } = chunk;
if (url.startsWith('data:')) {
return buffer_1.Buffer.from(url.split(',')[1], 'base64');
async function concat(arr, opts) {
const res = await Promise.all(arr.map(async (chunk) => {
const { url, headers } = chunk;
if (url.startsWith('data:')) {
// @ts-expect-error
return Uint8Array.fromBase64(url.split(',')[1], 'base64');
}
else {
//remove referer header, it is not even allowed to be specified
// @ts-expect-error
const { referer, ...rest } = headers;
const res = await fetch(url, {
...opts,
headers: { ...opts?.headers, ...rest },
});
if (!res.ok) {
throw new Error(`HTTP ${res.status} fetching ${url}: ${await res.text()}`);
}
else {
//remove referer header, it is not even allowed to be specified
// @ts-expect-error
const { referer } = headers, rest = __rest(headers, ["referer"]);
const res = yield fetch(url, Object.assign(Object.assign({}, opts), { headers: Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts.headers), rest) }));
if (!res.ok) {
throw new Error(`HTTP ${res.status} fetching ${url}: ${yield res.text()}`);
}
return buffer_1.Buffer.from(yield res.arrayBuffer());
}
})));
return buffer_1.Buffer.concat(yield Promise.all(res.map(elt => (0, bgzf_filehandle_1.unzip)(elt))));
});
return new Uint8Array(await res.arrayBuffer());
}
}));
return (0, util_1.concatUint8Array)(await Promise.all(res.map(elt => (0, bgzf_filehandle_1.unzip)(elt))));
}
class HtsgetFile extends bamFile_1.default {
baseUrl;
trackId;
constructor(args) {

@@ -103,97 +72,98 @@ super({ htsget: true });

}
streamRecordsForRange(chr, min, max, opts) {
return __asyncGenerator(this, arguments, function* streamRecordsForRange_1() {
var _a;
const base = `${this.baseUrl}/${this.trackId}`;
const url = `${base}?referenceName=${chr}&start=${min}&end=${max}&format=BAM`;
const chrId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[chr];
if (chrId === undefined) {
yield yield __await([]);
async *streamRecordsForRange(chr, min, max, opts) {
const base = `${this.baseUrl}/${this.trackId}`;
const url = `${base}?referenceName=${chr}&start=${min}&end=${max}&format=BAM`;
const chrId = this.chrToIndex?.[chr];
if (chrId === undefined) {
yield [];
}
else {
const result = await fetch(url, { ...opts });
if (!result.ok) {
throw new Error(`HTTP ${result.status} fetching ${url}: ${await result.text()}`);
}
else {
const result = yield __await(fetch(url, Object.assign({}, opts)));
if (!result.ok) {
throw new Error(`HTTP ${result.status} fetching ${url}: ${yield __await(result.text())}`);
}
const data = yield __await(result.json());
const uncba = yield __await(concat(data.htsget.urls.slice(1), opts));
yield __await(yield* __asyncDelegator(__asyncValues(this._fetchChunkFeatures([
// fake stuff to pretend to be a Chunk
{
buffer: uncba,
_fetchedSize: undefined,
bin: 0,
compareTo() {
return 0;
},
toUniqueString() {
return `${chr}_${min}_${max}`;
},
fetchedSize() {
return 0;
},
minv: {
dataPosition: 0,
blockPosition: 0,
compareTo: () => 0,
},
maxv: {
dataPosition: Number.MAX_SAFE_INTEGER,
blockPosition: 0,
compareTo: () => 0,
},
toString() {
return `${chr}_${min}_${max}`;
},
const data = await result.json();
const uncba = await concat(data.htsget.urls.slice(1), opts);
yield* this._fetchChunkFeatures([
// fake stuff to pretend to be a Chunk
{
buffer: uncba,
_fetchedSize: undefined,
bin: 0,
compareTo() {
return 0;
},
], chrId, min, max, opts))));
}
});
toUniqueString() {
return `${chr}_${min}_${max}`;
},
fetchedSize() {
return 0;
},
minv: {
dataPosition: 0,
blockPosition: 0,
compareTo: () => 0,
},
maxv: {
dataPosition: Number.MAX_SAFE_INTEGER,
blockPosition: 0,
compareTo: () => 0,
},
toString() {
return `${chr}_${min}_${max}`;
},
},
], chrId, min, max, opts);
}
}
_readChunk(_a) {
return __awaiter(this, arguments, void 0, function* ({ chunk }) {
if (!chunk.buffer) {
throw new Error('expected chunk.buffer in htsget');
}
return { data: chunk.buffer, cpositions: [], dpositions: [], chunk };
});
// @ts-expect-error
async _readChunk({ chunk }) {
if (!chunk.buffer) {
throw new Error('expected chunk.buffer in htsget');
}
return {
data: chunk.buffer,
cpositions: [],
dpositions: [],
chunk,
};
}
getHeader() {
return __awaiter(this, arguments, void 0, function* (opts = {}) {
const url = `${this.baseUrl}/${this.trackId}?referenceName=na&class=header`;
const result = yield fetch(url, opts);
if (!result.ok) {
throw new Error(`HTTP ${result.status} fetching ${url}: ${yield result.text()}`);
}
const data = yield result.json();
const uncba = yield concat(data.htsget.urls, opts);
if (uncba.readInt32LE(0) !== bamFile_1.BAM_MAGIC) {
throw new Error('Not a BAM file');
}
const headLen = uncba.readInt32LE(4);
const headerText = uncba.toString('utf8', 8, 8 + headLen);
const samHeader = (0, sam_1.parseHeaderText)(headerText);
// use the @SQ lines in the header to figure out the
// mapping between ref ref ID numbers and names
const idToName = [];
const nameToId = {};
const sqLines = samHeader.filter(l => l.tag === 'SQ');
for (const [refId, sqLine] of sqLines.entries()) {
let refName = '';
let length = 0;
for (const item of sqLine.data) {
if (item.tag === 'SN') {
refName = item.value;
}
else if (item.tag === 'LN') {
length = +item.value;
}
async getHeader(opts = {}) {
const url = `${this.baseUrl}/${this.trackId}?referenceName=na&class=header`;
const result = await fetch(url, opts);
if (!result.ok) {
throw new Error(`HTTP ${result.status} fetching ${url}: ${await result.text()}`);
}
const data = await result.json();
const uncba = await concat(data.htsget.urls, opts);
const dataView = new DataView(uncba.buffer);
if (dataView.getInt32(0, true) !== bamFile_1.BAM_MAGIC) {
throw new Error('Not a BAM file');
}
const headLen = dataView.getInt32(4, true);
const decoder = new TextDecoder('utf8');
const headerText = decoder.decode(uncba.subarray(8, 8 + headLen));
const samHeader = (0, sam_1.parseHeaderText)(headerText);
// use the @SQ lines in the header to figure out the
// mapping between ref ref ID numbers and names
const idToName = [];
const nameToId = {};
const sqLines = samHeader.filter(l => l.tag === 'SQ');
for (const [refId, sqLine] of sqLines.entries()) {
let refName = '';
let length = 0;
for (const item of sqLine.data) {
if (item.tag === 'SN') {
refName = item.value;
}
nameToId[refName] = refId;
idToName[refId] = { refName, length };
else if (item.tag === 'LN') {
length = +item.value;
}
}
this.chrToIndex = nameToId;
this.indexToChr = idToName;
return samHeader;
});
nameToId[refName] = refId;
idToName[refId] = { refName, length };
}
this.chrToIndex = nameToId;
this.indexToChr = idToName;
return samHeader;
}

@@ -200,0 +170,0 @@ }

@@ -1,2 +0,2 @@

import { GenericFilehandle } from 'generic-filehandle';
import { GenericFilehandle } from 'generic-filehandle2';
import Chunk from './chunk';

@@ -3,0 +3,0 @@ import { BaseOpts } from './util';

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class IndexFile {
filehandle;
renameRefSeq;
/**

@@ -5,0 +7,0 @@ * @param {filehandle} filehandle

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -23,6 +14,4 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

}
indexCov() {
return __awaiter(this, void 0, void 0, function* () {
throw new Error('never called');
});
async indexCov() {
throw new Error('never called');
}

@@ -29,0 +18,0 @@ blocksForRange() {

@@ -1,8 +0,8 @@

import type { Buffer } from 'buffer';
interface Bytes {
start: number;
end: number;
byteArray: Buffer;
byteArray: Uint8Array;
}
export default class BamRecord {
#private;
fileOffset: number;

@@ -14,3 +14,3 @@ private bytes;

});
get byteArray(): Buffer;
get byteArray(): Uint8Array<ArrayBufferLike>;
get flags(): number;

@@ -23,3 +23,3 @@ get ref_id(): number;

get score(): number | undefined;
get qual(): Buffer | undefined;
get qual(): Uint8Array<ArrayBufferLike> | undefined;
get strand(): 1 | -1;

@@ -26,0 +26,0 @@ get b0(): number;

@@ -10,5 +10,9 @@ "use strict";

class BamRecord {
fileOffset;
bytes;
#dataView;
constructor(args) {
this.bytes = args.bytes;
this.fileOffset = args.fileOffset;
this.#dataView = new DataView(this.bytes.byteArray.buffer);
}

@@ -19,9 +23,9 @@ get byteArray() {

get flags() {
return ((this.byteArray.readInt32LE(this.bytes.start + 16) & 0xffff0000) >> 16);
return ((this.#dataView.getInt32(this.bytes.start + 16, true) & 0xffff0000) >> 16);
}
get ref_id() {
return this.byteArray.readInt32LE(this.bytes.start + 4);
return this.#dataView.getInt32(this.bytes.start + 4, true);
}
get start() {
return this.byteArray.readInt32LE(this.bytes.start + 8);
return this.#dataView.getInt32(this.bytes.start + 8, true);
}

@@ -58,6 +62,9 @@ get end() {

get name() {
return this.byteArray.toString('ascii', this.b0, this.b0 + this.read_name_length - 1);
let str = '';
for (let i = 0; i < this.read_name_length - 1; i++) {
str += String.fromCharCode(this.byteArray[this.b0 + i]);
}
return str;
}
get tags() {
const { byteArray } = this.bytes;
let p = this.b0 +

@@ -71,35 +78,35 @@ this.read_name_length +

while (p < blockEnd) {
const tag = String.fromCharCode(byteArray[p], byteArray[p + 1]);
const type = String.fromCharCode(byteArray[p + 2]);
const tag = String.fromCharCode(this.byteArray[p], this.byteArray[p + 1]);
const type = String.fromCharCode(this.byteArray[p + 2]);
p += 3;
if (type === 'A') {
tags[tag] = String.fromCharCode(byteArray[p]);
tags[tag] = String.fromCharCode(this.byteArray[p]);
p += 1;
}
else if (type === 'i') {
tags[tag] = byteArray.readInt32LE(p);
tags[tag] = this.#dataView.getInt32(p, true);
p += 4;
}
else if (type === 'I') {
tags[tag] = byteArray.readUInt32LE(p);
tags[tag] = this.#dataView.getUint32(p, true);
p += 4;
}
else if (type === 'c') {
tags[tag] = byteArray.readInt8(p);
tags[tag] = this.#dataView.getInt8(p);
p += 1;
}
else if (type === 'C') {
tags[tag] = byteArray.readUInt8(p);
tags[tag] = this.#dataView.getUint8(p);
p += 1;
}
else if (type === 's') {
tags[tag] = byteArray.readInt16LE(p);
tags[tag] = this.#dataView.getInt16(p, true);
p += 2;
}
else if (type === 'S') {
tags[tag] = byteArray.readUInt16LE(p);
tags[tag] = this.#dataView.getUint16(p, true);
p += 2;
}
else if (type === 'f') {
tags[tag] = byteArray.readFloatLE(p);
tags[tag] = this.#dataView.getFloat32(p, true);
p += 4;

@@ -110,3 +117,3 @@ }

while (p <= blockEnd) {
const cc = byteArray[p++];
const cc = this.byteArray[p++];
if (cc !== 0) {

@@ -122,5 +129,5 @@ value.push(String.fromCharCode(cc));

else if (type === 'B') {
const cc = byteArray[p++];
const cc = this.byteArray[p++];
const Btype = String.fromCharCode(cc);
const limit = byteArray.readInt32LE(p);
const limit = this.#dataView.getInt32(p, true);
p += 4;

@@ -131,3 +138,3 @@ if (Btype === 'i') {

for (let k = 0; k < limit; k++) {
const cigop = byteArray.readInt32LE(p);
const cigop = this.#dataView.getInt32(p, true);
const lop = cigop >> 4;

@@ -143,3 +150,3 @@ const op = CIGAR_DECODER[cigop & 0xf];

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt32LE(p));
value.push(this.#dataView.getInt32(p, true));
p += 4;

@@ -154,3 +161,3 @@ }

for (let k = 0; k < limit; k++) {
const cigop = byteArray.readUInt32LE(p);
const cigop = this.#dataView.getUint32(p, true);
const lop = cigop >> 4;

@@ -166,3 +173,3 @@ const op = CIGAR_DECODER[cigop & 0xf];

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt32LE(p));
value.push(this.#dataView.getUint32(p, true));
p += 4;

@@ -176,3 +183,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt16LE(p));
value.push(this.#dataView.getInt16(p, true));
p += 2;

@@ -185,3 +192,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt16LE(p));
value.push(this.#dataView.getUint16(p, true));
p += 2;

@@ -194,3 +201,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt8(p));
value.push(this.#dataView.getInt8(p));
p += 1;

@@ -203,3 +210,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt8(p));
value.push(this.#dataView.getUint8(p));
p += 1;

@@ -212,3 +219,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readFloatLE(p));
value.push(this.#dataView.getFloat32(p, true));
p += 4;

@@ -289,3 +296,3 @@ }

// that consumes entire seqLen
let cigop = this.byteArray.readInt32LE(p);
let cigop = this.#dataView.getInt32(p, true);
let lop = cigop >> 4;

@@ -297,3 +304,3 @@ let op = CIGAR_DECODER[cigop & 0xf];

p += 4;
cigop = this.byteArray.readInt32LE(p);
cigop = this.#dataView.getInt32(p, true);
lop = cigop >> 4;

@@ -312,3 +319,3 @@ op = CIGAR_DECODER[cigop & 0xf];

for (let c = 0; c < numCigarOps; ++c) {
cigop = this.byteArray.readInt32LE(p);
cigop = this.#dataView.getInt32(p, true);
lop = cigop >> 4;

@@ -399,18 +406,18 @@ op = CIGAR_DECODER[cigop & 0xf];

get bin_mq_nl() {
return this.byteArray.readInt32LE(this.bytes.start + 12);
return this.#dataView.getInt32(this.bytes.start + 12, true);
}
get flag_nc() {
return this.byteArray.readInt32LE(this.bytes.start + 16);
return this.#dataView.getInt32(this.bytes.start + 16, true);
}
get seq_length() {
return this.byteArray.readInt32LE(this.bytes.start + 20);
return this.#dataView.getInt32(this.bytes.start + 20, true);
}
get next_refid() {
return this.byteArray.readInt32LE(this.bytes.start + 24);
return this.#dataView.getInt32(this.bytes.start + 24, true);
}
get next_pos() {
return this.byteArray.readInt32LE(this.bytes.start + 28);
return this.#dataView.getInt32(this.bytes.start + 28, true);
}
get template_length() {
return this.byteArray.readInt32LE(this.bytes.start + 32);
return this.#dataView.getInt32(this.bytes.start + 32, true);
}

@@ -417,0 +424,0 @@ toJSON() {

@@ -35,9 +35,11 @@ import Chunk from './chunk';

export declare function optimizeChunks(chunks: Chunk[], lowest?: VirtualOffset): Chunk[];
export declare function parsePseudoBin(bytes: Buffer, offset: number): {
export declare function parsePseudoBin(bytes: Uint8Array, offset: number): {
lineCount: number;
};
export declare function findFirstData(firstDataLine: VirtualOffset | undefined, virtualOffset: VirtualOffset): VirtualOffset;
export declare function parseNameBytes(namesBytes: Buffer, renameRefSeq?: (arg: string) => string): {
export declare function parseNameBytes(namesBytes: Uint8Array, renameRefSeq?: (arg: string) => string): {
refNameToId: Record<string, number>;
refIdToName: string[];
};
export declare function sum(array: Uint8Array[]): number;
export declare function concatUint8Array(args: Uint8Array[]): Uint8Array<ArrayBuffer>;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -24,2 +15,4 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

exports.parseNameBytes = parseNameBytes;
exports.sum = sum;
exports.concatUint8Array = concatUint8Array;
const long_1 = __importDefault(require("long"));

@@ -63,7 +56,5 @@ function timeout(ms) {

*/
function abortBreakPoint(signal) {
return __awaiter(this, void 0, void 0, function* () {
yield Promise.resolve();
checkAbortSignal(signal);
});
async function abortBreakPoint(signal) {
await Promise.resolve();
checkAbortSignal(signal);
}

@@ -128,3 +119,6 @@ function canMergeBlocks(chunk1, chunk2) {

if (currNameStart < i) {
let refName = namesBytes.toString('utf8', currNameStart, i);
let refName = '';
for (let j = currNameStart; j < i; j++) {
refName += String.fromCharCode(namesBytes[j]);
}
refName = renameRefSeq(refName);

@@ -140,2 +134,18 @@ refIdToName[currRefId] = refName;

}
function sum(array) {
let sum = 0;
for (const entry of array) {
sum += entry.length;
}
return sum;
}
function concatUint8Array(args) {
const mergedArray = new Uint8Array(sum(args));
let offset = 0;
for (const entry of args) {
mergedArray.set(entry, offset);
offset += entry.length;
}
return mergedArray;
}
//# sourceMappingURL=util.js.map

@@ -9,2 +9,2 @@ export default class VirtualOffset {

}
export declare function fromBytes(bytes: Buffer, offset?: number, bigendian?: boolean): VirtualOffset;
export declare function fromBytes(bytes: Uint8Array, offset?: number, bigendian?: boolean): VirtualOffset;

@@ -5,2 +5,4 @@ "use strict";

class VirtualOffset {
blockPosition;
dataPosition;
constructor(blockPosition, dataPosition) {

@@ -7,0 +9,0 @@ this.blockPosition = blockPosition; // < offset of the compressed data block

@@ -8,3 +8,3 @@ import VirtualOffset from './virtualOffset';

lineCount(refId: number, opts?: BaseOpts): Promise<number>;
_parse(opts?: BaseOpts): Promise<{
_parse(_opts?: BaseOpts): Promise<{
bai: boolean;

@@ -11,0 +11,0 @@ firstDataLine: VirtualOffset | undefined;

@@ -24,15 +24,16 @@ import { fromBytes } from './virtualOffset';

export default class BAI extends IndexFile {
setupP;
async lineCount(refId, opts) {
var _a, _b;
const indexData = await this.parse(opts);
return ((_b = (_a = indexData.indices[refId]) === null || _a === void 0 ? void 0 : _a.stats) === null || _b === void 0 ? void 0 : _b.lineCount) || 0;
return indexData.indices[refId]?.stats?.lineCount || 0;
}
// fetch and parse the index
async _parse(opts) {
const bytes = (await this.filehandle.readFile(opts));
async _parse(_opts) {
const bytes = await this.filehandle.readFile();
const dataView = new DataView(bytes.buffer);
// check BAI magic numbers
if (bytes.readUInt32LE(0) !== BAI_MAGIC) {
if (dataView.getUint32(0, true) !== BAI_MAGIC) {
throw new Error('Not a BAI file');
}
const refCount = bytes.readInt32LE(4);
const refCount = dataView.getInt32(4, true);
const depth = 5;

@@ -46,3 +47,3 @@ const binLimit = ((1 << ((depth + 1) * 3)) - 1) / 7;

// the binning index
const binCount = bytes.readInt32LE(curr);
const binCount = dataView.getInt32(curr, true);
let stats;

@@ -52,3 +53,3 @@ curr += 4;

for (let j = 0; j < binCount; j += 1) {
const bin = bytes.readUInt32LE(curr);
const bin = dataView.getUint32(curr, true);
curr += 4;

@@ -64,3 +65,3 @@ if (bin === binLimit + 1) {

else {
const chunkCount = bytes.readInt32LE(curr);
const chunkCount = dataView.getInt32(curr, true);
curr += 4;

@@ -79,3 +80,3 @@ const chunks = new Array(chunkCount);

}
const linearCount = bytes.readInt32LE(curr);
const linearCount = dataView.getInt32(curr, true);
curr += 4;

@@ -135,3 +136,3 @@ // as we're going through the linear index, figure out the smallest

...d,
score: (d.score * ((stats === null || stats === void 0 ? void 0 : stats.lineCount) || 0)) / totalSize,
score: (d.score * (stats?.lineCount || 0)) / totalSize,
}));

@@ -193,7 +194,6 @@ }

async hasRefSeq(seqId, opts = {}) {
var _a;
const header = await this.parse(opts);
return !!((_a = header.indices[seqId]) === null || _a === void 0 ? void 0 : _a.binIndex);
return !!header.indices[seqId]?.binIndex;
}
}
//# sourceMappingURL=bai.js.map

@@ -1,3 +0,2 @@

import { Buffer } from 'buffer';
import { GenericFilehandle } from 'generic-filehandle';
import { GenericFilehandle } from 'generic-filehandle2';
import BAI from './bai';

@@ -63,3 +62,3 @@ import CSI from './csi';

fetchPairs(chrId: number, feats: BAMFeature[][], opts: BamOpts): Promise<BAMFeature[]>;
_readRegion(position: number, size: number, opts?: BaseOpts): Promise<Buffer>;
_readRegion(position: number, size: number, opts?: BaseOpts): Promise<Uint8Array<ArrayBuffer>>;
_readChunk({ chunk, opts }: {

@@ -69,3 +68,3 @@ chunk: Chunk;

}): Promise<{
data: Buffer;
data: Uint8Array<ArrayBuffer>;
cpositions: number[];

@@ -75,3 +74,3 @@ dpositions: number[];

}>;
readBamFeatures(ba: Buffer, cpositions: number[], dpositions: number[], chunk: Chunk): Promise<BAMFeature[]>;
readBamFeatures(ba: Uint8Array, cpositions: number[], dpositions: number[], chunk: Chunk): Promise<BAMFeature[]>;
hasRefSeq(seqName: string): Promise<boolean | undefined>;

@@ -78,0 +77,0 @@ lineCount(seqName: string): Promise<number>;

@@ -1,5 +0,4 @@

import { Buffer } from 'buffer';
import crc32 from 'crc/crc32';
import { unzip, unzipChunkSlice } from '@gmod/bgzf-filehandle';
import { LocalFile, RemoteFile } from 'generic-filehandle';
import { LocalFile, RemoteFile } from 'generic-filehandle2';
import AbortablePromiseCache from '@gmod/abortable-promise-cache';

@@ -37,17 +36,25 @@ import QuickLRU from 'quick-lru';

export default class BamFile {
renameRefSeq;
bam;
header;
chrToIndex;
indexToChr;
yieldThreadTime;
index;
htsget = false;
headerP;
featureCache = new AbortablePromiseCache({
cache: new QuickLRU({
maxSize: 50,
}),
fill: async (args, signal) => {
const { chunk, opts } = args;
const { data, cpositions, dpositions } = await this._readChunk({
chunk,
opts: { ...opts, signal },
});
return this.readBamFeatures(data, cpositions, dpositions, chunk);
},
});
constructor({ bamFilehandle, bamPath, bamUrl, baiPath, baiFilehandle, baiUrl, csiPath, csiFilehandle, csiUrl, htsget, yieldThreadTime = 100, renameRefSeqs = n => n, }) {
this.htsget = false;
this.featureCache = new AbortablePromiseCache({
cache: new QuickLRU({
maxSize: 50,
}),
fill: async (args, signal) => {
const { chunk, opts } = args;
const { data, cpositions, dpositions } = await this._readChunk({
chunk,
opts: { ...opts, signal },
});
return this.readBamFeatures(data, cpositions, dpositions, chunk);
},
});
this.renameRefSeq = renameRefSeqs;

@@ -114,7 +121,3 @@ if (bamFilehandle) {

const s = ret + blockLen;
const res = await this.bam.read(Buffer.alloc(s), 0, s, 0, opts);
if (!res.bytesRead) {
throw new Error('Error reading header');
}
buffer = res.buffer.subarray(0, Math.min(res.bytesRead, ret));
buffer = await this.bam.read(s, 0);
}

@@ -125,7 +128,9 @@ else {

const uncba = await unzip(buffer);
if (uncba.readInt32LE(0) !== BAM_MAGIC) {
const dataView = new DataView(uncba.buffer);
if (dataView.getInt32(0, true) !== BAM_MAGIC) {
throw new Error('Not a BAM file');
}
const headLen = uncba.readInt32LE(4);
this.header = uncba.toString('utf8', 8, 8 + headLen);
const headLen = dataView.getInt32(4, true);
const decoder = new TextDecoder('utf8');
this.header = decoder.decode(uncba.subarray(8, 8 + headLen));
const { chrToIndex, indexToChr } = await this._readRefSeqs(headLen + 8, 65535, opts);

@@ -155,16 +160,15 @@ this.chrToIndex = chrToIndex;

}
const size = refSeqBytes + blockLen;
const { bytesRead, buffer } = await this.bam.read(Buffer.alloc(size), 0, refSeqBytes, 0, opts);
if (!bytesRead) {
throw new Error('Error reading refseqs from header');
}
const uncba = await unzip(buffer.subarray(0, Math.min(bytesRead, refSeqBytes)));
const nRef = uncba.readInt32LE(start);
// const size = refSeqBytes + blockLen <-- use this?
const buffer = await this.bam.read(refSeqBytes, 0, opts);
const uncba = await unzip(buffer);
const dataView = new DataView(uncba.buffer);
const nRef = dataView.getInt32(start, true);
let p = start + 4;
const chrToIndex = {};
const indexToChr = [];
const decoder = new TextDecoder('utf8');
for (let i = 0; i < nRef; i += 1) {
const lName = uncba.readInt32LE(p);
const refName = this.renameRefSeq(uncba.toString('utf8', p + 4, p + 4 + lName - 1));
const lRef = uncba.readInt32LE(p + lName + 4);
const lName = dataView.getInt32(p, true);
const refName = this.renameRefSeq(decoder.decode(uncba.subarray(p + 4, p + 4 + lName - 1)));
const lRef = dataView.getInt32(p + lName + 4, true);
chrToIndex[refName] = i;

@@ -184,5 +188,4 @@ indexToChr.push({ refName, length: lRef });

async *streamRecordsForRange(chr, min, max, opts) {
var _a;
await this.getHeader(opts);
const chrId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[chr];
const chrId = this.chrToIndex?.[chr];
if (chrId === undefined || !this.index) {

@@ -288,4 +291,3 @@ yield [];

async _readRegion(position, size, opts = {}) {
const { bytesRead, buffer } = await this.bam.read(Buffer.alloc(size), 0, size, position, opts);
return buffer.subarray(0, Math.min(bytesRead, size));
return this.bam.read(size, position, opts);
}

@@ -302,4 +304,5 @@ async _readChunk({ chunk, opts }) {

let last = +Date.now();
const dataView = new DataView(ba.buffer);
while (blockStart + 4 < ba.length) {
const blockSize = ba.readInt32LE(blockStart);
const blockSize = dataView.getInt32(blockStart, true);
const blockEnd = blockStart + 4 + blockSize - 1;

@@ -346,4 +349,4 @@ // increment position to the current decompressed status

: // must be slice, not subarray for buffer polyfill on web
// eslint-disable-next-line @typescript-eslint/no-deprecated
crc32.signed(ba.slice(blockStart, blockEnd)),
// @ts-expect-error
crc32.signed(ba.subarray(blockStart, blockEnd)),
});

@@ -361,13 +364,10 @@ sink.push(feature);

async hasRefSeq(seqName) {
var _a, _b;
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
return seqId === undefined ? false : (_b = this.index) === null || _b === void 0 ? void 0 : _b.hasRefSeq(seqId);
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined ? false : this.index?.hasRefSeq(seqId);
}
async lineCount(seqName) {
var _a;
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined || !this.index ? 0 : this.index.lineCount(seqId);
}
async indexCov(seqName, start, end) {
var _a;
if (!this.index) {

@@ -377,7 +377,6 @@ return [];

await this.index.parse();
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined ? [] : this.index.indexCov(seqId, start, end);
}
async blocksForRange(seqName, start, end, opts) {
var _a;
if (!this.index) {

@@ -387,3 +386,3 @@ return [];

await this.index.parse();
const seqId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[seqName];
const seqId = this.chrToIndex?.[seqName];
return seqId === undefined

@@ -390,0 +389,0 @@ ? []

@@ -7,3 +7,3 @@ import VirtualOffset from './virtualOffset';

_fetchedSize?: number | undefined;
buffer?: Buffer;
buffer?: Uint8Array;
constructor(minv: VirtualOffset, maxv: VirtualOffset, bin: number, _fetchedSize?: number | undefined);

@@ -10,0 +10,0 @@ toUniqueString(): string;

// little class representing a chunk in the index
export default class Chunk {
minv;
maxv;
bin;
_fetchedSize;
buffer;
constructor(minv, maxv, bin, _fetchedSize) {

@@ -4,0 +9,0 @@ this.minv = minv;

@@ -12,3 +12,3 @@ import VirtualOffset from './virtualOffset';

indexCov(): Promise<never[]>;
parseAuxData(bytes: Buffer, offset: number): {
parseAuxData(bytes: Uint8Array, offset: number): {
refNameToId: Record<string, number>;

@@ -15,0 +15,0 @@ refIdToName: string[];

@@ -15,12 +15,9 @@ import { unzip } from '@gmod/bgzf-filehandle';

export default class CSI extends IndexFile {
constructor() {
super(...arguments);
this.maxBinNumber = 0;
this.depth = 0;
this.minShift = 0;
}
maxBinNumber = 0;
depth = 0;
minShift = 0;
setupP;
async lineCount(refId, opts) {
var _a, _b;
const indexData = await this.parse(opts);
return ((_b = (_a = indexData.indices[refId]) === null || _a === void 0 ? void 0 : _a.stats) === null || _b === void 0 ? void 0 : _b.lineCount) || 0;
return indexData.indices[refId]?.stats?.lineCount || 0;
}

@@ -31,3 +28,4 @@ async indexCov() {

parseAuxData(bytes, offset) {
const formatFlags = bytes.readInt32LE(offset);
const dataView = new DataView(bytes.buffer);
const formatFlags = dataView.getUint32(offset, true);
const coordinateType = formatFlags & 0x10000 ? 'zero-based-half-open' : '1-based-closed';

@@ -39,10 +37,10 @@ const format = { 0: 'generic', 1: 'SAM', 2: 'VCF' }[formatFlags & 0xf];

const columnNumbers = {
ref: bytes.readInt32LE(offset + 4),
start: bytes.readInt32LE(offset + 8),
end: bytes.readInt32LE(offset + 12),
ref: dataView.getInt32(offset + 4, true),
start: dataView.getInt32(offset + 8, true),
end: dataView.getInt32(offset + 12, true),
};
const metaValue = bytes.readInt32LE(offset + 16);
const metaValue = dataView.getInt32(offset + 16, true);
const metaChar = metaValue ? String.fromCharCode(metaValue) : '';
const skipLines = bytes.readInt32LE(offset + 20);
const nameSectionLength = bytes.readInt32LE(offset + 24);
const skipLines = dataView.getInt32(offset + 20, true);
const nameSectionLength = dataView.getInt32(offset + 24, true);
return {

@@ -63,20 +61,21 @@ columnNumbers,

const bytes = await unzip(buffer);
const dataView = new DataView(bytes.buffer);
let csiVersion;
// check TBI magic numbers
if (bytes.readUInt32LE(0) === CSI1_MAGIC) {
const magic = dataView.getUint32(0, true);
if (magic === CSI1_MAGIC) {
csiVersion = 1;
}
else if (bytes.readUInt32LE(0) === CSI2_MAGIC) {
else if (magic === CSI2_MAGIC) {
csiVersion = 2;
}
else {
throw new Error('Not a CSI file');
throw new Error(`Not a CSI file ${magic}`);
// TODO: do we need to support big-endian CSI files?
}
this.minShift = bytes.readInt32LE(4);
this.depth = bytes.readInt32LE(8);
this.minShift = dataView.getInt32(4, true);
this.depth = dataView.getInt32(8, true);
this.maxBinNumber = ((1 << ((this.depth + 1) * 3)) - 1) / 7;
const auxLength = bytes.readInt32LE(12);
const auxLength = dataView.getInt32(12, true);
const aux = auxLength >= 30 ? this.parseAuxData(bytes, 16) : undefined;
const refCount = bytes.readInt32LE(16 + auxLength);
const refCount = dataView.getInt32(16 + auxLength, true);
// read the indexes for each reference sequence

@@ -88,3 +87,3 @@ let curr = 16 + auxLength + 4;

// the binning index
const binCount = bytes.readInt32LE(curr);
const binCount = dataView.getInt32(curr, true);
curr += 4;

@@ -94,3 +93,3 @@ const binIndex = {};

for (let j = 0; j < binCount; j++) {
const bin = bytes.readUInt32LE(curr);
const bin = dataView.getUint32(curr, true);
curr += 4;

@@ -104,3 +103,3 @@ if (bin > this.maxBinNumber) {

curr += 8;
const chunkCount = bytes.readInt32LE(curr);
const chunkCount = dataView.getInt32(curr, true);
curr += 4;

@@ -197,7 +196,6 @@ const chunks = new Array(chunkCount);

async hasRefSeq(seqId, opts = {}) {
var _a;
const header = await this.parse(opts);
return !!((_a = header.indices[seqId]) === null || _a === void 0 ? void 0 : _a.binIndex);
return !!header.indices[seqId]?.binIndex;
}
}
//# sourceMappingURL=csi.js.map

@@ -1,2 +0,1 @@

import { Buffer } from 'buffer';
import { BaseOpts, BamOpts } from './util';

@@ -17,3 +16,3 @@ import BamFile from './bamFile';

}): Promise<{
data: Buffer;
data: Uint8Array<ArrayBufferLike>;
cpositions: never[];

@@ -20,0 +19,0 @@ dpositions: never[];

import { unzip } from '@gmod/bgzf-filehandle';
import { Buffer } from 'buffer';
import { concatUint8Array } from './util';
import BamFile, { BAM_MAGIC } from './bamFile';

@@ -9,3 +9,4 @@ import { parseHeaderText } from './sam';

if (url.startsWith('data:')) {
return Buffer.from(url.split(',')[1], 'base64');
// @ts-expect-error
return Uint8Array.fromBase64(url.split(',')[1], 'base64');
}

@@ -18,3 +19,3 @@ else {

...opts,
headers: { ...opts === null || opts === void 0 ? void 0 : opts.headers, ...rest },
headers: { ...opts?.headers, ...rest },
});

@@ -24,8 +25,10 @@ if (!res.ok) {

}
return Buffer.from(await res.arrayBuffer());
return new Uint8Array(await res.arrayBuffer());
}
}));
return Buffer.concat(await Promise.all(res.map(elt => unzip(elt))));
return concatUint8Array(await Promise.all(res.map(elt => unzip(elt))));
}
export default class HtsgetFile extends BamFile {
baseUrl;
trackId;
constructor(args) {

@@ -37,6 +40,5 @@ super({ htsget: true });

async *streamRecordsForRange(chr, min, max, opts) {
var _a;
const base = `${this.baseUrl}/${this.trackId}`;
const url = `${base}?referenceName=${chr}&start=${min}&end=${max}&format=BAM`;
const chrId = (_a = this.chrToIndex) === null || _a === void 0 ? void 0 : _a[chr];
const chrId = this.chrToIndex?.[chr];
if (chrId === undefined) {

@@ -84,2 +86,3 @@ yield [];

}
// @ts-expect-error
async _readChunk({ chunk }) {

@@ -89,3 +92,8 @@ if (!chunk.buffer) {

}
return { data: chunk.buffer, cpositions: [], dpositions: [], chunk };
return {
data: chunk.buffer,
cpositions: [],
dpositions: [],
chunk,
};
}

@@ -100,7 +108,9 @@ async getHeader(opts = {}) {

const uncba = await concat(data.htsget.urls, opts);
if (uncba.readInt32LE(0) !== BAM_MAGIC) {
const dataView = new DataView(uncba.buffer);
if (dataView.getInt32(0, true) !== BAM_MAGIC) {
throw new Error('Not a BAM file');
}
const headLen = uncba.readInt32LE(4);
const headerText = uncba.toString('utf8', 8, 8 + headLen);
const headLen = dataView.getInt32(4, true);
const decoder = new TextDecoder('utf8');
const headerText = decoder.decode(uncba.subarray(8, 8 + headLen));
const samHeader = parseHeaderText(headerText);

@@ -107,0 +117,0 @@ // use the @SQ lines in the header to figure out the

@@ -1,2 +0,2 @@

import { GenericFilehandle } from 'generic-filehandle';
import { GenericFilehandle } from 'generic-filehandle2';
import Chunk from './chunk';

@@ -3,0 +3,0 @@ import { BaseOpts } from './util';

export default class IndexFile {
filehandle;
renameRefSeq;
/**

@@ -3,0 +5,0 @@ * @param {filehandle} filehandle

@@ -1,8 +0,8 @@

import type { Buffer } from 'buffer';
interface Bytes {
start: number;
end: number;
byteArray: Buffer;
byteArray: Uint8Array;
}
export default class BamRecord {
#private;
fileOffset: number;

@@ -14,3 +14,3 @@ private bytes;

});
get byteArray(): Buffer;
get byteArray(): Uint8Array<ArrayBufferLike>;
get flags(): number;

@@ -23,3 +23,3 @@ get ref_id(): number;

get score(): number | undefined;
get qual(): Buffer | undefined;
get qual(): Uint8Array<ArrayBufferLike> | undefined;
get strand(): 1 | -1;

@@ -26,0 +26,0 @@ get b0(): number;

@@ -5,5 +5,9 @@ import Constants from './constants';

export default class BamRecord {
fileOffset;
bytes;
#dataView;
constructor(args) {
this.bytes = args.bytes;
this.fileOffset = args.fileOffset;
this.#dataView = new DataView(this.bytes.byteArray.buffer);
}

@@ -14,9 +18,9 @@ get byteArray() {

get flags() {
return ((this.byteArray.readInt32LE(this.bytes.start + 16) & 0xffff0000) >> 16);
return ((this.#dataView.getInt32(this.bytes.start + 16, true) & 0xffff0000) >> 16);
}
get ref_id() {
return this.byteArray.readInt32LE(this.bytes.start + 4);
return this.#dataView.getInt32(this.bytes.start + 4, true);
}
get start() {
return this.byteArray.readInt32LE(this.bytes.start + 8);
return this.#dataView.getInt32(this.bytes.start + 8, true);
}

@@ -53,6 +57,9 @@ get end() {

get name() {
return this.byteArray.toString('ascii', this.b0, this.b0 + this.read_name_length - 1);
let str = '';
for (let i = 0; i < this.read_name_length - 1; i++) {
str += String.fromCharCode(this.byteArray[this.b0 + i]);
}
return str;
}
get tags() {
const { byteArray } = this.bytes;
let p = this.b0 +

@@ -66,35 +73,35 @@ this.read_name_length +

while (p < blockEnd) {
const tag = String.fromCharCode(byteArray[p], byteArray[p + 1]);
const type = String.fromCharCode(byteArray[p + 2]);
const tag = String.fromCharCode(this.byteArray[p], this.byteArray[p + 1]);
const type = String.fromCharCode(this.byteArray[p + 2]);
p += 3;
if (type === 'A') {
tags[tag] = String.fromCharCode(byteArray[p]);
tags[tag] = String.fromCharCode(this.byteArray[p]);
p += 1;
}
else if (type === 'i') {
tags[tag] = byteArray.readInt32LE(p);
tags[tag] = this.#dataView.getInt32(p, true);
p += 4;
}
else if (type === 'I') {
tags[tag] = byteArray.readUInt32LE(p);
tags[tag] = this.#dataView.getUint32(p, true);
p += 4;
}
else if (type === 'c') {
tags[tag] = byteArray.readInt8(p);
tags[tag] = this.#dataView.getInt8(p);
p += 1;
}
else if (type === 'C') {
tags[tag] = byteArray.readUInt8(p);
tags[tag] = this.#dataView.getUint8(p);
p += 1;
}
else if (type === 's') {
tags[tag] = byteArray.readInt16LE(p);
tags[tag] = this.#dataView.getInt16(p, true);
p += 2;
}
else if (type === 'S') {
tags[tag] = byteArray.readUInt16LE(p);
tags[tag] = this.#dataView.getUint16(p, true);
p += 2;
}
else if (type === 'f') {
tags[tag] = byteArray.readFloatLE(p);
tags[tag] = this.#dataView.getFloat32(p, true);
p += 4;

@@ -105,3 +112,3 @@ }

while (p <= blockEnd) {
const cc = byteArray[p++];
const cc = this.byteArray[p++];
if (cc !== 0) {

@@ -117,5 +124,5 @@ value.push(String.fromCharCode(cc));

else if (type === 'B') {
const cc = byteArray[p++];
const cc = this.byteArray[p++];
const Btype = String.fromCharCode(cc);
const limit = byteArray.readInt32LE(p);
const limit = this.#dataView.getInt32(p, true);
p += 4;

@@ -126,3 +133,3 @@ if (Btype === 'i') {

for (let k = 0; k < limit; k++) {
const cigop = byteArray.readInt32LE(p);
const cigop = this.#dataView.getInt32(p, true);
const lop = cigop >> 4;

@@ -138,3 +145,3 @@ const op = CIGAR_DECODER[cigop & 0xf];

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt32LE(p));
value.push(this.#dataView.getInt32(p, true));
p += 4;

@@ -149,3 +156,3 @@ }

for (let k = 0; k < limit; k++) {
const cigop = byteArray.readUInt32LE(p);
const cigop = this.#dataView.getUint32(p, true);
const lop = cigop >> 4;

@@ -161,3 +168,3 @@ const op = CIGAR_DECODER[cigop & 0xf];

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt32LE(p));
value.push(this.#dataView.getUint32(p, true));
p += 4;

@@ -171,3 +178,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt16LE(p));
value.push(this.#dataView.getInt16(p, true));
p += 2;

@@ -180,3 +187,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt16LE(p));
value.push(this.#dataView.getUint16(p, true));
p += 2;

@@ -189,3 +196,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt8(p));
value.push(this.#dataView.getInt8(p));
p += 1;

@@ -198,3 +205,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt8(p));
value.push(this.#dataView.getUint8(p));
p += 1;

@@ -207,3 +214,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readFloatLE(p));
value.push(this.#dataView.getFloat32(p, true));
p += 4;

@@ -284,3 +291,3 @@ }

// that consumes entire seqLen
let cigop = this.byteArray.readInt32LE(p);
let cigop = this.#dataView.getInt32(p, true);
let lop = cigop >> 4;

@@ -292,3 +299,3 @@ let op = CIGAR_DECODER[cigop & 0xf];

p += 4;
cigop = this.byteArray.readInt32LE(p);
cigop = this.#dataView.getInt32(p, true);
lop = cigop >> 4;

@@ -307,3 +314,3 @@ op = CIGAR_DECODER[cigop & 0xf];

for (let c = 0; c < numCigarOps; ++c) {
cigop = this.byteArray.readInt32LE(p);
cigop = this.#dataView.getInt32(p, true);
lop = cigop >> 4;

@@ -394,18 +401,18 @@ op = CIGAR_DECODER[cigop & 0xf];

get bin_mq_nl() {
return this.byteArray.readInt32LE(this.bytes.start + 12);
return this.#dataView.getInt32(this.bytes.start + 12, true);
}
get flag_nc() {
return this.byteArray.readInt32LE(this.bytes.start + 16);
return this.#dataView.getInt32(this.bytes.start + 16, true);
}
get seq_length() {
return this.byteArray.readInt32LE(this.bytes.start + 20);
return this.#dataView.getInt32(this.bytes.start + 20, true);
}
get next_refid() {
return this.byteArray.readInt32LE(this.bytes.start + 24);
return this.#dataView.getInt32(this.bytes.start + 24, true);
}
get next_pos() {
return this.byteArray.readInt32LE(this.bytes.start + 28);
return this.#dataView.getInt32(this.bytes.start + 28, true);
}
get template_length() {
return this.byteArray.readInt32LE(this.bytes.start + 32);
return this.#dataView.getInt32(this.bytes.start + 32, true);
}

@@ -412,0 +419,0 @@ toJSON() {

@@ -35,9 +35,11 @@ import Chunk from './chunk';

export declare function optimizeChunks(chunks: Chunk[], lowest?: VirtualOffset): Chunk[];
export declare function parsePseudoBin(bytes: Buffer, offset: number): {
export declare function parsePseudoBin(bytes: Uint8Array, offset: number): {
lineCount: number;
};
export declare function findFirstData(firstDataLine: VirtualOffset | undefined, virtualOffset: VirtualOffset): VirtualOffset;
export declare function parseNameBytes(namesBytes: Buffer, renameRefSeq?: (arg: string) => string): {
export declare function parseNameBytes(namesBytes: Uint8Array, renameRefSeq?: (arg: string) => string): {
refNameToId: Record<string, number>;
refIdToName: string[];
};
export declare function sum(array: Uint8Array[]): number;
export declare function concatUint8Array(args: Uint8Array[]): Uint8Array<ArrayBuffer>;

@@ -101,3 +101,6 @@ import Long from 'long';

if (currNameStart < i) {
let refName = namesBytes.toString('utf8', currNameStart, i);
let refName = '';
for (let j = currNameStart; j < i; j++) {
refName += String.fromCharCode(namesBytes[j]);
}
refName = renameRefSeq(refName);

@@ -113,2 +116,18 @@ refIdToName[currRefId] = refName;

}
export function sum(array) {
let sum = 0;
for (const entry of array) {
sum += entry.length;
}
return sum;
}
export function concatUint8Array(args) {
const mergedArray = new Uint8Array(sum(args));
let offset = 0;
for (const entry of args) {
mergedArray.set(entry, offset);
offset += entry.length;
}
return mergedArray;
}
//# sourceMappingURL=util.js.map

@@ -9,2 +9,2 @@ export default class VirtualOffset {

}
export declare function fromBytes(bytes: Buffer, offset?: number, bigendian?: boolean): VirtualOffset;
export declare function fromBytes(bytes: Uint8Array, offset?: number, bigendian?: boolean): VirtualOffset;
export default class VirtualOffset {
blockPosition;
dataPosition;
constructor(blockPosition, dataPosition) {

@@ -3,0 +5,0 @@ this.blockPosition = blockPosition; // < offset of the compressed data block

{
"name": "@gmod/bam",
"version": "4.0.1",
"version": "5.0.0",
"description": "Parser for BAM and BAM index (bai) files",

@@ -26,4 +26,4 @@ "license": "MIT",

"clean": "rimraf dist esm",
"build:esm": "tsc --target es2018 --outDir esm",
"build:es5": "tsc --target es2015 --module commonjs --outDir dist",
"build:esm": "tsc --outDir esm",
"build:es5": "tsc --module commonjs --outDir dist",
"build": "npm run build:esm && npm run build:es5",

@@ -43,6 +43,5 @@ "prebuild": "npm run clean && npm run lint",

"@gmod/abortable-promise-cache": "^2.0.0",
"@gmod/bgzf-filehandle": "^1.4.4",
"buffer": "^6.0.3",
"@gmod/bgzf-filehandle": "^2.0.1",
"crc": "^4.3.2",
"generic-filehandle": "^3.0.0",
"generic-filehandle2": "^0.0.2",
"long": "^4.0.0",

@@ -57,2 +56,3 @@ "quick-lru": "^4.0.0"

"@vitest/coverage-v8": "^2.0.5",
"buffer": "^6.0.3",
"eslint": "^9.9.0",

@@ -59,0 +59,0 @@ "eslint-config-prettier": "^9.1.0",

@@ -29,6 +29,6 @@ [![NPM version](https://img.shields.io/npm/v/@gmod/bam.svg?style=flat-square)](https://npmjs.org/package/@gmod/bam)

The `bamPath` argument only works on nodejs. In the browser, you should pass
`bamFilehandle` with a generic-filehandle e.g. `RemoteFile`
`bamFilehandle` with a generic-filehandle2 e.g. `RemoteFile`
```typescript
const { RemoteFile } = require('generic-filehandle')
const { RemoteFile } = require('generic-filehandle2')
const bam = new BamFile({

@@ -80,6 +80,7 @@ bamFilehandle: new RemoteFile('yourfile.bam'), // or a full http url

Note: filehandles implement the Filehandle interface from
https://www.npmjs.com/package/generic-filehandle. This module offers the path
and url arguments as convenience methods for supplying the LocalFile and
RemoteFile
https://www.npmjs.com/package/generic-filehandle2.
This module offers the path and url arguments as convenience methods for
supplying the LocalFile and RemoteFile
### async getRecordsForRange(refName, start, end, opts)

@@ -117,3 +118,3 @@

### async getHeader(opts: {....anything to pass to generic-filehandle opts})
### async getHeader(opts: {....anything to pass to generic-filehandle2 opts})

@@ -163,9 +164,4 @@ This obtains the header from `HtsgetFile` or `BamFile`. Retrieves BAM file and

#### Note
The reason that we hide the data behind this ".get" function is that we lazily
decode records on demand, which can reduce memory consumption.
## License
MIT © [Colin Diesh](https://github.com/cmdcolin)

@@ -37,11 +37,12 @@ import VirtualOffset, { fromBytes } from './virtualOffset'

// fetch and parse the index
async _parse(opts?: BaseOpts) {
const bytes = (await this.filehandle.readFile(opts)) as Buffer
async _parse(_opts?: BaseOpts) {
const bytes = await this.filehandle.readFile()
const dataView = new DataView(bytes.buffer)
// check BAI magic numbers
if (bytes.readUInt32LE(0) !== BAI_MAGIC) {
if (dataView.getUint32(0, true) !== BAI_MAGIC) {
throw new Error('Not a BAI file')
}
const refCount = bytes.readInt32LE(4)
const refCount = dataView.getInt32(4, true)
const depth = 5

@@ -61,5 +62,7 @@ const binLimit = ((1 << ((depth + 1) * 3)) - 1) / 7

}>(refCount)
for (let i = 0; i < refCount; i++) {
// the binning index
const binCount = bytes.readInt32LE(curr)
const binCount = dataView.getInt32(curr, true)
let stats

@@ -71,3 +74,3 @@

for (let j = 0; j < binCount; j += 1) {
const bin = bytes.readUInt32LE(curr)
const bin = dataView.getUint32(curr, true)
curr += 4

@@ -81,3 +84,3 @@ if (bin === binLimit + 1) {

} else {
const chunkCount = bytes.readInt32LE(curr)
const chunkCount = dataView.getInt32(curr, true)
curr += 4

@@ -97,3 +100,3 @@ const chunks = new Array<Chunk>(chunkCount)

const linearCount = bytes.readInt32LE(curr)
const linearCount = dataView.getInt32(curr, true)
curr += 4

@@ -100,0 +103,0 @@ // as we're going through the linear index, figure out the smallest

@@ -1,5 +0,4 @@

import { Buffer } from 'buffer'
import crc32 from 'crc/crc32'
import { unzip, unzipChunkSlice } from '@gmod/bgzf-filehandle'
import { LocalFile, RemoteFile, GenericFilehandle } from 'generic-filehandle'
import { LocalFile, RemoteFile, GenericFilehandle } from 'generic-filehandle2'
import AbortablePromiseCache from '@gmod/abortable-promise-cache'

@@ -151,7 +150,3 @@ import QuickLRU from 'quick-lru'

const s = ret + blockLen
const res = await this.bam.read(Buffer.alloc(s), 0, s, 0, opts)
if (!res.bytesRead) {
throw new Error('Error reading header')
}
buffer = res.buffer.subarray(0, Math.min(res.bytesRead, ret))
buffer = await this.bam.read(s, 0)
} else {

@@ -162,9 +157,11 @@ buffer = await this.bam.readFile(opts)

const uncba = await unzip(buffer)
const dataView = new DataView(uncba.buffer)
if (uncba.readInt32LE(0) !== BAM_MAGIC) {
if (dataView.getInt32(0, true) !== BAM_MAGIC) {
throw new Error('Not a BAM file')
}
const headLen = uncba.readInt32LE(4)
const headLen = dataView.getInt32(4, true)
this.header = uncba.toString('utf8', 8, 8 + headLen)
const decoder = new TextDecoder('utf8')
this.header = decoder.decode(uncba.subarray(8, 8 + headLen))
const { chrToIndex, indexToChr } = await this._readRefSeqs(

@@ -209,26 +206,17 @@ headLen + 8,

}
const size = refSeqBytes + blockLen
const { bytesRead, buffer } = await this.bam.read(
Buffer.alloc(size),
0,
refSeqBytes,
0,
opts,
)
if (!bytesRead) {
throw new Error('Error reading refseqs from header')
}
const uncba = await unzip(
buffer.subarray(0, Math.min(bytesRead, refSeqBytes)),
)
const nRef = uncba.readInt32LE(start)
// const size = refSeqBytes + blockLen <-- use this?
const buffer = await this.bam.read(refSeqBytes, 0, opts)
const uncba = await unzip(buffer)
const dataView = new DataView(uncba.buffer)
const nRef = dataView.getInt32(start, true)
let p = start + 4
const chrToIndex: Record<string, number> = {}
const indexToChr: { refName: string; length: number }[] = []
const decoder = new TextDecoder('utf8')
for (let i = 0; i < nRef; i += 1) {
const lName = uncba.readInt32LE(p)
const lName = dataView.getInt32(p, true)
const refName = this.renameRefSeq(
uncba.toString('utf8', p + 4, p + 4 + lName - 1),
decoder.decode(uncba.subarray(p + 4, p + 4 + lName - 1)),
)
const lRef = uncba.readInt32LE(p + lName + 4)
const lRef = dataView.getInt32(p + lName + 4, true)

@@ -394,11 +382,3 @@ chrToIndex[refName] = i

async _readRegion(position: number, size: number, opts: BaseOpts = {}) {
const { bytesRead, buffer } = await this.bam.read(
Buffer.alloc(size),
0,
size,
position,
opts,
)
return buffer.subarray(0, Math.min(bytesRead, size))
return this.bam.read(size, position, opts)
}

@@ -422,3 +402,3 @@

async readBamFeatures(
ba: Buffer,
ba: Uint8Array,
cpositions: number[],

@@ -433,4 +413,5 @@ dpositions: number[],

const dataView = new DataView(ba.buffer)
while (blockStart + 4 < ba.length) {
const blockSize = ba.readInt32LE(blockStart)
const blockSize = dataView.getInt32(blockStart, true)
const blockEnd = blockStart + 4 + blockSize - 1

@@ -480,4 +461,4 @@

: // must be slice, not subarray for buffer polyfill on web
// eslint-disable-next-line @typescript-eslint/no-deprecated
crc32.signed(ba.slice(blockStart, blockEnd)),
// @ts-expect-error
crc32.signed(ba.subarray(blockStart, blockEnd)),
})

@@ -484,0 +465,0 @@

@@ -5,3 +5,3 @@ import VirtualOffset from './virtualOffset'

export default class Chunk {
public buffer?: Buffer
public buffer?: Uint8Array

@@ -8,0 +8,0 @@ constructor(

@@ -40,4 +40,5 @@ import { unzip } from '@gmod/bgzf-filehandle'

parseAuxData(bytes: Buffer, offset: number) {
const formatFlags = bytes.readInt32LE(offset)
parseAuxData(bytes: Uint8Array, offset: number) {
const dataView = new DataView(bytes.buffer)
const formatFlags = dataView.getUint32(offset, true)
const coordinateType =

@@ -52,10 +53,10 @@ formatFlags & 0x10000 ? 'zero-based-half-open' : '1-based-closed'

const columnNumbers = {
ref: bytes.readInt32LE(offset + 4),
start: bytes.readInt32LE(offset + 8),
end: bytes.readInt32LE(offset + 12),
ref: dataView.getInt32(offset + 4, true),
start: dataView.getInt32(offset + 8, true),
end: dataView.getInt32(offset + 12, true),
}
const metaValue = bytes.readInt32LE(offset + 16)
const metaValue = dataView.getInt32(offset + 16, true)
const metaChar = metaValue ? String.fromCharCode(metaValue) : ''
const skipLines = bytes.readInt32LE(offset + 20)
const nameSectionLength = bytes.readInt32LE(offset + 24)
const skipLines = dataView.getInt32(offset + 20, true)
const nameSectionLength = dataView.getInt32(offset + 24, true)

@@ -82,19 +83,21 @@ return {

const dataView = new DataView(bytes.buffer)
let csiVersion
// check TBI magic numbers
if (bytes.readUInt32LE(0) === CSI1_MAGIC) {
const magic = dataView.getUint32(0, true)
if (magic === CSI1_MAGIC) {
csiVersion = 1
} else if (bytes.readUInt32LE(0) === CSI2_MAGIC) {
} else if (magic === CSI2_MAGIC) {
csiVersion = 2
} else {
throw new Error('Not a CSI file')
throw new Error(`Not a CSI file ${magic}`)
// TODO: do we need to support big-endian CSI files?
}
this.minShift = bytes.readInt32LE(4)
this.depth = bytes.readInt32LE(8)
this.minShift = dataView.getInt32(4, true)
this.depth = dataView.getInt32(8, true)
this.maxBinNumber = ((1 << ((this.depth + 1) * 3)) - 1) / 7
const auxLength = bytes.readInt32LE(12)
const auxLength = dataView.getInt32(12, true)
const aux = auxLength >= 30 ? this.parseAuxData(bytes, 16) : undefined
const refCount = bytes.readInt32LE(16 + auxLength)
const refCount = dataView.getInt32(16 + auxLength, true)

@@ -112,3 +115,3 @@ type BinIndex = Record<string, Chunk[]>

// the binning index
const binCount = bytes.readInt32LE(curr)
const binCount = dataView.getInt32(curr, true)
curr += 4

@@ -118,3 +121,3 @@ const binIndex: Record<string, Chunk[]> = {}

for (let j = 0; j < binCount; j++) {
const bin = bytes.readUInt32LE(curr)
const bin = dataView.getUint32(curr, true)
curr += 4

@@ -127,3 +130,3 @@ if (bin > this.maxBinNumber) {

curr += 8
const chunkCount = bytes.readInt32LE(curr)
const chunkCount = dataView.getInt32(curr, true)
curr += 4

@@ -130,0 +133,0 @@ const chunks = new Array<Chunk>(chunkCount)

import { unzip } from '@gmod/bgzf-filehandle'
import { Buffer } from 'buffer'
import { BaseOpts, BamOpts } from './util'
import { BaseOpts, BamOpts, concatUint8Array } from './util'
import BamFile, { BAM_MAGIC } from './bamFile'

@@ -17,3 +16,4 @@ import Chunk from './chunk'

if (url.startsWith('data:')) {
return Buffer.from(url.split(',')[1], 'base64')
// @ts-expect-error
return Uint8Array.fromBase64(url.split(',')[1], 'base64') as Uint8Array
} else {

@@ -33,3 +33,3 @@ //remove referer header, it is not even allowed to be specified

}
return Buffer.from(await res.arrayBuffer())
return new Uint8Array(await res.arrayBuffer())
}

@@ -39,3 +39,3 @@ }),

return Buffer.concat(await Promise.all(res.map(elt => unzip(elt))))
return concatUint8Array(await Promise.all(res.map(elt => unzip(elt))))
}

@@ -114,2 +114,3 @@

// @ts-expect-error
async _readChunk({ chunk }: { chunk: Chunk; opts: BaseOpts }) {

@@ -119,3 +120,8 @@ if (!chunk.buffer) {

}
return { data: chunk.buffer, cpositions: [], dpositions: [], chunk }
return {
data: chunk.buffer,
cpositions: [],
dpositions: [],
chunk,
}
}

@@ -133,8 +139,11 @@

const uncba = await concat(data.htsget.urls, opts)
const dataView = new DataView(uncba.buffer)
if (uncba.readInt32LE(0) !== BAM_MAGIC) {
if (dataView.getInt32(0, true) !== BAM_MAGIC) {
throw new Error('Not a BAM file')
}
const headLen = uncba.readInt32LE(4)
const headerText = uncba.toString('utf8', 8, 8 + headLen)
const headLen = dataView.getInt32(4, true)
const decoder = new TextDecoder('utf8')
const headerText = decoder.decode(uncba.subarray(8, 8 + headLen))
const samHeader = parseHeaderText(headerText)

@@ -141,0 +150,0 @@

@@ -1,2 +0,2 @@

import { GenericFilehandle } from 'generic-filehandle'
import { GenericFilehandle } from 'generic-filehandle2'
import Chunk from './chunk'

@@ -3,0 +3,0 @@ import { BaseOpts } from './util'

import Constants from './constants'
import type { Buffer } from 'buffer'

@@ -10,7 +9,9 @@ const SEQRET_DECODER = '=ACMGRSVTWYHKDBN'.split('')

end: number
byteArray: Buffer
byteArray: Uint8Array
}
export default class BamRecord {
public fileOffset: number
private bytes: Bytes
#dataView: DataView

@@ -20,2 +21,3 @@ constructor(args: { bytes: Bytes; fileOffset: number }) {

this.fileOffset = args.fileOffset
this.#dataView = new DataView(this.bytes.byteArray.buffer)
}

@@ -29,11 +31,11 @@

return (
(this.byteArray.readInt32LE(this.bytes.start + 16) & 0xffff0000) >> 16
(this.#dataView.getInt32(this.bytes.start + 16, true) & 0xffff0000) >> 16
)
}
get ref_id() {
return this.byteArray.readInt32LE(this.bytes.start + 4)
return this.#dataView.getInt32(this.bytes.start + 4, true)
}
get start() {
return this.byteArray.readInt32LE(this.bytes.start + 8)
return this.#dataView.getInt32(this.bytes.start + 8, true)
}

@@ -79,11 +81,10 @@

get name() {
return this.byteArray.toString(
'ascii',
this.b0,
this.b0 + this.read_name_length - 1,
)
let str = ''
for (let i = 0; i < this.read_name_length - 1; i++) {
str += String.fromCharCode(this.byteArray[this.b0 + i])
}
return str
}
get tags() {
const { byteArray } = this.bytes
let p =

@@ -99,29 +100,29 @@ this.b0 +

while (p < blockEnd) {
const tag = String.fromCharCode(byteArray[p], byteArray[p + 1])
const type = String.fromCharCode(byteArray[p + 2])
const tag = String.fromCharCode(this.byteArray[p], this.byteArray[p + 1])
const type = String.fromCharCode(this.byteArray[p + 2])
p += 3
if (type === 'A') {
tags[tag] = String.fromCharCode(byteArray[p])
tags[tag] = String.fromCharCode(this.byteArray[p])
p += 1
} else if (type === 'i') {
tags[tag] = byteArray.readInt32LE(p)
tags[tag] = this.#dataView.getInt32(p, true)
p += 4
} else if (type === 'I') {
tags[tag] = byteArray.readUInt32LE(p)
tags[tag] = this.#dataView.getUint32(p, true)
p += 4
} else if (type === 'c') {
tags[tag] = byteArray.readInt8(p)
tags[tag] = this.#dataView.getInt8(p)
p += 1
} else if (type === 'C') {
tags[tag] = byteArray.readUInt8(p)
tags[tag] = this.#dataView.getUint8(p)
p += 1
} else if (type === 's') {
tags[tag] = byteArray.readInt16LE(p)
tags[tag] = this.#dataView.getInt16(p, true)
p += 2
} else if (type === 'S') {
tags[tag] = byteArray.readUInt16LE(p)
tags[tag] = this.#dataView.getUint16(p, true)
p += 2
} else if (type === 'f') {
tags[tag] = byteArray.readFloatLE(p)
tags[tag] = this.#dataView.getFloat32(p, true)
p += 4

@@ -131,3 +132,3 @@ } else if (type === 'Z' || type === 'H') {

while (p <= blockEnd) {
const cc = byteArray[p++]
const cc = this.byteArray[p++]
if (cc !== 0) {

@@ -141,5 +142,5 @@ value.push(String.fromCharCode(cc))

} else if (type === 'B') {
const cc = byteArray[p++]
const cc = this.byteArray[p++]
const Btype = String.fromCharCode(cc)
const limit = byteArray.readInt32LE(p)
const limit = this.#dataView.getInt32(p, true)
p += 4

@@ -150,3 +151,3 @@ if (Btype === 'i') {

for (let k = 0; k < limit; k++) {
const cigop = byteArray.readInt32LE(p)
const cigop = this.#dataView.getInt32(p, true)
const lop = cigop >> 4

@@ -161,3 +162,3 @@ const op = CIGAR_DECODER[cigop & 0xf]

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt32LE(p))
value.push(this.#dataView.getInt32(p, true))
p += 4

@@ -171,3 +172,3 @@ }

for (let k = 0; k < limit; k++) {
const cigop = byteArray.readUInt32LE(p)
const cigop = this.#dataView.getUint32(p, true)
const lop = cigop >> 4

@@ -182,3 +183,3 @@ const op = CIGAR_DECODER[cigop & 0xf]

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt32LE(p))
value.push(this.#dataView.getUint32(p, true))
p += 4

@@ -191,3 +192,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt16LE(p))
value.push(this.#dataView.getInt16(p, true))
p += 2

@@ -199,3 +200,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt16LE(p))
value.push(this.#dataView.getUint16(p, true))
p += 2

@@ -207,3 +208,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readInt8(p))
value.push(this.#dataView.getInt8(p))
p += 1

@@ -215,3 +216,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readUInt8(p))
value.push(this.#dataView.getUint8(p))
p += 1

@@ -223,3 +224,3 @@ }

for (let k = 0; k < limit; k++) {
value.push(byteArray.readFloatLE(p))
value.push(this.#dataView.getFloat32(p, true))
p += 4

@@ -314,3 +315,3 @@ }

// that consumes entire seqLen
let cigop = this.byteArray.readInt32LE(p)
let cigop = this.#dataView.getInt32(p, true)
let lop = cigop >> 4

@@ -322,3 +323,3 @@ let op = CIGAR_DECODER[cigop & 0xf]

p += 4
cigop = this.byteArray.readInt32LE(p)
cigop = this.#dataView.getInt32(p, true)
lop = cigop >> 4

@@ -336,3 +337,3 @@ op = CIGAR_DECODER[cigop & 0xf]

for (let c = 0; c < numCigarOps; ++c) {
cigop = this.byteArray.readInt32LE(p)
cigop = this.#dataView.getInt32(p, true)
lop = cigop >> 4

@@ -434,23 +435,23 @@ op = CIGAR_DECODER[cigop & 0xf]

get bin_mq_nl() {
return this.byteArray.readInt32LE(this.bytes.start + 12)
return this.#dataView.getInt32(this.bytes.start + 12, true)
}
get flag_nc() {
return this.byteArray.readInt32LE(this.bytes.start + 16)
return this.#dataView.getInt32(this.bytes.start + 16, true)
}
get seq_length() {
return this.byteArray.readInt32LE(this.bytes.start + 20)
return this.#dataView.getInt32(this.bytes.start + 20, true)
}
get next_refid() {
return this.byteArray.readInt32LE(this.bytes.start + 24)
return this.#dataView.getInt32(this.bytes.start + 24, true)
}
get next_pos() {
return this.byteArray.readInt32LE(this.bytes.start + 28)
return this.#dataView.getInt32(this.bytes.start + 28, true)
}
get template_length() {
return this.byteArray.readInt32LE(this.bytes.start + 32)
return this.#dataView.getInt32(this.bytes.start + 32, true)
}

@@ -457,0 +458,0 @@

@@ -105,3 +105,3 @@ import Long from 'long'

export function parsePseudoBin(bytes: Buffer, offset: number) {
export function parsePseudoBin(bytes: Uint8Array, offset: number) {
return {

@@ -127,3 +127,3 @@ lineCount: Long.fromBytesLE(

export function parseNameBytes(
namesBytes: Buffer,
namesBytes: Uint8Array,
renameRefSeq: (arg: string) => string = s => s,

@@ -138,3 +138,6 @@ ) {

if (currNameStart < i) {
let refName = namesBytes.toString('utf8', currNameStart, i)
let refName = ''
for (let j = currNameStart; j < i; j++) {
refName += String.fromCharCode(namesBytes[j])
}
refName = renameRefSeq(refName)

@@ -150,1 +153,18 @@ refIdToName[currRefId] = refName

}
export function sum(array: Uint8Array[]) {
let sum = 0
for (const entry of array) {
sum += entry.length
}
return sum
}
export function concatUint8Array(args: Uint8Array[]) {
const mergedArray = new Uint8Array(sum(args))
let offset = 0
for (const entry of args) {
mergedArray.set(entry, offset)
offset += entry.length
}
return mergedArray
}

@@ -33,3 +33,3 @@ export default class VirtualOffset {

}
export function fromBytes(bytes: Buffer, offset = 0, bigendian = false) {
export function fromBytes(bytes: Uint8Array, offset = 0, bigendian = false) {
if (bigendian) {

@@ -36,0 +36,0 @@ throw new Error('big-endian virtual file offsets not implemented')

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc