@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.
Files changed (57) hide show
  1. package/README.md +413 -57
  2. package/bin/run +1 -1
  3. package/bundle/index.js +550 -2659
  4. package/dist/base.js +0 -3
  5. package/dist/bin.js +2 -1
  6. package/dist/cliFetch.js +3 -0
  7. package/dist/commands/add-assembly/index.js +46 -19
  8. package/dist/commands/add-assembly/utils.js +204 -290
  9. package/dist/commands/add-connection.js +73 -79
  10. package/dist/commands/add-track-json.js +24 -12
  11. package/dist/commands/add-track-utils/adapter-utils.js +222 -288
  12. package/dist/commands/add-track-utils/file-operations.js +27 -11
  13. package/dist/commands/add-track-utils/multiwig.js +47 -0
  14. package/dist/commands/add-track-utils/track-config.js +68 -25
  15. package/dist/commands/add-track-utils/validators.js +36 -20
  16. package/dist/commands/add-track.js +201 -62
  17. package/dist/commands/admin-server/index.js +30 -49
  18. package/dist/commands/admin-server/utils.js +235 -125
  19. package/dist/commands/create.js +18 -34
  20. package/dist/commands/make-density/density-generator.js +180 -0
  21. package/dist/commands/make-density/index.js +139 -0
  22. package/dist/commands/make-pif/index.js +128 -11
  23. package/dist/commands/make-pif/pif-generator.js +249 -43
  24. package/dist/commands/process-utils.js +13 -2
  25. package/dist/commands/remove-track.js +20 -13
  26. package/dist/commands/set-default-session.js +35 -31
  27. package/dist/commands/shared/config-operations.js +3 -10
  28. package/dist/commands/shared/density.js +12 -0
  29. package/dist/commands/shared/sort-utils.js +67 -10
  30. package/dist/commands/shared/validators.js +38 -3
  31. package/dist/commands/sort-bed.js +2 -33
  32. package/dist/commands/sort-gff.js +2 -33
  33. package/dist/commands/text-index/adapter-utils.js +6 -23
  34. package/dist/commands/text-index/aggregate.js +40 -34
  35. package/dist/commands/text-index/command.js +35 -7
  36. package/dist/commands/text-index/config-utils.js +60 -83
  37. package/dist/commands/text-index/file-list.js +32 -18
  38. package/dist/commands/text-index/index.js +4 -4
  39. package/dist/commands/text-index/indexing-utils.js +49 -73
  40. package/dist/commands/text-index/per-track.js +44 -31
  41. package/dist/commands/text-index/validators.js +1 -1
  42. package/dist/commands/upgrade.js +20 -32
  43. package/dist/commands/validate/configManifest.generated.js +5765 -0
  44. package/dist/commands/validate/displayDefaultKeys.js +28 -0
  45. package/dist/commands/validate/index.js +97 -0
  46. package/dist/commands/validate/suggest.js +35 -0
  47. package/dist/commands/validate/types.js +6 -0
  48. package/dist/commands/validate/validateConfig.js +522 -0
  49. package/dist/index.js +104 -50
  50. package/dist/types/common.js +3 -27
  51. package/dist/utils.js +309 -128
  52. package/dist/version.js +1 -1
  53. package/package.json +8 -10
  54. package/dist/commands/make-pif/cigar-utils.js +0 -24
  55. package/dist/commands/make-pif/file-utils.js +0 -30
  56. package/dist/commands/track-utils.js +0 -74
  57. 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 {};