@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.
@@ -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
- var abortable_promise_cache_1 = __importDefault(require("abortable-promise-cache"));
43
- var quick_lru_1 = __importDefault(require("quick-lru"));
44
- var generic_filehandle_1 = require("generic-filehandle");
45
- var bgzf_filehandle_1 = require("@gmod/bgzf-filehandle");
46
- var util_1 = require("./util");
47
- var tbi_1 = __importDefault(require("./tbi"));
48
- var csi_1 = __importDefault(require("./csi"));
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(function (resolve) {
24
+ return new Promise(resolve => {
51
25
  setTimeout(resolve, time);
52
26
  });
53
27
  }
54
- var TabixIndexedFile = /** @class */ (function () {
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
- function TabixIndexedFile(_a) {
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: 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: 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: 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: 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("".concat(path, ".tbi")),
108
- renameRefSeqs: 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
- TabixIndexedFile.prototype.getLines = function (refName, start, end, opts) {
131
- return __awaiter(this, void 0, void 0, function () {
132
- var signal, options, callback, metadata, chunks, i, size, last, chunkNum, previousStartCoordinate, c, _a, buffer, cpositions, dpositions, lines, blockStart, pos, i, line, _b, startCoordinate, overlaps;
133
- return __generator(this, function (_c) {
134
- switch (_c.label) {
135
- case 0:
136
- options = {};
137
- if (typeof opts === 'undefined') {
138
- throw new TypeError('line callback must be provided');
139
- }
140
- if (typeof opts === 'function') {
141
- callback = opts;
142
- }
143
- else {
144
- options = opts;
145
- callback = opts.lineCallback;
146
- }
147
- if (refName === undefined) {
148
- throw new TypeError('must provide a reference sequence name');
149
- }
150
- if (!callback) {
151
- throw new TypeError('line callback must be provided');
152
- }
153
- return [4 /*yield*/, this.index.getMetadata(options)];
154
- case 1:
155
- metadata = _c.sent();
156
- (0, util_1.checkAbortSignal)(signal);
157
- if (!start) {
158
- start = 0;
159
- }
160
- if (!end) {
161
- end = metadata.maxRefLength;
162
- }
163
- if (!(start <= end)) {
164
- throw new TypeError('invalid start and end coordinates. start must be less than or equal to end');
165
- }
166
- if (start === end) {
167
- return [2 /*return*/];
168
- }
169
- return [4 /*yield*/, this.index.blocksForRange(refName, start, end, options)];
170
- case 2:
171
- chunks = _c.sent();
172
- (0, util_1.checkAbortSignal)(signal);
173
- // check the chunks for any that are over the size limit. if
174
- // any are, don't fetch any of them
175
- for (i = 0; i < chunks.length; i += 1) {
176
- size = chunks[i].fetchedSize();
177
- if (size > this.chunkSizeLimit) {
178
- throw new Error("Too much data. Chunk size ".concat(size.toLocaleString(), " bytes exceeds chunkSizeLimit of ").concat(this.chunkSizeLimit.toLocaleString(), "."));
179
- }
180
- }
181
- last = Date.now();
182
- chunkNum = 0;
183
- _c.label = 3;
184
- case 3:
185
- if (!(chunkNum < chunks.length)) return [3 /*break*/, 9];
186
- previousStartCoordinate = void 0;
187
- c = chunks[chunkNum];
188
- return [4 /*yield*/, this.chunkCache.get(c.toString(), c, signal)];
189
- case 4:
190
- _a = _c.sent(), buffer = _a.buffer, cpositions = _a.cpositions, dpositions = _a.dpositions;
191
- lines = (typeof TextDecoder !== 'undefined'
192
- ? new TextDecoder('utf-8').decode(buffer)
193
- : buffer.toString()).split('\n');
194
- lines.pop();
195
- (0, util_1.checkAbortSignal)(signal);
196
- blockStart = c.minv.dataPosition;
197
- pos = void 0;
198
- i = 0;
199
- _c.label = 5;
200
- case 5:
201
- if (!(i < lines.length)) return [3 /*break*/, 8];
202
- line = lines[i];
203
- for (pos = 0; blockStart >= dpositions[pos]; pos += 1) { }
204
- _b = this.checkLine(metadata, refName, start, end, line), startCoordinate = _b.startCoordinate, overlaps = _b.overlaps;
205
- // do a small check just to make sure that the lines are really sorted by start coordinate
206
- if (previousStartCoordinate !== undefined &&
207
- startCoordinate !== undefined &&
208
- previousStartCoordinate > startCoordinate) {
209
- throw new Error("Lines not sorted by start coordinate (".concat(previousStartCoordinate, " > ").concat(startCoordinate, "), this file is not usable with Tabix."));
210
- }
211
- previousStartCoordinate = startCoordinate;
212
- if (overlaps) {
213
- callback(line.trim(),
214
- // cpositions[pos] refers to actual file offset of a bgzip block boundaries
215
- //
216
- // we multiply by (1 <<8) in order to make sure each block has a "unique"
217
- // address space so that data in that block could never overlap
218
- //
219
- // then the blockStart-dpositions is an uncompressed file offset from
220
- // that bgzip block boundary, and since the cpositions are multiplied by
221
- // (1 << 8) these uncompressed offsets get a unique space
222
- cpositions[pos] * (1 << 8) + (blockStart - dpositions[pos]));
223
- }
224
- else if (startCoordinate !== undefined && startCoordinate >= end) {
225
- // the lines were overlapping the region, but now have stopped, so
226
- // we must be at the end of the relevant data and we can stop
227
- // processing data now
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
- return [4 /*yield*/, timeout(1)];
235
- case 6:
236
- _c.sent();
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
- TabixIndexedFile.prototype.getMetadata = function (opts) {
250
- if (opts === void 0) { opts = {}; }
251
- return __awaiter(this, void 0, void 0, function () {
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
- TabixIndexedFile.prototype.getHeaderBuffer = function (opts) {
265
- if (opts === void 0) { opts = {}; }
266
- return __awaiter(this, void 0, void 0, function () {
267
- var _a, firstDataLine, metaChar, maxBlockSize, maxFetch, bytes, e_1, lastNewline, newlineByte, metaByte, i;
268
- return __generator(this, function (_b) {
269
- switch (_b.label) {
270
- case 0: return [4 /*yield*/, this.getMetadata(opts)];
271
- case 1:
272
- _a = _b.sent(), firstDataLine = _a.firstDataLine, metaChar = _a.metaChar, maxBlockSize = _a.maxBlockSize;
273
- (0, util_1.checkAbortSignal)(opts.signal);
274
- maxFetch = firstDataLine && firstDataLine.blockPosition
275
- ? firstDataLine.blockPosition + maxBlockSize
276
- : maxBlockSize;
277
- return [4 /*yield*/, this._readRegion(0, maxFetch, opts)];
278
- case 2:
279
- bytes = _b.sent();
280
- (0, util_1.checkAbortSignal)(opts.signal);
281
- _b.label = 3;
282
- case 3:
283
- _b.trys.push([3, 5, , 6]);
284
- return [4 /*yield*/, (0, bgzf_filehandle_1.unzip)(bytes)];
285
- case 4:
286
- bytes = _b.sent();
287
- return [3 /*break*/, 6];
288
- case 5:
289
- e_1 = _b.sent();
290
- console.error(e_1);
291
- throw new Error(
292
- //@ts-ignore
293
- "error decompressing block ".concat(e_1.code, " at 0 (length ").concat(maxFetch, ") ").concat(e_1));
294
- case 6:
295
- // trim off lines after the last non-meta line
296
- if (metaChar) {
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
- TabixIndexedFile.prototype.getHeader = function (opts) {
322
- if (opts === void 0) { opts = {}; }
323
- return __awaiter(this, void 0, void 0, function () {
324
- var bytes;
325
- return __generator(this, function (_a) {
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
- TabixIndexedFile.prototype.getReferenceSequenceNames = function (opts) {
345
- if (opts === void 0) { opts = {}; }
346
- return __awaiter(this, void 0, void 0, function () {
347
- var metadata;
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
- TabixIndexedFile.prototype.checkLine = function (_a, regionRefName, regionStart, regionEnd, line) {
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
- var ref = columnNumbers.ref, start = columnNumbers.start, end = columnNumbers.end;
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
- var maxColumn = Math.max(ref, start, end);
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
- var currentColumnNumber = 1; // cols are numbered starting at 1 in the index metadata
393
- var currentColumnStart = 0;
394
- var refSeq = '';
395
- var startCoordinate = -Infinity;
396
- for (var i = 0; i < line.length + 1; i += 1) {
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: startCoordinate, overlaps: false };
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: startCoordinate, overlaps: false };
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
- var endCoordinate
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: startCoordinate, overlaps: true };
446
- };
447
- TabixIndexedFile.prototype._getVcfEnd = function (startCoordinate, refSeq, info) {
448
- var endCoordinate = startCoordinate + refSeq.length;
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
- var isTRA = info.indexOf('SVTYPE=TRA') !== -1;
381
+ const isTRA = info.indexOf('SVTYPE=TRA') !== -1;
455
382
  if (info[0] !== '.' && !isTRA) {
456
- var prevChar = ';';
457
- for (var j = 0; j < info.length; j += 1) {
383
+ let prevChar = ';';
384
+ for (let j = 0; j < info.length; j += 1) {
458
385
  if (prevChar === ';' && info.slice(j, j + 4) === 'END=') {
459
- var valueEnd = info.indexOf(';', j);
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
- TabixIndexedFile.prototype.lineCount = function (refName, opts) {
480
- if (opts === void 0) { opts = {}; }
481
- return __awaiter(this, void 0, void 0, function () {
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
- TabixIndexedFile.prototype._readRegion = function (position, compressedSize, opts) {
488
- if (opts === void 0) { opts = {}; }
489
- return __awaiter(this, void 0, void 0, function () {
490
- var _a, bytesRead, buffer;
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
- TabixIndexedFile.prototype.readChunk = function (chunk, opts) {
508
- if (opts === void 0) { opts = {}; }
509
- return __awaiter(this, void 0, void 0, function () {
510
- var compressedData;
511
- return __generator(this, function (_a) {
512
- switch (_a.label) {
513
- case 0: return [4 /*yield*/, this._readRegion(chunk.minv.blockPosition, chunk.fetchedSize(), opts)];
514
- case 1:
515
- compressedData = _a.sent();
516
- try {
517
- return [2 /*return*/, (0, bgzf_filehandle_1.unzipChunkSlice)(compressedData, chunk)];
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
- return TabixIndexedFile;
528
- }());
435
+ }
436
+ }
529
437
  exports.default = TabixIndexedFile;
530
438
  //# sourceMappingURL=tabixIndexedFile.js.map