@jbrowse/graph-walks 0.0.0-stage → 1.0.0

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.
@@ -0,0 +1,609 @@
1
+ // A chunk of gfa-to-tabix's walk format 2, one file per reference (that
2
+ // repo's docs/walks-format.md). A chunk lists its `ref` rows, which joined in
3
+ // hapOffset order are the chunk's reference path R; a `nodes` row with the
4
+ // lengths of the nodes its routes visit; `route` rows, each a way off R; and a
5
+ // `walk` row per piece of every other path, naming the routes it takes in
6
+ // order. A handle is 2 * id + 1 when reversed.
7
+ const NONE = -1;
8
+ const idOf = (handle) => (handle - (handle & 1)) / 2;
9
+ // the other strand's handle; arithmetic, which holds past 2^31
10
+ const otherStrand = (handle) => (handle & 1 ? handle - 1 : handle + 1);
11
+ // The handles a column of delta tokens names, each 2 * (id - previous id) +
12
+ // reverse, from `previous.id`, which ends on the last id read
13
+ function tokens(field, previous, out) {
14
+ if (field === '.') {
15
+ return out;
16
+ }
17
+ let v = 0;
18
+ let neg = false;
19
+ for (let p = 0; p <= field.length; p++) {
20
+ const ch = p < field.length ? field.charCodeAt(p) : 44;
21
+ if (ch === 44) {
22
+ const d = neg ? -v : v;
23
+ const r = d & 1;
24
+ previous.id += (d - r) / 2;
25
+ out.push(previous.id * 2 + r);
26
+ v = 0;
27
+ neg = false;
28
+ }
29
+ else if (ch === 45) {
30
+ neg = true;
31
+ }
32
+ else {
33
+ v = v * 10 + (ch - 48);
34
+ }
35
+ }
36
+ return out;
37
+ }
38
+ function numbers(field) {
39
+ return field === '.' ? [] : field.split(',').map(Number);
40
+ }
41
+ // A link the way round the converter writes it, so a link and its reverse
42
+ // are one: the smaller of a and the flipped b first
43
+ export function canonicalLink(a, b) {
44
+ return a > otherStrand(b) ? [otherStrand(b), otherStrand(a)] : [a, b];
45
+ }
46
+ function tags(cols, from, anchor) {
47
+ let prev;
48
+ let next;
49
+ for (let k = from; k < cols.length; k++) {
50
+ const tag = cols[k];
51
+ if (tag.startsWith('pv:') || tag.startsWith('nx:')) {
52
+ const at = tag.lastIndexOf(':');
53
+ const chunk = {
54
+ refName: tag[3] === 'Z' ? tag.slice(5, at) : anchor,
55
+ start: +tag.slice(at + 1),
56
+ };
57
+ if (tag.startsWith('pv:')) {
58
+ prev = chunk;
59
+ }
60
+ else {
61
+ next = chunk;
62
+ }
63
+ }
64
+ }
65
+ return { prev, next };
66
+ }
67
+ // The chunk's reference path R: its handles, the bp of its steps before each
68
+ // position, and each node's occurrences in R
69
+ export class RefPath {
70
+ constructor() {
71
+ this.handles = [];
72
+ this.before = [0];
73
+ this.occurrences = new Map();
74
+ }
75
+ add(handles, lengths) {
76
+ for (let k = 0; k < handles.length; k++) {
77
+ const h = handles[k];
78
+ const i = this.handles.length;
79
+ this.handles.push(h);
80
+ this.before.push(this.before[i] + lengths[k]);
81
+ const id = idOf(h);
82
+ const list = this.occurrences.get(id);
83
+ if (list) {
84
+ list.push(i);
85
+ }
86
+ else {
87
+ this.occurrences.set(id, [i]);
88
+ }
89
+ }
90
+ }
91
+ // bp of R's steps before position i
92
+ bp(i) {
93
+ return this.before[i];
94
+ }
95
+ first(handle) {
96
+ var _a;
97
+ return (_a = this.occurrences.get(idOf(handle))) === null || _a === void 0 ? void 0 : _a[0];
98
+ }
99
+ nearest(handle, i) {
100
+ var _a;
101
+ let best;
102
+ for (const j of (_a = this.occurrences.get(idOf(handle))) !== null && _a !== void 0 ? _a : []) {
103
+ if (best === undefined || Math.abs(j - i) <= Math.abs(best - i)) {
104
+ best = j;
105
+ }
106
+ }
107
+ return best;
108
+ }
109
+ // The position a stretch along R from position i, reading R the way `h`
110
+ // does there, first holds `target`, i itself included
111
+ reach(i, h, target) {
112
+ var _a;
113
+ const forward = this.handles[i] === h;
114
+ let found;
115
+ for (const j of (_a = this.occurrences.get(idOf(target))) !== null && _a !== void 0 ? _a : []) {
116
+ if (forward && j >= i && this.handles[j] === target) {
117
+ if (found === undefined || j < found) {
118
+ found = j;
119
+ }
120
+ }
121
+ else if (!forward &&
122
+ j <= i &&
123
+ otherStrand(this.handles[j]) === target) {
124
+ if (found === undefined || j > found) {
125
+ found = j;
126
+ }
127
+ }
128
+ }
129
+ return found;
130
+ }
131
+ handleAt(i, forward) {
132
+ return forward ? this.handles[i] : otherStrand(this.handles[i]);
133
+ }
134
+ }
135
+ export class WalkRowError extends Error {
136
+ }
137
+ const orientedEntry = (route, flip) => flip ? (route.exit === NONE ? NONE : otherStrand(route.exit)) : route.entry;
138
+ const orientedExit = (route, flip) => flip ? (route.entry === NONE ? NONE : otherStrand(route.entry)) : route.exit;
139
+ // A route's interior handle k, taken the way round `flip` says
140
+ const interiorAt = (route, flip, k) => flip
141
+ ? otherStrand(route.interior[route.interior.length - 1 - k])
142
+ : route.interior[k];
143
+ // A walk row's choices, three numbers each: route index, flip and step count,
144
+ // from `tok` or `tok:count` tokens
145
+ function parseChoices(field) {
146
+ const choices = [];
147
+ if (field !== '.') {
148
+ let previous = 0;
149
+ let v = 0;
150
+ let neg = false;
151
+ let tok = 0;
152
+ let colonSeen = false;
153
+ for (let p = 0; p <= field.length; p++) {
154
+ const ch = p < field.length ? field.charCodeAt(p) : 44;
155
+ if (ch === 44) {
156
+ const value = neg ? -v : v;
157
+ const d = colonSeen ? tok : value;
158
+ const r = d & 1;
159
+ previous += (d - r) / 2;
160
+ choices.push(previous, r, colonSeen ? value : 0);
161
+ v = 0;
162
+ neg = false;
163
+ colonSeen = false;
164
+ }
165
+ else if (ch === 58) {
166
+ tok = neg ? -v : v;
167
+ v = 0;
168
+ neg = false;
169
+ colonSeen = true;
170
+ }
171
+ else if (ch === 45) {
172
+ neg = true;
173
+ }
174
+ else {
175
+ v = v * 10 + (ch - 48);
176
+ }
177
+ }
178
+ }
179
+ return choices;
180
+ }
181
+ // Follows a walk row as docs/walks-format.md decodes it
182
+ function traverse(path, routes, start, endField, choices, visit) {
183
+ const colon = endField.indexOf(':');
184
+ const end = start + +(colon < 0 ? endField : endField.slice(0, colon));
185
+ const counted = colon >= 0;
186
+ const tail = counted ? +endField.slice(colon + 1) : 0;
187
+ const routeOf = (index) => {
188
+ const route = routes[index];
189
+ if (!route) {
190
+ throw new WalkRowError(`route ${index} is not in the chunk`);
191
+ }
192
+ return route;
193
+ };
194
+ let k = 0;
195
+ if (choices.length > 0 &&
196
+ orientedEntry(routeOf(choices[0]), choices[1] === 1) === NONE) {
197
+ if (!visit.route(choices[0], choices[1] === 1)) {
198
+ return;
199
+ }
200
+ k = 3;
201
+ }
202
+ let h = start;
203
+ let i = path.first(h);
204
+ if (i === undefined) {
205
+ throw new WalkRowError('a walk row starts off its chunk');
206
+ }
207
+ visit.step(h);
208
+ const follow = (target, count) => {
209
+ const forward = path.handles[i] === h;
210
+ const j = counted
211
+ ? i + (forward ? count : -count)
212
+ : path.reach(i, h, target);
213
+ if (j === undefined || j < 0 || j >= path.handles.length) {
214
+ throw new WalkRowError('a walk row runs off its chunk');
215
+ }
216
+ if (j !== i) {
217
+ visit.stretch(i, j, forward);
218
+ }
219
+ i = j;
220
+ h = path.handleAt(j, forward);
221
+ };
222
+ for (; k < choices.length; k += 3) {
223
+ const index = choices[k];
224
+ const flip = choices[k + 1] === 1;
225
+ const route = routeOf(index);
226
+ const entry = orientedEntry(route, flip);
227
+ if (entry === NONE) {
228
+ throw new WalkRowError('an open route after the first choice');
229
+ }
230
+ follow(entry, choices[k + 2]);
231
+ if (!visit.route(index, flip)) {
232
+ return;
233
+ }
234
+ const exit = orientedExit(route, flip);
235
+ if (exit === NONE) {
236
+ return;
237
+ }
238
+ h = exit;
239
+ i = path.nearest(h, i);
240
+ if (i === undefined) {
241
+ throw new WalkRowError('a route lands off its chunk');
242
+ }
243
+ visit.step(h);
244
+ }
245
+ follow(end, tail);
246
+ if (h !== end) {
247
+ throw new WalkRowError('a walk row misses its last handle');
248
+ }
249
+ }
250
+ // A walk row's steps as node ids and reverse flags
251
+ function decodeWalk(path, routes, cols, choices, n) {
252
+ const ids = new Int32Array(n);
253
+ const rev = new Uint8Array(n);
254
+ let at = 0;
255
+ const push = (h) => {
256
+ if (at >= n) {
257
+ throw new WalkRowError('a walk row decodes to more steps than it states');
258
+ }
259
+ ids[at] = idOf(h);
260
+ rev[at++] = h & 1;
261
+ };
262
+ traverse(path, routes, +cols[9], cols[10], choices, {
263
+ step: push,
264
+ stretch(from, to, forward) {
265
+ if (forward) {
266
+ for (let j = from + 1; j <= to; j++) {
267
+ push(path.handles[j]);
268
+ }
269
+ }
270
+ else {
271
+ for (let j = from - 1; j >= to; j--) {
272
+ push(otherStrand(path.handles[j]));
273
+ }
274
+ }
275
+ },
276
+ route(index, flip) {
277
+ const route = routes[index];
278
+ for (let k = 0; k < route.interior.length; k++) {
279
+ push(interiorAt(route, flip, k));
280
+ }
281
+ return true;
282
+ },
283
+ });
284
+ if (at !== n) {
285
+ throw new WalkRowError(`a walk row decodes to ${at} steps, not ${n}`);
286
+ }
287
+ return { ids, rev };
288
+ }
289
+ // The bases an SQ:Z: tag at or after column `from` joins, one node's per
290
+ // length, as `ids` names them
291
+ function sliceBases(cols, from, ids, lengths, out) {
292
+ const tag = cols.slice(from).find(c => c.startsWith('SQ:Z:'));
293
+ if (tag === undefined) {
294
+ return;
295
+ }
296
+ let at = 5;
297
+ for (let k = 0; k < ids.length; k++) {
298
+ const length = lengths[k];
299
+ out.set(ids[k], tag.slice(at, at + length));
300
+ at += length;
301
+ }
302
+ }
303
+ /**
304
+ * Decodes one chunk's rows, each split on tabs. `keep` names the walk rows
305
+ * whose steps a cut wants, undefined for every one.
306
+ */
307
+ export function decodeChunk(rows, keep, withSequences = false) {
308
+ var _a, _b, _c;
309
+ const path = new RefPath();
310
+ const lengths = new Map();
311
+ const routes = [];
312
+ const out = {
313
+ sequences: new Map(),
314
+ rows: [],
315
+ nodes: new Map(),
316
+ ends: [],
317
+ chunk: { path, routes, walks: [], lengths },
318
+ };
319
+ const previous = { id: 0 };
320
+ const refs = rows.filter(c => c[3] === 'ref').sort((a, b) => +a[6] - +b[6]);
321
+ for (const cols of refs) {
322
+ const anchor = cols[0];
323
+ const handles = tokens(cols[9], { id: 0 }, []);
324
+ const nodeLengths = numbers(cols[10]);
325
+ const hapOffset = +cols[6];
326
+ path.add(handles, nodeLengths);
327
+ if (withSequences) {
328
+ sliceBases(cols, 11, handles.map(idOf), nodeLengths, out.sequences);
329
+ }
330
+ let start = hapOffset;
331
+ for (let k = 0; k < handles.length; k++) {
332
+ const id = idOf(handles[k]);
333
+ const length = nodeLengths[k];
334
+ lengths.set(id, length);
335
+ if (!out.nodes.has(id)) {
336
+ out.nodes.set(id, {
337
+ refName: cols[4],
338
+ start,
339
+ end: start + length,
340
+ rank: 0,
341
+ });
342
+ }
343
+ start += length;
344
+ }
345
+ out.ends.push({
346
+ name: cols[4],
347
+ fragStart: +cols[5],
348
+ piece: +cols[7],
349
+ first: handles[0],
350
+ last: handles.at(-1),
351
+ });
352
+ if (keep === undefined || keep(cols[4])) {
353
+ out.rows.push({
354
+ chunkStart: +cols[1],
355
+ name: cols[4],
356
+ fragStart: +cols[5],
357
+ hapOffset,
358
+ piece: +cols[7],
359
+ n: handles.length,
360
+ steps: {
361
+ ids: Int32Array.from(handles, idOf),
362
+ rev: Uint8Array.from(handles, h => h & 1),
363
+ },
364
+ ...tags(cols, 11, anchor),
365
+ });
366
+ }
367
+ }
368
+ for (const cols of rows) {
369
+ if (cols[3] === 'nodes') {
370
+ const ids = numbers(cols[4]);
371
+ const nodeLengths = numbers(cols[5]);
372
+ let id = 0;
373
+ for (let k = 0; k < ids.length; k++) {
374
+ id += ids[k];
375
+ ids[k] = id;
376
+ lengths.set(id, nodeLengths[k]);
377
+ }
378
+ if (withSequences) {
379
+ sliceBases(cols, 6, ids, nodeLengths, out.sequences);
380
+ }
381
+ }
382
+ else if (cols[3] === 'route') {
383
+ const entry = tokens(cols[4], previous, []);
384
+ const interior = tokens(cols[5], previous, []);
385
+ const exit = tokens(cols[6], previous, []);
386
+ routes.push({ entry: (_a = entry[0]) !== null && _a !== void 0 ? _a : NONE, interior, exit: (_b = exit[0]) !== null && _b !== void 0 ? _b : NONE });
387
+ }
388
+ }
389
+ const routeOf = (index) => {
390
+ const route = routes[index];
391
+ if (!route) {
392
+ throw new WalkRowError(`route ${index} is not in the chunk`);
393
+ }
394
+ return route;
395
+ };
396
+ for (const cols of rows) {
397
+ if (cols[3] !== 'walk') {
398
+ continue;
399
+ }
400
+ const choices = parseChoices(cols[11]);
401
+ out.chunk.walks.push({ cols, choices });
402
+ const name = cols[4];
403
+ const start = +cols[9];
404
+ const endField = cols[10];
405
+ const colon = endField.indexOf(':');
406
+ let first = start;
407
+ let last = start + +(colon < 0 ? endField : endField.slice(0, colon));
408
+ if (choices.length > 0) {
409
+ const lead = routeOf(choices[0]);
410
+ const leadFlip = choices[1] === 1;
411
+ if (orientedEntry(lead, leadFlip) === NONE) {
412
+ first = interiorAt(lead, leadFlip, 0);
413
+ }
414
+ const at = choices.length - 3;
415
+ const tail = routeOf(choices[at]);
416
+ const tailFlip = choices[at + 1] === 1;
417
+ if (orientedExit(tail, tailFlip) === NONE) {
418
+ last = interiorAt(tail, tailFlip, tail.interior.length - 1);
419
+ }
420
+ }
421
+ out.ends.push({
422
+ name,
423
+ fragStart: +cols[5],
424
+ piece: +cols[7],
425
+ first,
426
+ last,
427
+ });
428
+ if (keep === undefined || keep(name)) {
429
+ const n = +cols[8];
430
+ // the walk's nodes off R, by length; placeAndLink gives their position
431
+ for (let k = 0; k < choices.length; k += 3) {
432
+ for (const h of routeOf(choices[k]).interior) {
433
+ const id = idOf(h);
434
+ if (!out.nodes.has(id)) {
435
+ const length = (_c = lengths.get(id)) !== null && _c !== void 0 ? _c : 0;
436
+ out.nodes.set(id, { refName: name, start: 0, end: length, rank: 1 });
437
+ }
438
+ }
439
+ }
440
+ out.rows.push({
441
+ chunkStart: +cols[1],
442
+ name,
443
+ fragStart: +cols[5],
444
+ hapOffset: +cols[6],
445
+ piece: +cols[7],
446
+ n,
447
+ steps: decodeWalk(path, routes, cols, choices, n),
448
+ ...tags(cols, 12, cols[0]),
449
+ });
450
+ }
451
+ }
452
+ return out;
453
+ }
454
+ /**
455
+ * The positions of the cut's nodes off R, and the links among the cut's
456
+ * nodes, from the chunks a cut read: `kept` holds the node ids the cut draws.
457
+ * A node off R takes its position from the path whose name sorts first among
458
+ * those visiting it in the chunks read, at its earliest offset, as format 1's
459
+ * node rows had it. Links are R's and the routes', both ends in the cut.
460
+ */
461
+ export function placeAndLink(chunks, kept, place, link) {
462
+ const best = new Map();
463
+ for (const { path, routes, walks, lengths } of chunks) {
464
+ const handles = path.handles;
465
+ for (let k = 1; k < handles.length; k++) {
466
+ if (kept.has(idOf(handles[k - 1])) && kept.has(idOf(handles[k]))) {
467
+ link(...canonicalLink(handles[k - 1], handles[k]));
468
+ }
469
+ }
470
+ // routes holding a node of the cut off R
471
+ const wanted = new Uint8Array(routes.length);
472
+ for (let index = 0; index < routes.length; index++) {
473
+ const { entry, interior, exit } = routes[index];
474
+ let any = false;
475
+ let from = entry;
476
+ for (const h of interior) {
477
+ const inCut = kept.has(idOf(h));
478
+ any || (any = inCut);
479
+ if (inCut && from !== NONE && kept.has(idOf(from))) {
480
+ link(...canonicalLink(from, h));
481
+ }
482
+ from = h;
483
+ }
484
+ if (from !== NONE &&
485
+ exit !== NONE &&
486
+ kept.has(idOf(from)) &&
487
+ kept.has(idOf(exit))) {
488
+ link(...canonicalLink(from, exit));
489
+ }
490
+ wanted[index] = any ? 1 : 0;
491
+ }
492
+ const lengthOf = (h) => { var _a; return (_a = lengths.get(idOf(h))) !== null && _a !== void 0 ? _a : 0; };
493
+ const bp = new Float64Array(routes.length).fill(-1);
494
+ const routeBp = (index) => {
495
+ if (bp[index] < 0) {
496
+ bp[index] = routes[index].interior.reduce((s, h) => s + lengthOf(h), 0);
497
+ }
498
+ return bp[index];
499
+ };
500
+ // Walk rows run in path-name order, so a route's first taker is the
501
+ // first-named path to reach its nodes here; each row is followed only as
502
+ // far as the last route it is first to take
503
+ const taken = new Uint8Array(routes.length);
504
+ for (const { cols, choices } of walks) {
505
+ let remaining = 0;
506
+ for (let k = 0; k < choices.length; k += 3) {
507
+ const index = choices[k];
508
+ if (wanted[index] && !taken[index]) {
509
+ remaining++;
510
+ }
511
+ }
512
+ if (remaining === 0) {
513
+ continue;
514
+ }
515
+ const name = cols[4];
516
+ let offset = +cols[6];
517
+ traverse(path, routes, +cols[9], cols[10], choices, {
518
+ step(h) {
519
+ offset += lengthOf(h);
520
+ },
521
+ stretch(from, to, forward) {
522
+ offset += forward
523
+ ? path.bp(to + 1) - path.bp(from + 1)
524
+ : path.bp(from) - path.bp(to);
525
+ },
526
+ route(index, flip) {
527
+ if (!wanted[index] || taken[index]) {
528
+ offset += routeBp(index);
529
+ return true;
530
+ }
531
+ taken[index] = 1;
532
+ const route = routes[index];
533
+ for (let k = 0; k < route.interior.length; k++) {
534
+ const h = interiorAt(route, flip, k);
535
+ const id = idOf(h);
536
+ const length = lengthOf(h);
537
+ if (kept.has(id)) {
538
+ const held = best.get(id);
539
+ if (!held ||
540
+ name < held.refName ||
541
+ (name === held.refName && offset < held.start)) {
542
+ best.set(id, {
543
+ refName: name,
544
+ start: offset,
545
+ end: offset + length,
546
+ rank: 1,
547
+ });
548
+ }
549
+ }
550
+ offset += length;
551
+ }
552
+ remaining--;
553
+ return remaining > 0;
554
+ },
555
+ });
556
+ }
557
+ }
558
+ for (const [id, node] of best) {
559
+ place(id, node);
560
+ }
561
+ }
562
+ // The reference's nodes in a chunk's ref rows, with their positions
563
+ export function referenceNodes(rows) {
564
+ const nodes = [];
565
+ for (const cols of rows) {
566
+ if (cols[3] !== 'ref') {
567
+ continue;
568
+ }
569
+ const handles = tokens(cols[9], { id: 0 }, []);
570
+ const nodeLengths = numbers(cols[10]);
571
+ let start = +cols[6];
572
+ for (let k = 0; k < handles.length; k++) {
573
+ const length = nodeLengths[k];
574
+ nodes.push({
575
+ id: idOf(handles[k]),
576
+ refName: cols[4],
577
+ start,
578
+ end: start + length,
579
+ rank: 0,
580
+ });
581
+ start += length;
582
+ }
583
+ }
584
+ return nodes;
585
+ }
586
+ // Links between consecutive pieces of a path, each piece's last step to the
587
+ // next one's first, among the pieces read
588
+ export function pieceLinks(ends) {
589
+ const byPath = new Map();
590
+ for (const e of ends) {
591
+ const key = `${e.name}\t${e.fragStart}`;
592
+ let pieces = byPath.get(key);
593
+ if (!pieces) {
594
+ pieces = new Map();
595
+ byPath.set(key, pieces);
596
+ }
597
+ pieces.set(e.piece, e);
598
+ }
599
+ const links = [];
600
+ for (const pieces of byPath.values()) {
601
+ for (const [piece, e] of pieces) {
602
+ const next = pieces.get(piece + 1);
603
+ if (next) {
604
+ links.push(canonicalLink(e.last, next.first));
605
+ }
606
+ }
607
+ }
608
+ return links;
609
+ }
@@ -0,0 +1,75 @@
1
+ import { WalkGraph } from './walkRows.ts';
2
+ import type { BudgetNames, WalkHeader } from './walkRows.ts';
3
+ /**
4
+ * A Tabix-indexed file as the reader uses one: @gmod/tabix's
5
+ * TabixIndexedFile is one. `bytesForRegions` estimates the compressed bytes a
6
+ * read fetches, which the byte budget needs.
7
+ */
8
+ export interface TabixSource {
9
+ getHeaderLines(opts?: {
10
+ signal?: AbortSignal;
11
+ }): Promise<string[]>;
12
+ getLines(refName: string, start: number, end: number, opts: {
13
+ signal?: AbortSignal;
14
+ lineCallback: (line: string) => void;
15
+ }): Promise<void>;
16
+ getReferenceSequenceNames(opts?: {
17
+ signal?: AbortSignal;
18
+ }): Promise<string[]>;
19
+ bytesForRegions?(regions: {
20
+ refName: string;
21
+ start: number;
22
+ end: number;
23
+ }[], opts?: {
24
+ signal?: AbortSignal;
25
+ }): Promise<number>;
26
+ }
27
+ export declare class WalkFormatError extends Error {
28
+ name: string;
29
+ }
30
+ export interface WalkCutOptions {
31
+ signal?: AbortSignal;
32
+ keep?: (name: string) => boolean;
33
+ stepBudget?: number;
34
+ byteBudget?: number;
35
+ context?: number;
36
+ links?: boolean;
37
+ sequences?: boolean;
38
+ budgetNames?: BudgetNames;
39
+ }
40
+ export type WalkReaderHeader = WalkHeader & {
41
+ chunk: number;
42
+ lookback: number;
43
+ };
44
+ /**
45
+ * A `gfa-to-tabix --walks` file, `<prefix>.<sample>.walks.bed.gz`, read a
46
+ * window at a time: one read over whole chunks, from the lookback chunks
47
+ * before the window to its end, each chunk decoded once. The index first
48
+ * estimates the bytes the read fetches, and a window past the byte budget
49
+ * reads no row. Only the walks `keep` accepts are decoded. The step budget
50
+ * bounds the steps the cut keeps, and rows holding READ_STEPS_PER_KEPT times
51
+ * as many stop the read.
52
+ */
53
+ export declare class WalkReader {
54
+ private headerRead;
55
+ private readonly walks;
56
+ constructor(walks: TabixSource);
57
+ header(opts?: {
58
+ signal?: AbortSignal;
59
+ }): Promise<WalkReaderHeader>;
60
+ referenceSequences(opts?: {
61
+ signal?: AbortSignal;
62
+ }): Promise<string[]>;
63
+ queryStart(start: number, opts?: {
64
+ signal?: AbortSignal;
65
+ }): Promise<number>;
66
+ cut(refName: string, region: {
67
+ start: number;
68
+ end: number;
69
+ }, opts?: WalkCutOptions): Promise<{
70
+ kept: Set<number>;
71
+ fragments: import("./walkRows.ts").WalkFragment[];
72
+ open: import("./walkRows.ts").WalkChunk[];
73
+ graph: WalkGraph;
74
+ }>;
75
+ }