@jbrowse/add-track-core 5.0.0-beta.1 → 5.0.0-beta.11

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/README.md CHANGED
@@ -21,6 +21,12 @@ plugins loaded rather than from a list somebody has to remember to edit. A
21
21
  format the table cannot express is still added the old way, with
22
22
  `addAdapterGuesser` — those run later in the chain and win.
23
23
 
24
+ `indexCandidates.ts` beside the table holds the other filename question both
25
+ sides ask: the names an index file might be under — htslib's `.csi`, Picard and
26
+ GATK's `reads.bai` beside `reads.bam`. Core probes those candidates over the
27
+ network in `detectIndexLocation`, the CLI with `existsSync` in `siblingSidecar`,
28
+ and neither spells the list itself.
29
+
24
30
  `products/jbrowse-web/src/addTrackFormats.test.ts` is the check over all of it:
25
31
  one filename per entry, compared between the app's guesser chain and the CLI.
26
32
 
package/esm/formats.d.ts CHANGED
@@ -60,6 +60,12 @@ export interface FormatEntry {
60
60
  * precede their plain counterparts.
61
61
  */
62
62
  export declare const formats: FormatEntry[];
63
+ /**
64
+ * The bare filename the format table matches against — the same thing
65
+ * `@jbrowse/core`'s `getFileName` hands the guesser chain, so a path and a URL
66
+ * with a presigned query string both reduce to what the regexes expect.
67
+ */
68
+ export declare function fileNameOf(location: string): string;
63
69
  /**
64
70
  * The format `fileName` is, or — when `adapterHint` names an adapter type — that
65
71
  * adapter's format whatever the filename says. A hint always wins, which is what
@@ -72,8 +78,3 @@ export declare function matchFormat(fileName: string, adapterHint?: string): For
72
78
  * adapter — a `.bedmethyl.gz` from a plain `.bed.gz` — so pass it when known.
73
79
  */
74
80
  export declare function trackTypeForAdapter(adapterType: string, fileName?: string): string | undefined;
75
- /**
76
- * The index spelling a location implies: htslib writes `.csi` in place of a
77
- * `.bai` or a `.tbi` for a reference over 512 Mb, and on request at any size.
78
- */
79
- export declare function resolveIndexType(indexName: string | undefined, fallback: 'BAI' | 'TBI'): "BAI" | "CSI" | "TBI";
package/esm/formats.js CHANGED
@@ -153,6 +153,26 @@ export const formats = [
153
153
  indexType: 'TBI',
154
154
  },
155
155
  },
