@jbrowse/graph-walks 0.0.0-stage → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +89 -2
- package/esm/index.d.ts +7 -0
- package/esm/index.js +4 -0
- package/esm/util.d.ts +39 -0
- package/esm/util.js +34 -0
- package/esm/walkFormat2.d.ts +64 -0
- package/esm/walkFormat2.js +589 -0
- package/esm/walkReader.d.ts +77 -0
- package/esm/walkReader.js +250 -0
- package/esm/walkRows.d.ts +172 -0
- package/esm/walkRows.js +579 -0
- package/package.json +62 -4
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { decodeChunk, pieceLinks } from "./walkFormat2.js";
|
|
2
|
+
import { WalkGraph, byteBudgetError, chunkQueryStart, joinPieces, keptStepsError, lookbackChunks, parseWalkRow, stepBudgetError, walkCut, walkHeader, walkRowName, } from "./walkRows.js";
|
|
3
|
+
// A window reads whole chunks and keeps the steps on it, so a narrow window
|
|
4
|
+
// reads several times what it keeps: 464 haplotypes across the 1.5 kb ABCA7
|
|
5
|
+
// repeat read 5.0 M steps and keep 1.16 M, and the cut takes 0.4 s from warm
|
|
6
|
+
// files. The step budget bounds what the cut keeps, and rows are read to this
|
|
7
|
+
// multiple of it.
|
|
8
|
+
const READ_STEPS_PER_KEPT = 8;
|
|
9
|
+
// A walk the read cuts off goes on in the chunk its row names, out to which a
|
|
10
|
+
// cut reads on, in at most this many rounds and this many chunks from the read
|
|
11
|
+
const FOLLOW_ROUNDS = 3;
|
|
12
|
+
const FOLLOW_CHUNKS = 4;
|
|
13
|
+
// bp of reference a cut keeps past each end of the window
|
|
14
|
+
const WALK_CONTEXT = 1000;
|
|
15
|
+
// @gmod/tabix loops forever on a NaN end and reads nothing for a NaN start
|
|
16
|
+
// (GMOD/tabix-js#157), so every query refuses a range that is not finite first
|
|
17
|
+
function checkRange(refName, start, end) {
|
|
18
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
19
|
+
throw new Error(`${refName}:${start}-${end} is not a finite range to query the graph's index for`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function getLines(file, refName, start, end, opts) {
|
|
23
|
+
checkRange(refName, start, end);
|
|
24
|
+
return file.getLines(refName, start, end, opts);
|
|
25
|
+
}
|
|
26
|
+
// Settles like `promise` unless `signal` aborts first
|
|
27
|
+
function raceAbort(promise, signal) {
|
|
28
|
+
if (!signal) {
|
|
29
|
+
return promise;
|
|
30
|
+
}
|
|
31
|
+
signal.throwIfAborted();
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const onAbort = () => {
|
|
34
|
+
reject(signal.reason instanceof Error
|
|
35
|
+
? signal.reason
|
|
36
|
+
: new Error(String(signal.reason)));
|
|
37
|
+
};
|
|
38
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
39
|
+
promise.then(resolve, reject).finally(() => {
|
|
40
|
+
signal.removeEventListener('abort', onAbort);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A walk-indexed graph's three files, read a window at a time: one read of
|
|
46
|
+
* each over whole chunks, from the lookback chunks before the window to its
|
|
47
|
+
* end. The indexes first estimate the bytes those reads fetch, and a window
|
|
48
|
+
* past the byte budget reads no row. Only the walks `keep` accepts are
|
|
49
|
+
* decoded. The step budget bounds the steps the cut keeps, and rows holding
|
|
50
|
+
* READ_STEPS_PER_KEPT times as many stop the reads, which go out together.
|
|
51
|
+
*/
|
|
52
|
+
export class WalkReader {
|
|
53
|
+
constructor(files, chunkWithoutHeader = 65536) {
|
|
54
|
+
this.files = files;
|
|
55
|
+
this.chunkWithoutHeader = chunkWithoutHeader;
|
|
56
|
+
}
|
|
57
|
+
// The header, read once for every caller: one caller giving up stops only
|
|
58
|
+
// its own wait, and a read that fails is tried again by the next caller
|
|
59
|
+
header(opts = {}) {
|
|
60
|
+
if (!this.headerRead) {
|
|
61
|
+
const read = this.files.walks.getHeaderLines().then(lines => {
|
|
62
|
+
var _a;
|
|
63
|
+
const header = walkHeader(lines);
|
|
64
|
+
const chunk = (_a = header.chunk) !== null && _a !== void 0 ? _a : this.chunkWithoutHeader;
|
|
65
|
+
return {
|
|
66
|
+
...header,
|
|
67
|
+
chunk,
|
|
68
|
+
lookback: lookbackChunks(header.maxNode, chunk),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
read.catch(() => {
|
|
72
|
+
if (this.headerRead === read) {
|
|
73
|
+
this.headerRead = undefined;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
this.headerRead = read;
|
|
77
|
+
}
|
|
78
|
+
return raceAbort(this.headerRead, opts.signal);
|
|
79
|
+
}
|
|
80
|
+
// The reference sequences the walk file holds windows on
|
|
81
|
+
referenceSequences(opts = {}) {
|
|
82
|
+
return this.files.walks.getReferenceSequenceNames(opts);
|
|
83
|
+
}
|
|
84
|
+
async queryStart(start, opts = {}) {
|
|
85
|
+
const { chunk, lookback } = await this.header(opts);
|
|
86
|
+
return chunkQueryStart(start, chunk, lookback);
|
|
87
|
+
}
|
|
88
|
+
async cut(refName, region, opts = {}) {
|
|
89
|
+
const { signal, keep, context = WALK_CONTEXT, stepBudget = Infinity, byteBudget = Infinity, } = opts;
|
|
90
|
+
const { chunk, lookback, format } = await this.header({ signal });
|
|
91
|
+
// format 2 holds the whole cut in its walk file
|
|
92
|
+
const format2 = format === 2;
|
|
93
|
+
const nodeFile = this.files.nodes;
|
|
94
|
+
const linkFile = opts.links === false ? undefined : this.files.links;
|
|
95
|
+
if (!format2 && !nodeFile) {
|
|
96
|
+
throw new Error('A format 1 walk file needs its node file: open it with `nodes`');
|
|
97
|
+
}
|
|
98
|
+
const links = format2 ? opts.links !== false : linkFile !== undefined;
|
|
99
|
+
const from = chunkQueryStart(region.start, chunk, lookback);
|
|
100
|
+
const files = format2
|
|
101
|
+
? [this.files.walks]
|
|
102
|
+
: [this.files.walks, nodeFile, ...(linkFile ? [linkFile] : [])];
|
|
103
|
+
const budgeted = Number.isFinite(byteBudget) &&
|
|
104
|
+
files.every(file => file.bytesForRegions !== undefined);
|
|
105
|
+
const bytesFor = async (spans) => {
|
|
106
|
+
if (!budgeted) {
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
const sizes = await Promise.all(files.map(file => file.bytesForRegions(spans.map(span => ({ refName, ...span })), { signal })));
|
|
110
|
+
return sizes.reduce((sum, bytes) => sum + bytes, 0);
|
|
111
|
+
};
|
|
112
|
+
const tooLarge = budgeted
|
|
113
|
+
? await byteBudgetError(async (end) => {
|
|
114
|
+
checkRange(refName, from, end);
|
|
115
|
+
return bytesFor([{ start: from, end }]);
|
|
116
|
+
}, byteBudget, region, chunk, opts.budgetNames)
|
|
117
|
+
: undefined;
|
|
118
|
+
if (tooLarge) {
|
|
119
|
+
throw tooLarge;
|
|
120
|
+
}
|
|
121
|
+
const reads = new AbortController();
|
|
122
|
+
const stop = () => {
|
|
123
|
+
reads.abort(signal === null || signal === void 0 ? void 0 : signal.reason);
|
|
124
|
+
};
|
|
125
|
+
signal === null || signal === void 0 ? void 0 : signal.throwIfAborted();
|
|
126
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', stop, { once: true });
|
|
127
|
+
const rows = [];
|
|
128
|
+
let cut;
|
|
129
|
+
const graph = new WalkGraph({
|
|
130
|
+
refName,
|
|
131
|
+
start: region.start,
|
|
132
|
+
end: region.end,
|
|
133
|
+
context,
|
|
134
|
+
sequences: opts.sequences,
|
|
135
|
+
});
|
|
136
|
+
const read = (file, lineCallback, start = from, end = region.end) => getLines(file, refName, start, end, {
|
|
137
|
+
signal: reads.signal,
|
|
138
|
+
lineCallback,
|
|
139
|
+
});
|
|
140
|
+
const addRow = (line) => {
|
|
141
|
+
if (keep === undefined || keep(walkRowName(line))) {
|
|
142
|
+
rows.push(parseWalkRow(line));
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const addNode = (line) => {
|
|
146
|
+
graph.addNode(line);
|
|
147
|
+
};
|
|
148
|
+
const addLink = (line) => {
|
|
149
|
+
graph.addLink(line);
|
|
150
|
+
};
|
|
151
|
+
const decoded = new Set();
|
|
152
|
+
const ends = [];
|
|
153
|
+
// Format 2: each chunk read is decoded once, into the rows, nodes and
|
|
154
|
+
// links format 1 would have filed, and the links between pieces read
|
|
155
|
+
const readChunks = async (start = from, end = region.end) => {
|
|
156
|
+
const byChunk = new Map();
|
|
157
|
+
await read(this.files.walks, line => {
|
|
158
|
+
const cols = line.split('\t');
|
|
159
|
+
const cs = +cols[1];
|
|
160
|
+
const held = byChunk.get(cs);
|
|
161
|
+
if (held) {
|
|
162
|
+
held.push(cols);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
byChunk.set(cs, [cols]);
|
|
166
|
+
}
|
|
167
|
+
}, start, end);
|
|
168
|
+
for (const [cs, chunkRows] of byChunk) {
|
|
169
|
+
if (decoded.has(cs)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
decoded.add(cs);
|
|
173
|
+
const chunkCut = decodeChunk(chunkRows, keep);
|
|
174
|
+
rows.push(...chunkCut.rows);
|
|
175
|
+
for (const [id, node] of chunkCut.nodes) {
|
|
176
|
+
graph.placeNode(id, node);
|
|
177
|
+
}
|
|
178
|
+
graph.addFormat2Chunk(chunkCut.chunk);
|
|
179
|
+
ends.push(...chunkCut.ends);
|
|
180
|
+
}
|
|
181
|
+
if (links) {
|
|
182
|
+
for (const [a, b] of pieceLinks(ends)) {
|
|
183
|
+
graph.addLinkEnds(a, b);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const checkSteps = () => {
|
|
188
|
+
const error = stepBudgetError(rows, stepBudget, READ_STEPS_PER_KEPT, region, chunk, keep !== undefined, opts.budgetNames);
|
|
189
|
+
if (error) {
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
try {
|
|
194
|
+
await (format2
|
|
195
|
+
? readChunks().then(checkSteps)
|
|
196
|
+
: Promise.all([
|
|
197
|
+
read(this.files.walks, addRow).then(checkSteps),
|
|
198
|
+
read(nodeFile, addNode),
|
|
199
|
+
...(linkFile ? [read(linkFile, addLink)] : []),
|
|
200
|
+
]));
|
|
201
|
+
// the chunks read, first to last, which a follow widens to the
|
|
202
|
+
// farthest chunk a cut-off walk goes on in, so the reference between
|
|
203
|
+
// is read too
|
|
204
|
+
let first = from;
|
|
205
|
+
let last = Math.floor((region.end - 1) / chunk) * chunk;
|
|
206
|
+
const reach = FOLLOW_CHUNKS * chunk;
|
|
207
|
+
const [lo, hi] = [first - reach, last + reach];
|
|
208
|
+
cut = walkCut(joinPieces(rows), graph.nodes, refName);
|
|
209
|
+
let spent = await bytesFor([{ start: from, end: region.end }]);
|
|
210
|
+
for (let round = 0; round < FOLLOW_ROUNDS; round++) {
|
|
211
|
+
const starts = cut.open
|
|
212
|
+
.filter(c => c.refName === refName && c.start >= lo && c.start <= hi)
|
|
213
|
+
.map(c => c.start);
|
|
214
|
+
const spans = [
|
|
215
|
+
{ start: Math.min(first, ...starts), end: first },
|
|
216
|
+
{ start: last + chunk, end: Math.max(last, ...starts) + 1 },
|
|
217
|
+
].filter(span => span.end > span.start);
|
|
218
|
+
if (spans.length === 0) {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
spent += await bytesFor(spans);
|
|
222
|
+
if (spent > byteBudget) {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
first = Math.min(first, ...starts);
|
|
226
|
+
last = Math.max(last, ...starts);
|
|
227
|
+
await Promise.all(spans.flatMap(({ start, end }) => format2
|
|
228
|
+
? [readChunks(start, end)]
|
|
229
|
+
: [
|
|
230
|
+
read(this.files.walks, addRow, start, end),
|
|
231
|
+
read(nodeFile, addNode, start, end),
|
|
232
|
+
...(linkFile ? [read(linkFile, addLink, start, end)] : []),
|
|
233
|
+
]));
|
|
234
|
+
cut = walkCut(joinPieces(rows), graph.nodes, refName);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
reads.abort();
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', stop);
|
|
243
|
+
}
|
|
244
|
+
const tooMany = keptStepsError(cut.fragments, stepBudget, region, keep !== undefined);
|
|
245
|
+
if (tooMany) {
|
|
246
|
+
throw tooMany;
|
|
247
|
+
}
|
|
248
|
+
return { graph, ...cut };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { RegionTooLargeError } from './util.ts';
|
|
2
|
+
import type { GraphTables } from './util.ts';
|
|
3
|
+
import type { Format2Chunk } from './walkFormat2.ts';
|
|
4
|
+
export interface WalkRow {
|
|
5
|
+
name: string;
|
|
6
|
+
fragStart: number;
|
|
7
|
+
hapOffset: number;
|
|
8
|
+
piece: number;
|
|
9
|
+
n: number;
|
|
10
|
+
enc: string;
|
|
11
|
+
steps?: {
|
|
12
|
+
ids: Int32Array;
|
|
13
|
+
rev: Uint8Array;
|
|
14
|
+
};
|
|
15
|
+
chunkStart: number;
|
|
16
|
+
prev?: WalkChunk;
|
|
17
|
+
next?: WalkChunk;
|
|
18
|
+
}
|
|
19
|
+
export interface WalkChunk {
|
|
20
|
+
refName: string;
|
|
21
|
+
start: number;
|
|
22
|
+
}
|
|
23
|
+
export interface WalkNode {
|
|
24
|
+
refName: string;
|
|
25
|
+
start: number;
|
|
26
|
+
end: number;
|
|
27
|
+
rank: number;
|
|
28
|
+
onWindow: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface WalkWindow {
|
|
31
|
+
refName: string;
|
|
32
|
+
start: number;
|
|
33
|
+
end: number;
|
|
34
|
+
context: number;
|
|
35
|
+
sequences?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function walkRowName(line: string): string;
|
|
38
|
+
export declare function parseWalkRow(line: string): WalkRow;
|
|
39
|
+
export interface WalkHeader {
|
|
40
|
+
format?: number;
|
|
41
|
+
chunk?: number;
|
|
42
|
+
maxNode?: number;
|
|
43
|
+
references: string[];
|
|
44
|
+
haplotypes: string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* What a walk file's header states: its chunk size in a
|
|
48
|
+
* `#walks\tchunk:i:65536` line, from gfa-to-tabix 0.4.0 a
|
|
49
|
+
* `#reference\tGRCh38` line per reference sample and a
|
|
50
|
+
* `#haplotype\tHG002#1` line per other haplotype with rows, and from 0.5.0
|
|
51
|
+
* the longest node in bp as `maxnode:i:1024` beside the chunk size
|
|
52
|
+
*/
|
|
53
|
+
export declare function walkHeader(lines: string[]): WalkHeader;
|
|
54
|
+
/**
|
|
55
|
+
* How many chunks before the window's own a cut reads. A node is filed under
|
|
56
|
+
* the chunk holding its start, so a node `maxNode` bp long reaching into the
|
|
57
|
+
* window starts at most ceil(maxNode / chunk) chunks back. At least one, since
|
|
58
|
+
* the chunk before carries the detour a haplotype takes into the window's
|
|
59
|
+
* first reference node; one when the header gives no `maxnode:i:`, which holds
|
|
60
|
+
* for vg's graphs, chopped to nodes of at most 1,024 bp.
|
|
61
|
+
*/
|
|
62
|
+
export declare function lookbackChunks(maxNode: number | undefined, chunk: number): number;
|
|
63
|
+
export declare function chunkQueryStart(start: number, chunk: number, lookback?: number): number;
|
|
64
|
+
export declare function walkNameFilter(wanted: string[] | undefined, referenceHaplotype: string | undefined): ((name: string) => boolean) | undefined;
|
|
65
|
+
export declare function decodeSteps(enc: string, n: number, ids?: Int32Array<ArrayBuffer>, rev?: Uint8Array<ArrayBuffer>, at?: number): {
|
|
66
|
+
ids: Int32Array<ArrayBuffer>;
|
|
67
|
+
rev: Uint8Array<ArrayBuffer>;
|
|
68
|
+
};
|
|
69
|
+
export interface WalkRun {
|
|
70
|
+
name: string;
|
|
71
|
+
hapOffset: number;
|
|
72
|
+
ids: Int32Array;
|
|
73
|
+
rev: Uint8Array;
|
|
74
|
+
prev?: WalkChunk;
|
|
75
|
+
next?: WalkChunk;
|
|
76
|
+
}
|
|
77
|
+
export declare function joinPieces(rows: WalkRow[]): WalkRun[];
|
|
78
|
+
export interface WalkFragment {
|
|
79
|
+
name: string;
|
|
80
|
+
hapStart: number;
|
|
81
|
+
hapEnd: number;
|
|
82
|
+
ids: Int32Array;
|
|
83
|
+
rev: Uint8Array;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The cut a window draws from its walks: the reference inside the window plus
|
|
87
|
+
* its context, and each run from its first to its last step on that stretch.
|
|
88
|
+
* A run that leaves the reference there, through steps off it or across a
|
|
89
|
+
* deletion, is followed to the reference step where it rejoins, when the read
|
|
90
|
+
* holds it, and otherwise through the steps off the reference it holds: at
|
|
91
|
+
* the ABCA7 VNTR four haplotypes leave inside the repeat and rejoin 1.8 kb
|
|
92
|
+
* past the window, and cut at the context they measured as partial walks. The reference is cut out as far as those rejoins reach, and
|
|
93
|
+
* a run then runs on outward while the next step's node is already in the
|
|
94
|
+
* cut. Without the run-on a haplotype whose alternate allele straddles the
|
|
95
|
+
* edge stopped short of a node other walks had brought into the cut, and that
|
|
96
|
+
* node drew without it. The run-on adds no node, so one pass is enough, but
|
|
97
|
+
* it reaches through nodes other walks brought in, so with a window edge inside
|
|
98
|
+
* a collapsed array a row's length depends on which haplotypes are cut.
|
|
99
|
+
*/
|
|
100
|
+
export declare function walkCut(runs: WalkRun[], nodes: Map<number, WalkNode>, refName: string): {
|
|
101
|
+
kept: Set<number>;
|
|
102
|
+
fragments: WalkFragment[];
|
|
103
|
+
open: WalkChunk[];
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* The node and link rows a walk cut reads, parsed as they arrive, and the
|
|
107
|
+
* tables of the cut they make with its walks. A node filed under two chunks
|
|
108
|
+
* arrives twice and is read once; columns past a node row's eighth (LN:i:,
|
|
109
|
+
* SQ:Z: and whatever follows) are read only for the bases a window asks for.
|
|
110
|
+
*/
|
|
111
|
+
export declare class WalkGraph {
|
|
112
|
+
readonly nodes: Map<number, WalkNode>;
|
|
113
|
+
readonly sequences: Map<number, string>;
|
|
114
|
+
private links;
|
|
115
|
+
private readonly format2;
|
|
116
|
+
private readonly t;
|
|
117
|
+
private readonly window;
|
|
118
|
+
constructor(window: WalkWindow);
|
|
119
|
+
addNode(line: string): void;
|
|
120
|
+
placeNode(id: number, node: Omit<WalkNode, 'onWindow'>): void;
|
|
121
|
+
addLinkEnds(source: number, target: number): void;
|
|
122
|
+
addFormat2Chunk(chunk: Format2Chunk): void;
|
|
123
|
+
addLink(line: string): void;
|
|
124
|
+
/**
|
|
125
|
+
* The cut as typed arrays, which the worker transfers rather than copies:
|
|
126
|
+
* nodes by reference position, each link once in the order its L line
|
|
127
|
+
* sorts, then a walk per fragment, the reference's first and the rest by
|
|
128
|
+
* name. graphTablesGFA writes them as the GFA they stand for, the same bytes
|
|
129
|
+
* for the same rows.
|
|
130
|
+
*/
|
|
131
|
+
tables(kept: Set<number>, fragments: WalkFragment[]): GraphTables;
|
|
132
|
+
private referenceFirst;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The zoom-in notice for a cut whose walk rows hold more steps than
|
|
136
|
+
* `readPerKept` times `stepBudget`, or undefined when they fit. A window reads whole chunks, those before it
|
|
137
|
+
* included, so the span that fits is counted in chunks from the window's
|
|
138
|
+
* start; when the chunks a window there always reads are over on their own,
|
|
139
|
+
* no zoom fits and the notice asks for fewer haplotypes. A cut for every
|
|
140
|
+
* haplotype is also offered fewer where the reference and one haplotype, at
|
|
141
|
+
* the walks' average steps, would fit the whole window.
|
|
142
|
+
*/
|
|
143
|
+
export interface BudgetNames {
|
|
144
|
+
steps: string;
|
|
145
|
+
bytes: string;
|
|
146
|
+
}
|
|
147
|
+
export declare const BUDGET_NAMES: BudgetNames;
|
|
148
|
+
export declare function stepBudgetError(rows: WalkRow[], stepBudget: number, readPerKept: number, window: {
|
|
149
|
+
start: number;
|
|
150
|
+
end: number;
|
|
151
|
+
}, chunk: number, filtered: boolean, names?: BudgetNames): RegionTooLargeError | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* The zoom-in notice for a cut that keeps more steps than `budget`, or
|
|
154
|
+
* undefined when it fits. The span that fits is the window scaled by how far
|
|
155
|
+
* over the cut is.
|
|
156
|
+
*/
|
|
157
|
+
export declare function keptStepsError(fragments: WalkFragment[], budget: number, window: {
|
|
158
|
+
start: number;
|
|
159
|
+
end: number;
|
|
160
|
+
}, filtered: boolean): RegionTooLargeError | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* The zoom-in notice for a cut whose three files would fetch more than
|
|
163
|
+
* `budget` compressed bytes, or undefined when they fit. `bytesTo(end)` is the
|
|
164
|
+
* indexes' estimate for a read from the window's first query base to `end`;
|
|
165
|
+
* the span that fits grows a chunk at a time from the chunk holding the
|
|
166
|
+
* window's start, and when that chunk and those before it are over on their
|
|
167
|
+
* own, no zoom fits.
|
|
168
|
+
*/
|
|
169
|
+
export declare function byteBudgetError(bytesTo: (end: number) => Promise<number>, budget: number, window: {
|
|
170
|
+
start: number;
|
|
171
|
+
end: number;
|
|
172
|
+
}, chunk: number, names?: BudgetNames): Promise<RegionTooLargeError | undefined>;
|