@gmod/tabix 1.5.4 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +37 -37
- package/dist/chunk.js +12 -14
- package/dist/chunk.js.map +1 -1
- package/dist/csi.js +170 -253
- package/dist/csi.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/indexFile.js +27 -73
- package/dist/indexFile.js.map +1 -1
- package/dist/tabixIndexedFile.d.ts +1 -1
- package/dist/tabixIndexedFile.js +214 -306
- package/dist/tabixIndexedFile.js.map +1 -1
- package/dist/tbi.js +185 -256
- package/dist/tbi.js.map +1 -1
- package/dist/util.js +10 -64
- package/dist/util.js.map +1 -1
- package/dist/virtualOffset.js +13 -20
- package/dist/virtualOffset.js.map +1 -1
- package/esm/tabixIndexedFile.d.ts +1 -1
- package/esm/tabixIndexedFile.js +21 -12
- package/esm/tabixIndexedFile.js.map +1 -1
- package/package.json +6 -7
- package/src/tabixIndexedFile.ts +23 -16
package/dist/tabixIndexedFile.js
CHANGED
|
@@ -8,50 +8,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
-
function step(op) {
|
|
16
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
-
while (_) try {
|
|
18
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
-
switch (op[0]) {
|
|
21
|
-
case 0: case 1: t = op; break;
|
|
22
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
-
default:
|
|
26
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
-
if (t[2]) _.ops.pop();
|
|
31
|
-
_.trys.pop(); continue;
|
|
32
|
-
}
|
|
33
|
-
op = body.call(thisArg, _);
|
|
34
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
13
|
};
|
|
41
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
15
|
+
const abortable_promise_cache_1 = __importDefault(require("abortable-promise-cache"));
|
|
16
|
+
const quick_lru_1 = __importDefault(require("quick-lru"));
|
|
17
|
+
const generic_filehandle_1 = require("generic-filehandle");
|
|
18
|
+
const bgzf_filehandle_1 = require("@gmod/bgzf-filehandle");
|
|
19
|
+
const util_1 = require("./util");
|
|
20
|
+
const tbi_1 = __importDefault(require("./tbi"));
|
|
21
|
+
const csi_1 = __importDefault(require("./csi"));
|
|
22
|
+
const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8') : undefined;
|
|
49
23
|
function timeout(time) {
|
|
50
|
-
return new Promise(
|
|
24
|
+
return new Promise(resolve => {
|
|
51
25
|
setTimeout(resolve, time);
|
|
52
26
|
});
|
|
53
27
|
}
|
|
54
|
-
|
|
28
|
+
class TabixIndexedFile {
|
|
55
29
|
/**
|
|
56
30
|
* @param {object} args
|
|
57
31
|
* @param {string} [args.path]
|
|
@@ -67,8 +41,7 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
67
41
|
* @param {number} [args.chunkCacheSize] maximum size in bytes of the chunk cache. default 5MB
|
|
68
42
|
* @param {number} [args.blockCacheSize] maximum size in bytes of the block cache. default 5MB
|
|
69
43
|
*/
|
|
70
|
-
|
|
71
|
-
var path = _a.path, filehandle = _a.filehandle, tbiPath = _a.tbiPath, tbiFilehandle = _a.tbiFilehandle, csiPath = _a.csiPath, csiFilehandle = _a.csiFilehandle, _b = _a.chunkSizeLimit, chunkSizeLimit = _b === void 0 ? 50000000 : _b, _c = _a.renameRefSeqs, renameRefSeqs = _c === void 0 ? function (n) { return n; } : _c, _d = _a.chunkCacheSize, chunkCacheSize = _d === void 0 ? 5 * Math.pow(2, 20) : _d;
|
|
44
|
+
constructor({ path, filehandle, tbiPath, tbiFilehandle, csiPath, csiFilehandle, chunkSizeLimit = 50000000, renameRefSeqs = n => n, chunkCacheSize = 5 * Math.pow(2, 20), }) {
|
|
72
45
|
if (filehandle) {
|
|
73
46
|
this.filehandle = filehandle;
|
|
74
47
|
}
|
|
@@ -81,31 +54,31 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
81
54
|
if (tbiFilehandle) {
|
|
82
55
|
this.index = new tbi_1.default({
|
|
83
56
|
filehandle: tbiFilehandle,
|
|
84
|
-
renameRefSeqs
|
|
57
|
+
renameRefSeqs,
|
|
85
58
|
});
|
|
86
59
|
}
|
|
87
60
|
else if (csiFilehandle) {
|
|
88
61
|
this.index = new csi_1.default({
|
|
89
62
|
filehandle: csiFilehandle,
|
|
90
|
-
renameRefSeqs
|
|
63
|
+
renameRefSeqs,
|
|
91
64
|
});
|
|
92
65
|
}
|
|
93
66
|
else if (tbiPath) {
|
|
94
67
|
this.index = new tbi_1.default({
|
|
95
68
|
filehandle: new generic_filehandle_1.LocalFile(tbiPath),
|
|
96
|
-
renameRefSeqs
|
|
69
|
+
renameRefSeqs,
|
|
97
70
|
});
|
|
98
71
|
}
|
|
99
72
|
else if (csiPath) {
|
|
100
73
|
this.index = new csi_1.default({
|
|
101
74
|
filehandle: new generic_filehandle_1.LocalFile(csiPath),
|
|
102
|
-
renameRefSeqs
|
|
75
|
+
renameRefSeqs,
|
|
103
76
|
});
|
|
104
77
|
}
|
|
105
78
|
else if (path) {
|
|
106
79
|
this.index = new tbi_1.default({
|
|
107
|
-
filehandle: new generic_filehandle_1.LocalFile(
|
|
108
|
-
renameRefSeqs
|
|
80
|
+
filehandle: new generic_filehandle_1.LocalFile(`${path}.tbi`),
|
|
81
|
+
renameRefSeqs,
|
|
109
82
|
});
|
|
110
83
|
}
|
|
111
84
|
else {
|
|
@@ -127,133 +100,118 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
127
100
|
* @param {function|object} lineCallback callback called for each line in the region. can also pass a object param containing obj.lineCallback, obj.signal, etc
|
|
128
101
|
* @returns {Promise} resolved when the whole read is finished, rejected on error
|
|
129
102
|
*/
|
|
130
|
-
|
|
131
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return [2 /*return*/];
|
|
229
|
-
}
|
|
230
|
-
blockStart += line.length + 1;
|
|
231
|
-
if (!(last - Date.now() > 500)) return [3 /*break*/, 7];
|
|
103
|
+
getLines(refName, start, end, opts) {
|
|
104
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
105
|
+
let signal;
|
|
106
|
+
let options = {};
|
|
107
|
+
let callback;
|
|
108
|
+
if (typeof opts === 'undefined') {
|
|
109
|
+
throw new TypeError('line callback must be provided');
|
|
110
|
+
}
|
|
111
|
+
if (typeof opts === 'function') {
|
|
112
|
+
callback = opts;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
options = opts;
|
|
116
|
+
callback = opts.lineCallback;
|
|
117
|
+
}
|
|
118
|
+
if (refName === undefined) {
|
|
119
|
+
throw new TypeError('must provide a reference sequence name');
|
|
120
|
+
}
|
|
121
|
+
if (!callback) {
|
|
122
|
+
throw new TypeError('line callback must be provided');
|
|
123
|
+
}
|
|
124
|
+
const metadata = yield this.index.getMetadata(options);
|
|
125
|
+
(0, util_1.checkAbortSignal)(signal);
|
|
126
|
+
if (!start) {
|
|
127
|
+
start = 0;
|
|
128
|
+
}
|
|
129
|
+
if (!end) {
|
|
130
|
+
end = metadata.maxRefLength;
|
|
131
|
+
}
|
|
132
|
+
if (!(start <= end)) {
|
|
133
|
+
throw new TypeError('invalid start and end coordinates. start must be less than or equal to end');
|
|
134
|
+
}
|
|
135
|
+
if (start === end) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const chunks = yield this.index.blocksForRange(refName, start, end, options);
|
|
139
|
+
(0, util_1.checkAbortSignal)(signal);
|
|
140
|
+
// check the chunks for any that are over the size limit. if
|
|
141
|
+
// any are, don't fetch any of them
|
|
142
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
143
|
+
const size = chunks[i].fetchedSize();
|
|
144
|
+
if (size > this.chunkSizeLimit) {
|
|
145
|
+
throw new Error(`Too much data. Chunk size ${size.toLocaleString()} bytes exceeds chunkSizeLimit of ${this.chunkSizeLimit.toLocaleString()}.`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// now go through each chunk and parse and filter the lines out of it
|
|
149
|
+
let last = Date.now();
|
|
150
|
+
for (let chunkNum = 0; chunkNum < chunks.length; chunkNum += 1) {
|
|
151
|
+
let previousStartCoordinate;
|
|
152
|
+
const c = chunks[chunkNum];
|
|
153
|
+
const { buffer, cpositions, dpositions } = yield this.chunkCache.get(c.toString(), c, signal);
|
|
154
|
+
(0, util_1.checkAbortSignal)(signal);
|
|
155
|
+
let blockStart = 0;
|
|
156
|
+
let pos = 0;
|
|
157
|
+
while (blockStart < buffer.length) {
|
|
158
|
+
const n = buffer.indexOf('\n', blockStart);
|
|
159
|
+
if (n === -1) {
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
const b = buffer.subarray(blockStart, n);
|
|
163
|
+
const line = (decoder === null || decoder === void 0 ? void 0 : decoder.decode(b)) || b.toString();
|
|
164
|
+
if (dpositions) {
|
|
165
|
+
while (blockStart + c.minv.dataPosition >= dpositions[pos++]) { }
|
|
166
|
+
pos--;
|
|
167
|
+
}
|
|
168
|
+
// filter the line for whether it is within the requested range
|
|
169
|
+
const { startCoordinate, overlaps } = this.checkLine(metadata, refName, start, end, line);
|
|
170
|
+
// do a small check just to make sure that the lines are really sorted
|
|
171
|
+
// by start coordinate
|
|
172
|
+
if (previousStartCoordinate !== undefined &&
|
|
173
|
+
startCoordinate !== undefined &&
|
|
174
|
+
previousStartCoordinate > startCoordinate) {
|
|
175
|
+
throw new Error(`Lines not sorted by start coordinate (${previousStartCoordinate} > ${startCoordinate}), this file is not usable with Tabix.`);
|
|
176
|
+
}
|
|
177
|
+
previousStartCoordinate = startCoordinate;
|
|
178
|
+
if (overlaps) {
|
|
179
|
+
callback(line.trim(),
|
|
180
|
+
// cpositions[pos] refers to actual file offset of a bgzip block boundaries
|
|
181
|
+
//
|
|
182
|
+
// we multiply by (1 <<8) in order to make sure each block has a "unique"
|
|
183
|
+
// address space so that data in that block could never overlap
|
|
184
|
+
//
|
|
185
|
+
// then the blockStart-dpositions is an uncompressed file offset from
|
|
186
|
+
// that bgzip block boundary, and since the cpositions are multiplied by
|
|
187
|
+
// (1 << 8) these uncompressed offsets get a unique space
|
|
188
|
+
cpositions[pos] * (1 << 8) +
|
|
189
|
+
(blockStart - dpositions[pos]) +
|
|
190
|
+
c.minv.dataPosition +
|
|
191
|
+
1);
|
|
192
|
+
}
|
|
193
|
+
else if (startCoordinate !== undefined && startCoordinate >= end) {
|
|
194
|
+
// the lines were overlapping the region, but now have stopped, so
|
|
195
|
+
// we must be at the end of the relevant data and we can stop
|
|
196
|
+
// processing data now
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// yield if we have emitted beyond the yield limit
|
|
200
|
+
if (last - Date.now() > 500) {
|
|
232
201
|
last = Date.now();
|
|
233
202
|
(0, util_1.checkAbortSignal)(signal);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
_c.label = 7;
|
|
238
|
-
case 7:
|
|
239
|
-
i += 1;
|
|
240
|
-
return [3 /*break*/, 5];
|
|
241
|
-
case 8:
|
|
242
|
-
chunkNum += 1;
|
|
243
|
-
return [3 /*break*/, 3];
|
|
244
|
-
case 9: return [2 /*return*/];
|
|
203
|
+
yield timeout(1);
|
|
204
|
+
}
|
|
205
|
+
blockStart = n + 1;
|
|
245
206
|
}
|
|
246
|
-
}
|
|
207
|
+
}
|
|
247
208
|
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return __generator(this, function (_a) {
|
|
253
|
-
return [2 /*return*/, this.index.getMetadata(opts)];
|
|
254
|
-
});
|
|
209
|
+
}
|
|
210
|
+
getMetadata(opts = {}) {
|
|
211
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
212
|
+
return this.index.getMetadata(opts);
|
|
255
213
|
});
|
|
256
|
-
}
|
|
214
|
+
}
|
|
257
215
|
/**
|
|
258
216
|
* get a buffer containing the "header" region of
|
|
259
217
|
* the file, which are the bytes up to the first
|
|
@@ -261,78 +219,58 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
261
219
|
*
|
|
262
220
|
* @returns {Promise} for a buffer
|
|
263
221
|
*/
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
lastNewline = -1;
|
|
298
|
-
newlineByte = '\n'.charCodeAt(0);
|
|
299
|
-
metaByte = metaChar.charCodeAt(0);
|
|
300
|
-
for (i = 0; i < bytes.length; i += 1) {
|
|
301
|
-
if (i === lastNewline + 1 && bytes[i] !== metaByte) {
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
if (bytes[i] === newlineByte) {
|
|
305
|
-
lastNewline = i;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
bytes = bytes.slice(0, lastNewline + 1);
|
|
309
|
-
}
|
|
310
|
-
return [2 /*return*/, bytes];
|
|
222
|
+
getHeaderBuffer(opts = {}) {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
const { firstDataLine, metaChar, maxBlockSize } = yield this.getMetadata(opts);
|
|
225
|
+
(0, util_1.checkAbortSignal)(opts.signal);
|
|
226
|
+
const maxFetch = firstDataLine && firstDataLine.blockPosition
|
|
227
|
+
? firstDataLine.blockPosition + maxBlockSize
|
|
228
|
+
: maxBlockSize;
|
|
229
|
+
// TODO: what if we don't have a firstDataLine, and the header
|
|
230
|
+
// actually takes up more than one block? this case is not covered here
|
|
231
|
+
let bytes = yield this._readRegion(0, maxFetch, opts);
|
|
232
|
+
(0, util_1.checkAbortSignal)(opts.signal);
|
|
233
|
+
try {
|
|
234
|
+
bytes = yield (0, bgzf_filehandle_1.unzip)(bytes);
|
|
235
|
+
}
|
|
236
|
+
catch (e) {
|
|
237
|
+
console.error(e);
|
|
238
|
+
throw new Error(
|
|
239
|
+
//@ts-ignore
|
|
240
|
+
`error decompressing block ${e.code} at 0 (length ${maxFetch}) ${e}`);
|
|
241
|
+
}
|
|
242
|
+
// trim off lines after the last non-meta line
|
|
243
|
+
if (metaChar) {
|
|
244
|
+
// trim backward from the end
|
|
245
|
+
let lastNewline = -1;
|
|
246
|
+
const newlineByte = '\n'.charCodeAt(0);
|
|
247
|
+
const metaByte = metaChar.charCodeAt(0);
|
|
248
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
249
|
+
if (i === lastNewline + 1 && bytes[i] !== metaByte) {
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
if (bytes[i] === newlineByte) {
|
|
253
|
+
lastNewline = i;
|
|
254
|
+
}
|
|
311
255
|
}
|
|
312
|
-
|
|
256
|
+
bytes = bytes.slice(0, lastNewline + 1);
|
|
257
|
+
}
|
|
258
|
+
return bytes;
|
|
313
259
|
});
|
|
314
|
-
}
|
|
260
|
+
}
|
|
315
261
|
/**
|
|
316
262
|
* get a string containing the "header" region of the
|
|
317
263
|
* file, is the portion up to the first non-meta line
|
|
318
264
|
*
|
|
319
265
|
* @returns {Promise} for a string
|
|
320
266
|
*/
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return
|
|
326
|
-
switch (_a.label) {
|
|
327
|
-
case 0: return [4 /*yield*/, this.getHeaderBuffer(opts)];
|
|
328
|
-
case 1:
|
|
329
|
-
bytes = _a.sent();
|
|
330
|
-
(0, util_1.checkAbortSignal)(opts.signal);
|
|
331
|
-
return [2 /*return*/, bytes.toString('utf8')];
|
|
332
|
-
}
|
|
333
|
-
});
|
|
267
|
+
getHeader(opts = {}) {
|
|
268
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
269
|
+
const bytes = yield this.getHeaderBuffer(opts);
|
|
270
|
+
(0, util_1.checkAbortSignal)(opts.signal);
|
|
271
|
+
return bytes.toString('utf8');
|
|
334
272
|
});
|
|
335
|
-
}
|
|
273
|
+
}
|
|
336
274
|
/**
|
|
337
275
|
* get an array of reference sequence names, in the order in which
|
|
338
276
|
* they occur in the file.
|
|
@@ -341,20 +279,12 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
341
279
|
*
|
|
342
280
|
* @returns {Promise} for an array of string sequence names
|
|
343
281
|
*/
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return __generator(this, function (_a) {
|
|
349
|
-
switch (_a.label) {
|
|
350
|
-
case 0: return [4 /*yield*/, this.getMetadata(opts)];
|
|
351
|
-
case 1:
|
|
352
|
-
metadata = _a.sent();
|
|
353
|
-
return [2 /*return*/, metadata.refIdToName];
|
|
354
|
-
}
|
|
355
|
-
});
|
|
282
|
+
getReferenceSequenceNames(opts = {}) {
|
|
283
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
284
|
+
const metadata = yield this.getMetadata(opts);
|
|
285
|
+
return metadata.refIdToName;
|
|
356
286
|
});
|
|
357
|
-
}
|
|
287
|
+
}
|
|
358
288
|
/**
|
|
359
289
|
* @param {object} metadata metadata object from the parsed index,
|
|
360
290
|
* containing columnNumbers, metaChar, and format
|
|
@@ -365,14 +295,13 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
365
295
|
* @returns {object} like `{startCoordinate, overlaps}`. overlaps is boolean,
|
|
366
296
|
* true if line is a data line that overlaps the given region
|
|
367
297
|
*/
|
|
368
|
-
|
|
369
|
-
var columnNumbers = _a.columnNumbers, metaChar = _a.metaChar, coordinateType = _a.coordinateType, format = _a.format;
|
|
298
|
+
checkLine({ columnNumbers, metaChar, coordinateType, format, }, regionRefName, regionStart, regionEnd, line) {
|
|
370
299
|
// skip meta lines
|
|
371
300
|
if (line.charAt(0) === metaChar) {
|
|
372
301
|
return { overlaps: false };
|
|
373
302
|
}
|
|
374
303
|
// check ref/start/end using column metadata from index
|
|
375
|
-
|
|
304
|
+
let { ref, start, end } = columnNumbers;
|
|
376
305
|
if (!ref) {
|
|
377
306
|
ref = 0;
|
|
378
307
|
}
|
|
@@ -385,15 +314,15 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
385
314
|
if (format === 'VCF') {
|
|
386
315
|
end = 8;
|
|
387
316
|
}
|
|
388
|
-
|
|
317
|
+
const maxColumn = Math.max(ref, start, end);
|
|
389
318
|
// this code is kind of complex, but it is fairly fast.
|
|
390
319
|
// basically, we want to avoid doing a split, because if the lines are really long
|
|
391
320
|
// that could lead to us allocating a bunch of extra memory, which is slow
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
for (
|
|
321
|
+
let currentColumnNumber = 1; // cols are numbered starting at 1 in the index metadata
|
|
322
|
+
let currentColumnStart = 0;
|
|
323
|
+
let refSeq = '';
|
|
324
|
+
let startCoordinate = -Infinity;
|
|
325
|
+
for (let i = 0; i < line.length + 1; i += 1) {
|
|
397
326
|
if (line[i] === '\t' || i === line.length) {
|
|
398
327
|
if (currentColumnNumber === ref) {
|
|
399
328
|
if (this.renameRefSeq(line.slice(currentColumnStart, i)) !==
|
|
@@ -408,12 +337,12 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
408
337
|
startCoordinate -= 1;
|
|
409
338
|
}
|
|
410
339
|
if (startCoordinate >= regionEnd) {
|
|
411
|
-
return { startCoordinate
|
|
340
|
+
return { startCoordinate, overlaps: false };
|
|
412
341
|
}
|
|
413
342
|
if (end === 0 || end === start) {
|
|
414
343
|
// if we have no end, we assume the feature is 1 bp long
|
|
415
344
|
if (startCoordinate + 1 <= regionStart) {
|
|
416
|
-
return { startCoordinate
|
|
345
|
+
return { startCoordinate, overlaps: false };
|
|
417
346
|
}
|
|
418
347
|
}
|
|
419
348
|
}
|
|
@@ -421,9 +350,7 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
421
350
|
refSeq = line.slice(currentColumnStart, i);
|
|
422
351
|
}
|
|
423
352
|
else if (currentColumnNumber === end) {
|
|
424
|
-
|
|
425
|
-
// this will never match if there is no end column
|
|
426
|
-
= void 0;
|
|
353
|
+
let endCoordinate;
|
|
427
354
|
// this will never match if there is no end column
|
|
428
355
|
if (format === 'VCF') {
|
|
429
356
|
endCoordinate = this._getVcfEnd(startCoordinate, refSeq, line.slice(currentColumnStart, i));
|
|
@@ -442,21 +369,21 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
442
369
|
}
|
|
443
370
|
}
|
|
444
371
|
}
|
|
445
|
-
return { startCoordinate
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
|
|
372
|
+
return { startCoordinate, overlaps: true };
|
|
373
|
+
}
|
|
374
|
+
_getVcfEnd(startCoordinate, refSeq, info) {
|
|
375
|
+
let endCoordinate = startCoordinate + refSeq.length;
|
|
449
376
|
// ignore TRA features as they specify CHR2 and END
|
|
450
377
|
// as being on a different chromosome
|
|
451
378
|
// if CHR2 is on the same chromosome, still ignore it
|
|
452
379
|
// because there should be another pairwise feature
|
|
453
380
|
// at the end of this one
|
|
454
|
-
|
|
381
|
+
const isTRA = info.indexOf('SVTYPE=TRA') !== -1;
|
|
455
382
|
if (info[0] !== '.' && !isTRA) {
|
|
456
|
-
|
|
457
|
-
for (
|
|
383
|
+
let prevChar = ';';
|
|
384
|
+
for (let j = 0; j < info.length; j += 1) {
|
|
458
385
|
if (prevChar === ';' && info.slice(j, j + 4) === 'END=') {
|
|
459
|
-
|
|
386
|
+
let valueEnd = info.indexOf(';', j);
|
|
460
387
|
if (valueEnd === -1) {
|
|
461
388
|
valueEnd = info.length;
|
|
462
389
|
}
|
|
@@ -470,61 +397,42 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
470
397
|
return startCoordinate + 1;
|
|
471
398
|
}
|
|
472
399
|
return endCoordinate;
|
|
473
|
-
}
|
|
400
|
+
}
|
|
474
401
|
/**
|
|
475
402
|
* return the approximate number of data lines in the given reference sequence
|
|
476
403
|
* @param {string} refSeq reference sequence name
|
|
477
404
|
* @returns {Promise} for number of data lines present on that reference sequence
|
|
478
405
|
*/
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return __generator(this, function (_a) {
|
|
483
|
-
return [2 /*return*/, this.index.lineCount(refName, opts)];
|
|
484
|
-
});
|
|
406
|
+
lineCount(refName, opts = {}) {
|
|
407
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
408
|
+
return this.index.lineCount(refName, opts);
|
|
485
409
|
});
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
return __generator(this, function (_b) {
|
|
492
|
-
switch (_b.label) {
|
|
493
|
-
case 0: return [4 /*yield*/, this.filehandle.read(Buffer.alloc(compressedSize), 0, compressedSize, position, opts)];
|
|
494
|
-
case 1:
|
|
495
|
-
_a = _b.sent(), bytesRead = _a.bytesRead, buffer = _a.buffer;
|
|
496
|
-
return [2 /*return*/, bytesRead < compressedSize ? buffer.slice(0, bytesRead) : buffer];
|
|
497
|
-
}
|
|
498
|
-
});
|
|
410
|
+
}
|
|
411
|
+
_readRegion(position, compressedSize, opts = {}) {
|
|
412
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
413
|
+
const { bytesRead, buffer } = yield this.filehandle.read(Buffer.alloc(compressedSize), 0, compressedSize, position, opts);
|
|
414
|
+
return bytesRead < compressedSize ? buffer.slice(0, bytesRead) : buffer;
|
|
499
415
|
});
|
|
500
|
-
}
|
|
416
|
+
}
|
|
501
417
|
/**
|
|
502
418
|
* read and uncompress the data in a chunk (composed of one or more
|
|
503
419
|
* contiguous bgzip blocks) of the file
|
|
504
420
|
* @param {Chunk} chunk
|
|
505
421
|
* @returns {Promise} for a string chunk of the file
|
|
506
422
|
*/
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
catch (e) {
|
|
520
|
-
throw new Error("error decompressing chunk ".concat(chunk.toString(), " ").concat(e));
|
|
521
|
-
}
|
|
522
|
-
return [2 /*return*/];
|
|
523
|
-
}
|
|
524
|
-
});
|
|
423
|
+
readChunk(chunk, opts = {}) {
|
|
424
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
425
|
+
// fetch the uncompressed data, uncompress carefully a block at a time,
|
|
426
|
+
// and stop when done
|
|
427
|
+
const compressedData = yield this._readRegion(chunk.minv.blockPosition, chunk.fetchedSize(), opts);
|
|
428
|
+
try {
|
|
429
|
+
return (0, bgzf_filehandle_1.unzipChunkSlice)(compressedData, chunk);
|
|
430
|
+
}
|
|
431
|
+
catch (e) {
|
|
432
|
+
throw new Error(`error decompressing chunk ${chunk.toString()} ${e}`);
|
|
433
|
+
}
|
|
525
434
|
});
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
}());
|
|
435
|
+
}
|
|
436
|
+
}
|
|
529
437
|
exports.default = TabixIndexedFile;
|
|
530
438
|
//# sourceMappingURL=tabixIndexedFile.js.map
|