156
+ {
157
+ // `.bedGraph` is UCSC's own spelling and what bedGraphToBigWig is pointed
158
+ // at, so it turns up far more often than the `.bg` short form below
159
+ regex: /\.bedgraph\.b?gz$/i,
160
+ spec: {
161
+ kind: 'indexed',
162
+ adapterType: 'BedGraphTabixAdapter',
163
+ locField: 'bedGraphGzLocation',
164
+ suffix: '.tbi',
165
+ indexType: 'TBI',
166
+ },
167
+ },
168
+ {
169
+ regex: /\.bedgraph$/i,
170
+ spec: {
171
+ kind: 'single',
172
+ adapterType: 'BedGraphAdapter',
173
+ locField: 'bedGraphLocation',
174
+ },
175
+ },
156
176
  {
157
177
  regex: /\.bg\.b?gz$/i,
158
178
  spec: {
@@ -247,6 +267,18 @@ export const formats = [
247
267
  locField: 'twoBitLocation',
248
268
  },
249
269
  },
270
+ {
271
+ // the pattern `jbrowse add-assembly` has always inferred `--type
272
+ // chromSizes` from, so a file that names a genome on the command line names
273
+ // one in a config. The bare `.sizes` below stays refused: it is nobody's
274
+ // convention, and a genome with no sequence is too easy to reach by accident
275
+ regex: /\.chrom\.sizes$/i,
276
+ spec: {
277
+ kind: 'single',
278
+ adapterType: 'ChromSizesAdapter',
279
+ locField: 'chromSizesLocation',
280
+ },
281
+ },
250
282
  { regex: /\.sizes$/i, spec: { kind: 'unsupported' } },
251
283
  {
252
284
  regex: /trackData\.jsonz?$/i,
@@ -321,19 +353,19 @@ export const formats = [
321
353
  },
322
354
  },
323
355
  {
324
- // an all-vs-all PAF looks like any `.paf`; the assembly list and the PanSN
356
+ // a multi-genome PAF looks like any `.paf`; the assembly list and the PanSN
325
357
  // mapping come from its add-track form
326
358
  spec: {
327
359
  kind: 'single',
328
- adapterType: 'AllVsAllPAFAdapter',
360
+ adapterType: 'MultiGenomePAFAdapter',
329
361
  locField: 'pafLocation',
330
362
  },
331
363
  },
332
364
  {
333
- // likewise an all-vs-all PIF against a pairwise one
365
+ // likewise a multi-genome PIF against a pairwise one
334
366
  spec: {
335
367
  kind: 'indexed',
336
- adapterType: 'AllVsAllIndexedPAFAdapter',
368
+ adapterType: 'MultiGenomeIndexedPAFAdapter',
337
369
  locField: 'pifGzLocation',
338
370
  suffix: '.tbi',
339
371
  indexType: 'TBI',
@@ -354,6 +386,14 @@ export const formats = [
354
386
  },
355
387
  },
356
388
  ];
389
+ /**
390
+ * The bare filename the format table matches against — the same thing
391
+ * `@jbrowse/core`'s `getFileName` hands the guesser chain, so a path and a URL
392
+ * with a presigned query string both reduce to what the regexes expect.
393
+ */
394
+ export function fileNameOf(location) {
395
+ return (location.replaceAll('\\', '/').split('/').at(-1)?.split(/[?#]/)[0] ?? '');
396
+ }
357
397
  function matchesRegex(fileName, regex) {
358
398
  return Array.isArray(regex)
359
399
  ? regex.every(r => r.test(fileName))
@@ -382,10 +422,3 @@ export function trackTypeForAdapter(adapterType, fileName) {
382
422
  ? entry.trackType
383
423
  : adapterTypesToTrackTypeMap[adapterType];
384
424
  }
385
- /**
386
- * The index spelling a location implies: htslib writes `.csi` in place of a
387
- * `.bai` or a `.tbi` for a reference over 512 Mb, and on request at any size.
388
- */
389
- export function resolveIndexType(indexName, fallback) {
390
- return indexName?.toUpperCase().endsWith('CSI') ? 'CSI' : fallback;
391
- }
package/esm/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
- export { type AdapterSpec, type FormatEntry, type Sidecar, formats, matchFormat, resolveIndexType, trackTypeForAdapter, } from './formats.ts';
1
+ export { type AdapterSpec, type FormatEntry, type Sidecar, fileNameOf, formats, matchFormat, trackTypeForAdapter, } from './formats.ts';
2
+ export { indexCandidateNames, indexSpellings, resolveIndexType, sidecarCandidateNames, } from './indexCandidates.ts';
3
+ export { type LooseTrackInput, isLooseTrackConfig } from './looseTrackConfig.ts';
2
4
  export { adapterTypesToTrackTypeMap } from './trackTypes.generated.ts';
package/esm/index.js CHANGED
@@ -1,2 +1,4 @@
1
- export { formats, matchFormat, resolveIndexType, trackTypeForAdapter, } from './formats.js';
1
+ export { fileNameOf, formats, matchFormat, trackTypeForAdapter, } from './formats.js';
2
+ export { indexCandidateNames, indexSpellings, resolveIndexType, sidecarCandidateNames, } from './indexCandidates.js';
3
+ export { isLooseTrackConfig } from './looseTrackConfig.js';
2
4
  export { adapterTypesToTrackTypeMap } from './trackTypes.generated.js';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Every conventional spelling of an index file, for the docs that describe this
3
+ * behavior — the prose form of the list {@link indexCandidateNames} builds.
4
+ */
5
+ export declare const indexSpellings: {
6
+ name: string;
7
+ writtenBy: string;
8
+ }[];
9
+ /**
10
+ * The names a sidecar with this suffix might be under beside `location`, best
11
+ * guess first — the conventional one, then htslib's `.csi`, then the stripped
12
+ * form Picard and GATK write.
13
+ *
14
+ * The guard on the stripped spelling is load-bearing: `replace` hands back the
15
+ * subject unchanged when the pattern does not match, so a name carrying no
16
+ * extension at all would offer the data file as its own index.
17
+ */
18
+ export declare function sidecarCandidateNames(location: string, suffix: string): [string, ...string[]];
19
+ /**
20
+ * The index filenames worth looking for beside `fileName`, best guess first.
21
+ *
22
+ * One data file has several names its index might carry, and picking only the
23
+ * first left everyone else with a missing-file error naming a path they never
24
+ * wrote. Empty for a file type that carries no sibling index (BigWig, BigBed,
25
+ * hic), which is how a caller knows not to go looking.
26
+ */
27
+ export declare function indexCandidateNames(fileName: string): string[];
28
+ /**
29
+ * The index spelling a location implies: htslib writes `.csi` in place of a
30
+ * `.bai` or a `.tbi` for a reference over 512 Mb, and on request at any size.
31
+ */
32
+ export declare function resolveIndexType(indexName: string | undefined, fallback: 'BAI' | 'TBI'): "BAI" | "CSI" | "TBI";
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Every conventional spelling of an index file, for the docs that describe this
3
+ * behavior — the prose form of the list {@link indexCandidateNames} builds.
4
+ */
5
+ export const indexSpellings = [
6
+ {
7
+ name: '`<file>.tbi`, `<file>.bai`, `<file>.crai`',
8
+ writtenBy: 'samtools, tabix',
9
+ },
10
+ {
11
+ name: '`<file>.csi`',
12
+ writtenBy: 'htslib, for a reference over 512 Mb and on request at any size',
13
+ },
14
+ {
15
+ name: '`reads.bai` beside `reads.bam`',
16
+ writtenBy: 'Picard, GATK',
17
+ },
18
+ ];
19
+ // Per-suffix because the alternatives are: a `.crai` has no CSI form, and
20
+ // stripping the extension off `calls.vcf.gz` would name `calls.vcf.tbi`, which
21
+ // nothing writes. A `.fai`/`.gzi` has neither and so is absent here.
22
+ const ALTERNATE_SPELLINGS = {
23
+ '.bai': { csi: true, stripped: true },
24
+ '.crai': { stripped: true },
25
+ '.tbi': { csi: true },
26
+ };
27
+ /**
28
+ * The names a sidecar with this suffix might be under beside `location`, best
29
+ * guess first — the conventional one, then htslib's `.csi`, then the stripped
30
+ * form Picard and GATK write.
31
+ *
32
+ * The guard on the stripped spelling is load-bearing: `replace` hands back the
33
+ * subject unchanged when the pattern does not match, so a name carrying no
34
+ * extension at all would offer the data file as its own index.
35
+ */
36
+ export function sidecarCandidateNames(location, suffix) {
37
+ const { csi, stripped } = ALTERNATE_SPELLINGS[suffix] ?? {};
38
+ const strippedName = location.replace(/\.[^./\\]+$/, suffix);
39
+ return [
40
+ `${location}${suffix}`,
41
+ ...(csi ? [`${location}.csi`] : []),
42
+ ...(stripped && strippedName !== location ? [strippedName] : []),
43
+ ];
44
+ }
45
+ // The tabix family is spelled by its compression, not its content: a `.gz` here
46
+ // is a bgzipped VCF/GFF/BED/SAM, all of which index the same two ways, and
47
+ // `.bgz` is the same file under the name htslib's own tools give it.
48
+ const INDEX_SUFFIXES = [
49
+ [/\.bam$/i, '.bai'],
50
+ [/\.cram$/i, '.crai'],
51
+ [/\.b?gz$/i, '.tbi'],
52
+ ];
53
+ /**
54
+ * The index filenames worth looking for beside `fileName`, best guess first.
55
+ *
56
+ * One data file has several names its index might carry, and picking only the
57
+ * first left everyone else with a missing-file error naming a path they never
58
+ * wrote. Empty for a file type that carries no sibling index (BigWig, BigBed,
59
+ * hic), which is how a caller knows not to go looking.
60
+ */
61
+ export function indexCandidateNames(fileName) {
62
+ const suffix = INDEX_SUFFIXES.find(([re]) => re.test(fileName))?.[1];
63
+ return suffix ? sidecarCandidateNames(fileName, suffix) : [];
64
+ }
65
+ /**
66
+ * The index spelling a location implies: htslib writes `.csi` in place of a
67
+ * `.bai` or a `.tbi` for a reference over 512 Mb, and on request at any size.
68
+ */
69
+ export function resolveIndexType(indexName, fallback) {
70
+ return indexName?.toUpperCase().endsWith('CSI') ? 'CSI' : fallback;
71
+ }
@@ -0,0 +1,13 @@
1
+ export interface LooseTrackInput {
2
+ uri: string;
3
+ index?: string;
4
+ baseUri?: string;
5
+ [key: string]: unknown;
6
+ }
7
+ /**
8
+ * A track written as a data file with no `adapter`, the form
9
+ * `expandLooseTrackConfig` expands.
10
+ */
11
+ export declare function isLooseTrackConfig(snap: unknown): snap is LooseTrackInput & {
12
+ trackId?: string;
13
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A track written as a data file with no `adapter`, the form
3
+ * `expandLooseTrackConfig` expands.
4
+ */
5
+ export function isLooseTrackConfig(snap) {
6
+ return (typeof snap === 'object' &&
7
+ snap !== null &&
8
+ !Array.isArray(snap) &&
9
+ typeof snap.uri === 'string' &&
10
+ !('adapter' in snap));
11
+ }
@@ -6,8 +6,6 @@
6
6
  * fall back to `FeatureTrack`.
7
7
  */
8
8
  export const adapterTypesToTrackTypeMap = {
9
- AllVsAllIndexedPAFAdapter: 'SyntenyTrack',
10
- AllVsAllPAFAdapter: 'SyntenyTrack',
11
9
  BamAdapter: 'AlignmentsTrack',
12
10
  BedAdapter: 'FeatureTrack',
13
11
  BedGraphAdapter: 'QuantitativeTrack',
@@ -29,7 +27,7 @@ export const adapterTypesToTrackTypeMap = {
29
27
  FromConfigAdapter: 'FeatureTrack',
30
28
  FromConfigRegionsAdapter: 'ReferenceSequenceTrack',
31
29
  FromConfigSequenceAdapter: 'ReferenceSequenceTrack',
32
- GCContentAdapter: 'QuantitativeTrack',
30
+ GCContentAdapter: 'GCContentTrack',
33
31
  Gff3Adapter: 'FeatureTrack',
34
32
  Gff3TabixAdapter: 'FeatureTrack',
35
33
  GtfAdapter: 'FeatureTrack',
@@ -43,6 +41,9 @@ export const adapterTypesToTrackTypeMap = {
43
41
  MCScanAnchorsAdapter: 'SyntenyTrack',
44
42
  MCScanBlocksAdapter: 'SyntenyTrack',
45
43
  MCScanSimpleAnchorsAdapter: 'SyntenyTrack',
44
+ MultiGenomeIndexedPAFAdapter: 'SyntenyTrack',
45
+ MultiGenomePAFAdapter: 'SyntenyTrack',
46
+ MultiPairwiseSyntenyAdapter: 'SyntenyTrack',
46
47
  MultiWiggleAdapter: 'MultiQuantitativeTrack',
47
48
  NCListAdapter: 'FeatureTrack',
48
49
  PAFAdapter: 'SyntenyTrack',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/add-track-core",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0-beta.11",
4
4
  "type": "module",
5
5
  "description": "The file-format table the add-track guessers and the CLI both read — no framework deps",
6
6
  "keywords": [