@jbrowse/cli 4.3.0 → 5.0.0-beta.2
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 +413 -57
- package/bin/run +1 -1
- package/bundle/index.js +550 -2659
- package/dist/base.js +0 -3
- package/dist/bin.js +2 -1
- package/dist/cliFetch.js +3 -0
- package/dist/commands/add-assembly/index.js +46 -19
- package/dist/commands/add-assembly/utils.js +204 -290
- package/dist/commands/add-connection.js +73 -79
- package/dist/commands/add-track-json.js +24 -12
- package/dist/commands/add-track-utils/adapter-utils.js +222 -288
- package/dist/commands/add-track-utils/file-operations.js +27 -11
- package/dist/commands/add-track-utils/multiwig.js +47 -0
- package/dist/commands/add-track-utils/track-config.js +68 -25
- package/dist/commands/add-track-utils/validators.js +36 -20
- package/dist/commands/add-track.js +201 -62
- package/dist/commands/admin-server/index.js +30 -49
- package/dist/commands/admin-server/utils.js +235 -125
- package/dist/commands/create.js +18 -34
- package/dist/commands/make-density/density-generator.js +180 -0
- package/dist/commands/make-density/index.js +139 -0
- package/dist/commands/make-pif/index.js +128 -11
- package/dist/commands/make-pif/pif-generator.js +249 -43
- package/dist/commands/process-utils.js +13 -2
- package/dist/commands/remove-track.js +20 -13
- package/dist/commands/set-default-session.js +35 -31
- package/dist/commands/shared/config-operations.js +3 -10
- package/dist/commands/shared/density.js +12 -0
- package/dist/commands/shared/sort-utils.js +67 -10
- package/dist/commands/shared/validators.js +38 -3
- package/dist/commands/sort-bed.js +2 -33
- package/dist/commands/sort-gff.js +2 -33
- package/dist/commands/text-index/adapter-utils.js +6 -23
- package/dist/commands/text-index/aggregate.js +40 -34
- package/dist/commands/text-index/command.js +35 -7
- package/dist/commands/text-index/config-utils.js +60 -83
- package/dist/commands/text-index/file-list.js +32 -18
- package/dist/commands/text-index/index.js +4 -4
- package/dist/commands/text-index/indexing-utils.js +49 -73
- package/dist/commands/text-index/per-track.js +44 -31
- package/dist/commands/text-index/validators.js +1 -1
- package/dist/commands/upgrade.js +20 -32
- package/dist/commands/validate/configManifest.generated.js +5765 -0
- package/dist/commands/validate/displayDefaultKeys.js +28 -0
- package/dist/commands/validate/index.js +97 -0
- package/dist/commands/validate/suggest.js +35 -0
- package/dist/commands/validate/types.js +6 -0
- package/dist/commands/validate/validateConfig.js +522 -0
- package/dist/index.js +104 -50
- package/dist/types/common.js +3 -27
- package/dist/utils.js +309 -128
- package/dist/version.js +1 -1
- package/package.json +8 -10
- package/dist/commands/make-pif/cigar-utils.js +0 -24
- package/dist/commands/make-pif/file-utils.js +0 -30
- package/dist/commands/track-utils.js +0 -74
- package/dist/util.js +0 -35
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { configManifest } from './configManifest.generated.js';
|
|
2
|
+
// `displayDefaults: {color: 'green'}` is a track-level shorthand
|
|
3
|
+
// (expandTrackConfigShorthand.ts) routing each key to whichever of the track's
|
|
4
|
+
// display types declares it, so the keys a track accepts are the union of those
|
|
5
|
+
// displays' slots rather than the track's own.
|
|
6
|
+
export function displayDefaultKeys(trackEntry, manifest) {
|
|
7
|
+
return (trackEntry.displayTypes ?? []).flatMap(name => manifest.displays[name]?.slots.map(slot => slot.name) ?? []);
|
|
8
|
+
}
|
|
9
|
+
// The same set for a track type named rather than resolved — add-track knows
|
|
10
|
+
// the type it is writing, not a manifest entry. The display types come back
|
|
11
|
+
// alongside, because most tracks offer exactly one and naming it is the whole
|
|
12
|
+
// difference between a message someone can act on and one they cannot: an
|
|
13
|
+
// AlignmentsTrack has only LinearAlignmentsDisplay, so "no AlignmentsTrack
|
|
14
|
+
// display declares color" sends the reader looking for the other displays.
|
|
15
|
+
//
|
|
16
|
+
// Both empty for a type the manifest does not carry (a plugin's), which no
|
|
17
|
+
// check here can speak to.
|
|
18
|
+
export function displayDefaultsForTrackType(trackType, manifest = configManifest) {
|
|
19
|
+
const entry = lookup(manifest.tracks, trackType);
|
|
20
|
+
return {
|
|
21
|
+
displayTypes: entry?.displayTypes ?? [],
|
|
22
|
+
keys: entry ? displayDefaultKeys(entry, manifest) : [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function lookup(group, typeName) {
|
|
26
|
+
return (group[typeName] ??
|
|
27
|
+
Object.values(group).find(entry => entry.aliases?.includes(typeName)));
|
|
28
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import { printHelp, readConfigFile } from '../../utils.js';
|
|
3
|
+
import { validateConfig } from './validateConfig.js';
|
|
4
|
+
const description = 'Check a JBrowse configuration for errors, including the ones JBrowse itself accepts silently';
|
|
5
|
+
const notes = `A config key JBrowse does not recognize is ignored rather than reported, so a
|
|
6
|
+
misspelled slot leaves the track loading normally with the setting doing
|
|
7
|
+
nothing. That is what this command is mainly for.
|
|
8
|
+
|
|
9
|
+
Two levels are reported:
|
|
10
|
+
|
|
11
|
+
error JBrowse accepts it and silently does the wrong thing — an unknown
|
|
12
|
+
slot, a key a defaultSession view or display does not declare, a
|
|
13
|
+
track naming an assembly the config never defines, a defaultSession
|
|
14
|
+
naming a trackId that does not exist, a duplicate trackId. Exits 1.
|
|
15
|
+
|
|
16
|
+
warning JBrowse will complain by itself on load, or handles it — a type name
|
|
17
|
+
the core plugins do not register (expected if one of your plugins
|
|
18
|
+
registers it), or a legacy key a migration rewrites, such as a view
|
|
19
|
+
nesting its settings under "init". Never fails the run.
|
|
20
|
+
|
|
21
|
+
Types registered by plugins are not known to this command, so they come through
|
|
22
|
+
as warnings rather than errors.`;
|
|
23
|
+
const examples = [
|
|
24
|
+
'# check the config.json in the current directory',
|
|
25
|
+
'$ jbrowse validate',
|
|
26
|
+
'',
|
|
27
|
+
'# check a specific config or saved session',
|
|
28
|
+
'$ jbrowse validate /path/to/config.json',
|
|
29
|
+
'$ jbrowse validate mysession.jbrowse',
|
|
30
|
+
'',
|
|
31
|
+
'# machine-readable output',
|
|
32
|
+
'$ jbrowse validate config.json --json',
|
|
33
|
+
];
|
|
34
|
+
const options = {
|
|
35
|
+
json: {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
description: 'Output the findings as JSON instead of text',
|
|
38
|
+
},
|
|
39
|
+
quiet: {
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
short: 'q',
|
|
42
|
+
description: 'Only print errors, suppressing warnings and notes',
|
|
43
|
+
},
|
|
44
|
+
help: { type: 'boolean', short: 'h', description: 'Show help' },
|
|
45
|
+
};
|
|
46
|
+
function format({ level, where, message }) {
|
|
47
|
+
return where ? `${level}: ${where}: ${message}` : `${level}: ${message}`;
|
|
48
|
+
}
|
|
49
|
+
export async function run(args) {
|
|
50
|
+
const { values: flags, positionals } = parseArgs({
|
|
51
|
+
options,
|
|
52
|
+
args,
|
|
53
|
+
allowPositionals: true,
|
|
54
|
+
});
|
|
55
|
+
if (flags.help) {
|
|
56
|
+
printHelp({
|
|
57
|
+
description,
|
|
58
|
+
examples,
|
|
59
|
+
notes,
|
|
60
|
+
usage: 'jbrowse validate [config.json] [options]',
|
|
61
|
+
options,
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const file = positionals[0] ?? 'config.json';
|
|
66
|
+
// readConfigFile, not readJsonFile: running from the wrong directory is the
|
|
67
|
+
// most common way this fails, and it says which path it looked at.
|
|
68
|
+
const config = await readConfigFile(file);
|
|
69
|
+
const result = validateConfig(config);
|
|
70
|
+
if (flags.json) {
|
|
71
|
+
console.log(JSON.stringify({ file, ok: result.errorCount === 0, ...result }, null, 2));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const shown = flags.quiet
|
|
75
|
+
? result.problems.filter(p => p.level === 'error')
|
|
76
|
+
: result.problems;
|
|
77
|
+
if (!flags.quiet) {
|
|
78
|
+
for (const note of result.notes) {
|
|
79
|
+
console.log(`note: ${note}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const problem of shown) {
|
|
83
|
+
console.log(format(problem));
|
|
84
|
+
}
|
|
85
|
+
if (result.problems.length === 0) {
|
|
86
|
+
console.log(`${file} looks good`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(`\n${result.errorCount} error(s), ${result.warningCount} warning(s) in ${file}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (result.errorCount > 0) {
|
|
93
|
+
// Non-zero so this can gate a deploy. Thrown rather than process.exit so it
|
|
94
|
+
// goes through the same error path as every other command.
|
|
95
|
+
throw new Error(`${result.errorCount} error(s) found in ${file}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// "did you mean" for a mistyped identifier, shared by the validator and by
|
|
2
|
+
// add-track's up-front check on the settings it is about to write.
|
|
3
|
+
function editDistance(a, b) {
|
|
4
|
+
// Two rolling rows rather than the full matrix; these are short identifiers
|
|
5
|
+
// and this runs once per unknown key.
|
|
6
|
+
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
7
|
+
for (let i = 1; i <= a.length; i++) {
|
|
8
|
+
const row = [i];
|
|
9
|
+
for (let j = 1; j <= b.length; j++) {
|
|
10
|
+
row[j] = Math.min(prev[j] + 1, row[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
11
|
+
}
|
|
12
|
+
prev = row;
|
|
13
|
+
}
|
|
14
|
+
return prev[b.length];
|
|
15
|
+
}
|
|
16
|
+
// The nearest candidate, if it is near enough to be worth naming. The threshold
|
|
17
|
+
// scales with length so `uri` doesn't match every other three-letter slot while
|
|
18
|
+
// a longer typo still resolves.
|
|
19
|
+
export function suggest(word, candidates) {
|
|
20
|
+
const limit = Math.max(2, Math.floor(word.length / 3));
|
|
21
|
+
let best;
|
|
22
|
+
let bestScore = Infinity;
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
const score = editDistance(word.toLowerCase(), candidate.toLowerCase());
|
|
25
|
+
if (score < bestScore) {
|
|
26
|
+
bestScore = score;
|
|
27
|
+
best = candidate;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return bestScore <= limit ? best : undefined;
|
|
31
|
+
}
|
|
32
|
+
export function didYouMean(word, candidates) {
|
|
33
|
+
const hit = suggest(word, candidates);
|
|
34
|
+
return hit ? ` — did you mean "${hit}"?` : '';
|
|
35
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Shape of the generated config manifest (configManifest.generated.ts), which
|
|
2
|
+
// scripts/generateConfigManifest.ts reads out of the live ConfigurationSchema
|
|
3
|
+
// objects. Declared here rather than inferred from the generated file so the
|
|
4
|
+
// generator and the validator agree on a contract, and a generator change that
|
|
5
|
+
// drops a field is a type error rather than a silent behaviour change.
|
|
6
|
+
export {};
|