@jbrowse/cli 5.0.0-beta.10 → 5.0.0-beta.13

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.
@@ -24,7 +24,7 @@ what it cannot draw in a corner notice rather than refusing the track.
24
24
  This command runs the display's own rule list over every "marks" list in the
25
25
  file: an error where a mark draws nothing, never draws or a step cannot run,
26
26
  and a warning where a slot waits unread or the marks draw in an arrangement
27
- the author may not have meant. With --json each finding carries the stable
27
+ the author may not have meant. With --json each finding has the stable
28
28
  "rule" id of the rule that made it.
29
29
 
30
30
  Types registered by plugins are not known to this command, so they come through
@@ -21,8 +21,8 @@ export const IDENTITY_SCALE = 'identity';
21
21
  const CATEGORICAL_PRESET = { scale: 'categorical' };
22
22
  /**
23
23
  * The fields whose values have names, an order and colours of their own on
24
- * every display, beneath each display's own presets. Red forward and blue
25
- * reverse is what the synteny ribbons paint too. The SV classes are
24
+ * every display, beneath each display's own presets. The synteny ribbons
25
+ * paint strand from this range too. The SV classes are
26
26
  * `svClassOf`'s, deletion red and duplication blue as dbVar and gnomAD-SV
27
27
  * paint them, insertion the pileup's purple, and the rest kept apart under
28
28
  * deuteranopia and protanopia. A record with no class files under `''`, so
@@ -166,6 +166,37 @@ export function scaleEndProblems({ domainMin, domainMax, domainQuantile }, { rev
166
166
  export function noticeLines(setting, problems) {
167
167
  return problems.map(({ slot, message }) => `${setting}.${slot}: ${message}`);
168
168
  }
169
+ function numberOf(value) {
170
+ return typeof value === 'number' ? value : undefined;
171
+ }
172
+ function stringOf(value) {
173
+ return typeof value === 'string' ? value : undefined;
174
+ }
175
+ function listOf(value) {
176
+ return Array.isArray(value) ? value : undefined;
177
+ }
178
+ /** A quantitative scale's ends among the members `member` reads, each a number or unset. */
179
+ export function scaleEndsOf(member) {
180
+ return {
181
+ domainMin: numberOf(member('domainMin')),
182
+ domainMax: numberOf(member('domainMax')),
183
+ domainQuantile: numberOf(member('domainQuantile')),
184
+ };
185
+ }
186
+ /**
187
+ * The members `colorProblems` reads among the ones `member` reads, each of the
188
+ * type it reads or unset, so a config snapshot and a config node answer alike.
189
+ */
190
+ export function colorSlotsOf(member) {
191
+ return {
192
+ ...scaleEndsOf(member),
193
+ field: stringOf(member('field')) ?? '',
194
+ scale: stringOf(member('scale')),
195
+ domain: listOf(member('domain')),
196
+ range: listOf(member('range')),
197
+ labels: listOf(member('labels')),
198
+ };
199
+ }
169
200
  function pinned(entry) {
170
201
  return entry !== '' && Number.isFinite(Number(entry));
171
202
  }
@@ -1,8 +1,8 @@
1
1
  // Generated by scripts/generateMarkRules.ts from plugins/marks/src/LinearMarkDisplay/markProblems.ts: do not edit, `pnpm autogen` rewrites it.
2
2
  import { AUTO_BIN } from './autoBin.js';
3
3
  import { FEATURE_FIELD_PRESETS, aggregateFieldName, colorProblems, fieldScaleOf, isJexl, SHAPE_NAMES, isNamedColor, paintedScale, scaleEndProblems, universalPresetOf, } from './markRuleFacts.js';
4
- import { MARK_SPECS, rampResolvesPerRegion, readsValue } from './markSpecs.js';
5
- import { DEFAULT_AGGREGATE_OP, BIN_OVERLAP_FIELD, DEFAULT_BIN_AS, DEFAULT_BIN_FIELD, DEFAULT_COVERAGE_AS, DEFAULT_FORMULA_AS, DEFAULT_LINE_INTERPOLATE, DEFAULT_ROW_PROPORTION, DEFAULT_LINK_SHAPE, DEFAULT_MARK_TYPE, CELLS_FIELDS, DEFAULT_PILEUP_AS, DEFAULT_PILEUP_FIELDS, DEFAULT_TEXT_FIELD, DEFAULT_X2, MATE_FIELDS, } from './markVocabulary.js';
4
+ import { MARK_SPECS, readsValue } from './markSpecs.js';
5
+ import { DEFAULT_AGGREGATE_OP, BIN_OVERLAP_FIELD, DEFAULT_BIN_AS, DEFAULT_BIN_FIELD, DEFAULT_COVERAGE_AS, DEFAULT_FORMULA_AS, DEFAULT_LINE_INTERPOLATE, DEFAULT_ROW_PROPORTION, DEFAULT_LINK_SHAPE, DEFAULT_MARK_TYPE, CELLS_FIELDS, DEFAULT_PILEUP_AS, DEFAULT_PILEUP_FIELDS, DEFAULT_TEXT_FIELD, DEFAULT_X2, MATE_FIELDS, MAX_THRESHOLD_CUTS, } from './markVocabulary.js';
6
6
  import { stepChannels } from './stepChannels.js';
7
7
  /**
8
8
  * The level of every rule of the list, by the stable id a report and a test
@@ -27,6 +27,8 @@ export const MARK_RULES = {
27
27
  'threshold-cuts': 'warning',
28
28
  /** A threshold colour naming no cut, so every value paints one colour. */
29
29
  'threshold-no-cuts': 'warning',
30
+ /** A threshold colour naming more cuts than it paints, so a value past the eighth takes the ninth colour. */
31
+ 'threshold-cut-count': 'warning',
30
32
  /** A threshold `range` not one colour longer than its cuts. */
31
33
  'threshold-range': 'warning',
32
34
  /** A `domain` on a linear or log colour, whose ends are `domainMin` and `domainMax`. */
@@ -39,8 +41,6 @@ export const MARK_RULES = {
39
41
  'field-spells-constant': 'warning',
40
42
  /** A colour's or a shape's `labels` naming values its `domain` does not list, or no categorical scale's. */
41
43
  'labels-domain': 'warning',
42
- /** A text's colour ramp with an open end, whose colours then differ from one region to the next. */
43
- 'unpinned-text-ramp': 'warning',
44
44
  /** A `minBpPerPx` not below the mark's `maxBpPerPx`, so the mark never draws. */
45
45
  'empty-zoom-range': 'error',
46
46
  /** A `filter` or `formula` whose `expr` is not a `jexl:` expression. */
@@ -59,6 +59,8 @@ export const MARK_RULES = {
59
59
  'step-field-expression': 'error',
60
60
  /** A `y` naming a field that no `aggregate` or `coverage` step before it writes. */
61
61
  'unwritten-y': 'error',
62
+ /** Another channel naming a field that no `aggregate` or `coverage` step before it writes, so it reads no value; the field a `facet` or `rows` splits on counts as written. */
63
+ 'unwritten-field': 'warning',
62
64
  /** A mark other than a span drawn beside one that stacks rows, standing in the first of them. */
63
65
  'value-beside-rows': 'warning',
64
66
  /** Two `pileup` steps packing one plot, whose rows share numbers. */
@@ -120,11 +122,6 @@ function stepsOf(mark) {
120
122
  function colorScaleOf({ scale, field = '' }) {
121
123
  return paintedScale({ scale, field }, fieldScaleOf(FEATURE_FIELD_PRESETS, field));
122
124
  }
123
- function rampColor(mark) {
124
- const color = mark.encoding?.color ?? {};
125
- const painted = colorScaleOf(color);
126
- return painted === 'linear' || painted === 'log' ? color : undefined;
127
- }
128
125
  function sizeFieldOf(size) {
129
126
  return typeof size === 'string'
130
127
  ? size
@@ -381,7 +378,34 @@ function stepProblems(steps, list = 'transform') {
381
378
  }
382
379
  return problems;
383
380
  }
384
- function ownProblems(mark, display, section) {
381
+ // Each field a mark's channels read besides `y`, by the slot naming it: the
382
+ // channels its type reads, a colour's or a shape's field where it is a scale,
383
+ // a text's field as its default leaves it, and a link's size field.
384
+ function channelFields(mark, spec) {
385
+ const encoding = mark.encoding ?? {};
386
+ const { x, x2, row, color = {}, shape, text = DEFAULT_TEXT_FIELD } = encoding;
387
+ const reads = (channel) => spec.channels.includes(channel);
388
+ const named = [
389
+ ['encoding.x', x],
390
+ ['encoding.x2', typeof x2 === 'string' ? x2 : x2?.pos],
391
+ ['encoding.x2.chrom', typeof x2 === 'object' ? x2.chrom : undefined],
392
+ ['encoding.row', reads('row') ? row : undefined],
393
+ [
394
+ 'encoding.color.field',
395
+ reads('color') && colorScaleOf(color) !== 'none'
396
+ ? fieldOf(color)
397
+ : undefined,
398
+ ],
399
+ ['encoding.shape.field', reads('shape') ? fieldOf(shape) : undefined],
400
+ ['encoding.text', reads('text') ? text : undefined],
401
+ [
402
+ 'encoding.size.field',
403
+ spec.size === 'channel' ? sizeFieldOf(encoding.size) : undefined,
404
+ ],
405
+ ];
406
+ return named.flatMap(([slot, field]) => field && !isJexl(field) ? [[slot, field]] : []);
407
+ }
408
+ function ownProblems(mark, display, section, split) {
385
409
  const type = markTypeOf(mark);
386
410
  const problems = [];
387
411
  const y = mark.encoding?.y;
@@ -430,6 +454,10 @@ function ownProblems(mark, display, section) {
430
454
  (color.domain ?? []).length === 0) {
431
455
  problems.push(found('threshold-no-cuts', 'encoding.color.domain', 'a threshold names its cut points in domain, and with none every value paints the first colour'));
432
456
  }
457
+ const cutCount = (color.domain ?? []).filter(cut => Number.isFinite(Number(cut))).length;
458
+ if (colorScaleOf(color) === 'threshold' && cutCount > MAX_THRESHOLD_CUTS) {
459
+ problems.push(found('threshold-cut-count', 'encoding.color.domain', `a threshold paints ${MAX_THRESHOLD_CUTS} cuts, and these ${cutCount} leave a value past the ${MAX_THRESHOLD_CUTS}th in the ${MAX_THRESHOLD_CUTS + 1}th colour`));
460
+ }
433
461
  problems.push(...shapeLabelProblems(mark.encoding?.shape));
434
462
  problems.push(...constantAsFieldProblems(mark.encoding));
435
463
  if (typeof size === 'object') {
@@ -437,14 +465,6 @@ function ownProblems(mark, display, section) {
437
465
  problems.push(found(rule, `encoding.size.${slot}`, message));
438
466
  }
439
467
  }
440
- const ramp = rampColor(mark);
441
- if (ramp) {
442
- const { domainMin, domainMax } = ramp;
443
- if (rampResolvesPerRegion(type) &&
444
- (domainMin === undefined || domainMax === undefined)) {
445
- problems.push(found('unpinned-text-ramp', `encoding.color.${domainMin === undefined ? 'domainMin' : 'domainMax'}`, `a ${type}'s ramp resolves an open end against each region's own extremes, so its colours agree across regions only with domainMin and domainMax both pinned`));
446
- }
447
- }
448
468
  const { minBpPerPx = 0, maxBpPerPx = 0 } = mark;
449
469
  if (minBpPerPx > 0 && maxBpPerPx > 0 && minBpPerPx >= maxBpPerPx) {
450
470
  problems.push(found('empty-zoom-range', 'minBpPerPx', `never draws: minBpPerPx ${minBpPerPx} is not below maxBpPerPx ${maxBpPerPx}`));
@@ -454,8 +474,19 @@ function ownProblems(mark, display, section) {
454
474
  problems.push(found('two-packings', 'transform', `packs rows of its own over the ${packsIn(section) ? "facet's" : "display's"} pileup, and the two share row numbers; one pileup packs every mark`));
455
475
  }
456
476
  const fields = madeFields([...display, ...section, ...stepsOf(mark)]);
457
- if (fields && y && !isJexl(y) && !fields.has(y)) {
458
- problems.push(found('unwritten-y', 'encoding.y', `reads "${y}", which no step before it writes; they leave ${[...fields].join(', ')}`));
477
+ if (fields) {
478
+ if (split) {
479
+ fields.add(split);
480
+ }
481
+ const leaves = `which no step before it writes; they leave ${[...fields].join(', ')}`;
482
+ if (y && !isJexl(y) && !fields.has(y)) {
483
+ problems.push(found('unwritten-y', 'encoding.y', `reads "${y}", ${leaves}`));
484
+ }
485
+ for (const [slot, field] of channelFields(mark, spec)) {
486
+ if (!fields.has(field)) {
487
+ problems.push(found('unwritten-field', slot, `reads "${field}", ${leaves}`));
488
+ }
489
+ }
459
490
  }
460
491
  return problems;
461
492
  }
@@ -468,6 +499,13 @@ function ownProblems(mark, display, section) {
468
499
  export function markProblems({ marks, facet, transform = [], rows, scales, }) {
469
500
  const faceted = named(facet?.field);
470
501
  const drawsRows = named(rows?.field) && !faceted;
502
+ // the field the sections or rows are split on, which every section's rows
503
+ // keep through the steps that make rows
504
+ const split = faceted
505
+ ? String(facet?.field)
506
+ : drawsRows
507
+ ? String(rows?.field)
508
+ : undefined;
471
509
  const section = readable(facet?.transform ?? []);
472
510
  const display = readable(transform);
473
511
  const shared = [...display, ...section];
@@ -489,7 +527,10 @@ export function markProblems({ marks, facet, transform = [], rows, scales, }) {
489
527
  : []),
490
528
  ...(drawsRows ? packingsUnderRows(marks, transform, section) : []),
491
529
  ...marks.flatMap((mark, i) => mark
492
- ? ownProblems(mark, display, section).map(p => ({ mark: i, ...p }))
530
+ ? ownProblems(mark, display, section, split).map(p => ({
531
+ mark: i,
532
+ ...p,
533
+ }))
493
534
  : []),
494
535
  ];
495
536
  for (const [i, mark] of marks.entries()) {
@@ -12,46 +12,39 @@ export const MARK_SPECS = {
12
12
  bar: {
13
13
  channels: ['y', 'row', 'color'],
14
14
  value: 'required',
15
- ramp: 'display',
16
15
  hit: 'rows',
17
16
  },
18
17
  point: {
19
18
  channels: ['y', 'row', 'color', 'shape'],
20
19
  value: 'required',
21
- ramp: 'display',
22
20
  hit: 'index',
23
21
  size: 'constant',
24
22
  },
25
23
  rule: {
26
24
  channels: ['y', 'row', 'color'],
27
25
  value: 'required',
28
- ramp: 'display',
29
26
  hit: 'rows',
30
27
  size: 'constant',
31
28
  },
32
29
  line: {
33
30
  channels: ['y', 'row', 'color'],
34
31
  value: 'required',
35
- ramp: 'display',
36
32
  hit: 'rows',
37
33
  size: 'constant',
38
34
  },
39
35
  span: {
40
36
  channels: ['row', 'color'],
41
37
  value: 'none',
42
- ramp: 'display',
43
38
  hit: 'rows',
44
39
  },
45
40
  text: {
46
41
  channels: ['y', 'row', 'color', 'text'],
47
42
  value: 'optional',
48
- ramp: 'worker',
49
43
  hit: false,
50
44
  },
51
45
  link: {
52
46
  channels: ['y', 'row', 'color', 'size'],
53
47
  value: 'optional',
54
- ramp: 'display',
55
48
  hit: 'index',
56
49
  farFoot: true,
57
50
  size: 'channel',
@@ -72,23 +65,18 @@ export function plotsValue(type) {
72
65
  export function hitsByIndex(type) {
73
66
  return specOf(type).hit === 'index';
74
67
  }
75
- /** Whether a mark type's ramp colour resolves per region, in the worker. */
76
- export function rampResolvesPerRegion(type) {
77
- return specOf(type).ramp === 'worker';
78
- }
79
68
  /**
80
- * The lanes a mark asks the worker to fill: its type's channels, a ramp's raw
81
- * values beside the colour where the display resolves the ramp, and the hit
82
- * index where the mark answers a hover through one. The encoder fills whichever of
83
- * `color` and `colorValue` the colour declaration calls for.
69
+ * The lanes a mark asks the worker to fill: its type's channels, the colour
70
+ * lanes `colorLanesOf` chose for its declaration, and the hit index where the
71
+ * mark answers a hover through one.
84
72
  */
85
- export function markLanes(type) {
73
+ export function markLanes(type, colorLanes) {
86
74
  const spec = specOf(type);
87
75
  return [
88
76
  ...spec.channels.flatMap((channel) => channel === 'shape'
89
77
  ? ['glyph']
90
- : channel === 'color' && spec.ramp === 'display'
91
- ? [channel, 'colorValue']
78
+ : channel === 'color'
79
+ ? colorLanes
92
80
  : [channel]),
93
81
  ...(spec.farFoot ? ['x2Ref'] : []),
94
82
  ...(hitsByIndex(type) ? ['index'] : []),
@@ -26,6 +26,8 @@ export const DEFAULT_LINK_SHAPE = 'dome';
26
26
  export const DEFAULT_LINK_STROKE_PX = 2;
27
27
  /** The fraction of its row a span fills where its `rowProportion` is left unwritten. */
28
28
  export const DEFAULT_ROW_PROPORTION = 1;
29
+ /** The cuts a threshold colour paints, render-core's `MAX_COLOR_CUTS`. */
30
+ export const MAX_THRESHOLD_CUTS = 8;
29
31
  export const SIZE_SCALES = ['linear', 'log'];
30
32
  export const MARK_SOURCES = ['features', 'density'];
31
33
  export const DEFAULT_MARK_SOURCE = 'features';
@@ -37,7 +39,7 @@ export const BIN_OVERLAP_FIELD = 'overlap';
37
39
  export const DEFAULT_COVERAGE_AS = 'coverage';
38
40
  export const DEFAULT_FLATTEN_FIELD = 'subfeatures';
39
41
  export const DEFAULT_CELLS_FIELD = 'seq';
40
- /** What a `cells` step writes: `state` on every cell, `base` on a mismatch or an insertion, `match` off a gap, `length` on an insertion. */
42
+ /** What a `cells` step writes: `state` on every cell, `base` on a mismatch or an insertion, `match` off a gap or a reference `N`, `length` on an insertion. */
41
43
  export const CELLS_FIELDS = ['state', 'base', 'match', 'length'];
42
44
  export const DEFAULT_PILEUP_AS = 'row';
43
45
  export const DEFAULT_PILEUP_FIELDS = ['start', 'end'];
@@ -68,7 +68,7 @@ function describe(schema) {
68
68
  if (name === 'JexlString') {
69
69
  return 'a "jexl:" expression';
70
70
  }
71
- if (name === 'CssColor') {
71
+ if (name === 'CssColor' || name === 'CssColorEntry') {
72
72
  return 'a CSS color (a name like "red", "#rrggbb", "rgb()" or "hsl()")';
73
73
  }
74
74
  if (name) {
@@ -378,7 +378,7 @@ function explain(data, errors, problems, depth) {
378
378
  break;
379
379
  }
380
380
  case 'pattern':
381
- emit(where, error.schemaPath.endsWith('/CssColor/pattern')
381
+ emit(where, /\/CssColor(?:Entry)?\/pattern$/.test(error.schemaPath)
382
382
  ? `expected ${describe({ $ref: '#/$defs/CssColor' })}, got ${JSON.stringify(error.data)}`
383
383
  : `expected a string matching /${String(parent.pattern)}/, got ${JSON.stringify(error.data)}`);
384
384
  break;
@@ -8,10 +8,11 @@
8
8
  // loading normally with the setting doing nothing.
9
9
  //
10
10
  // Pure: no filesystem, no process exit. The command wrapper owns both.
11
+ import { fileNameOf, isLooseTrackConfig, matchFormat, } from '@jbrowse/add-track-core';
11
12
  import { configManifest } from './configManifest.generated.js';
12
13
  import { displayDefaultsForTrackType } from './displayDefaultKeys.js';
13
14
  import { isRecord, liftToSnapshot } from './liftConfig.js';
14
- import { colorProblems, scaleEndProblems } from './markRules/colorScale.js';
15
+ import { colorProblems, colorSlotsOf, scaleEndProblems, scaleEndsOf, } from './markRules/colorScale.js';
15
16
  import { markProblems } from './markRules/markProblems.js';
16
17
  import { hasDeclaredShape, rootConfigurationSlots, schemaProblems, } from './schemaValidate.js';
17
18
  import { didYouMean } from './suggest.js';
@@ -103,7 +104,7 @@ function checkPluggable(obj, group, groupLabel, where, report) {
103
104
  }
104
105
  function checkTrack(track, index, manifest, report, ctx) {
105
106
  const where = `tracks[${index}]`;
106
- const loose = typeof track.uri === 'string' && !('adapter' in track);
107
+ const loose = isLooseTrackConfig(track);
107
108
  const trackId = track.trackId;
108
109
  if (typeof trackId === 'string' && trackId) {
109
110
  if (ctx.seenTrackIds.has(trackId)) {
@@ -129,6 +130,19 @@ function checkTrack(track, index, manifest, report, ctx) {
129
130
  }
130
131
  }
131
132
  }
133
+ if (loose) {
134
+ const file = fileNameOf(track.uri);
135
+ const spec = matchFormat(file)?.spec;
136
+ if (!spec || !('adapterType' in spec)) {
137
+ const message = `JBrowse infers no track from "${file}", so the track fails to load: give it an "adapter"`;
138
+ if (ctx.declaresPlugins) {
139
+ report.warn(`${where}.uri`, `${message}, unless a plugin it declares guesses this extension`);
140
+ }
141
+ else {
142
+ report.error(`${where}.uri`, message);
143
+ }
144
+ }
145
+ }
132
146
  checkPluggable(track, manifest.tracks, 'track', where, report);
133
147
  if (!loose) {
134
148
  checkPluggable(track.adapter, manifest.adapters, 'adapter', `${where}.adapter`, report);
@@ -276,7 +290,7 @@ function declaredEntries(lifted, display, slot, slots) {
276
290
  return (slots.find(s => s.name === slot)?.defaultEntries ?? []);
277
291
  }
278
292
  return Array.isArray(list)
279
- ? list.map((entry, i) => hasDeclaredShape(written[i], `${SLOTS_POINTER}/${slot}/items`)
293
+ ? list.map((entry, i) => hasDeclaredShape([written[i]], `${SLOTS_POINTER}/${slot}`)
280
294
  ? entry
281
295
  : undefined)
282
296
  : [];
@@ -330,18 +344,9 @@ function pushOnce(report, problem) {
330
344
  report.problems.push(problem);
331
345
  }
332
346
  }
333
- function numberOf(value) {
334
- return typeof value === 'number' ? value : undefined;
335
- }
336
- function scaleEndsOf(scale) {
337
- return {
338
- domainMin: numberOf(scale.domainMin),
339
- domainMax: numberOf(scale.domainMax),
340
- domainQuantile: numberOf(scale.domainQuantile),
341
- };
342
- }
343
347
  function scalesOf(scales) {
344
- return isRecord(scales.y) ? { y: scaleEndsOf(scales.y) } : {};
348
+ const { y } = scales;
349
+ return isRecord(y) ? { y: scaleEndsOf(name => y[name]) } : {};
345
350
  }
346
351
  function declaredMembers(written, slot) {
347
352
  const declared = new Set((slot.subSlots ?? []).map(s => s.name));
@@ -371,19 +376,15 @@ function checkScaleSlots(display, slots, where, report) {
371
376
  }
372
377
  const members = declaredMembers(written, slot);
373
378
  if (slot.fieldPresets) {
374
- for (const problem of colorProblems({
375
- ...scaleEndsOf(members),
376
- field: typeof members.field === 'string' ? members.field : '',
377
- scale: typeof members.scale === 'string' ? members.scale : undefined,
378
- domain: Array.isArray(members.domain) ? members.domain : undefined,
379
- range: Array.isArray(members.range) ? members.range : undefined,
380
- labels: Array.isArray(members.labels) ? members.labels : undefined,
381
- }, slot.fieldPresets)) {
379
+ for (const problem of colorProblems(colorSlotsOf(name => name === 'field'
380
+ ? (members.field ?? slot.fieldDefault)
381
+ : members[name]), slot.fieldPresets)) {
382
382
  warn(`${where}.${slot.name}`, problem);
383
383
  }
384
384
  }
385
- if (slot.name === 'scales' && isRecord(members.y)) {
386
- for (const problem of scaleEndProblems(scaleEndsOf(members.y))) {
385
+ const { y } = members;
386
+ if (slot.name === 'scales' && isRecord(y)) {
387
+ for (const problem of scaleEndProblems(scaleEndsOf(name => y[name]))) {
387
388
  warn(`${where}.scales.y`, problem);
388
389
  }
389
390
  }
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.0.0-beta.10';
1
+ export const version = '5.0.0-beta.13';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/cli",
3
- "version": "5.0.0-beta.10",
3
+ "version": "5.0.0-beta.13",
4
4
  "type": "module",
5
5
  "description": "A command line tool for working with JBrowse 2",
6
6
  "keywords": [
@@ -31,9 +31,9 @@
31
31
  "cli-progress": "^3.12.0",
32
32
  "serve-handler": "^6.1.7",
33
33
  "tmp": "^0.2.7",
34
- "@jbrowse/add-track-core": "^5.0.0-beta.10",
35
- "@jbrowse/cigar-utils": "^5.0.0-beta.10",
36
- "@jbrowse/text-indexing-core": "^5.0.0-beta.10"
34
+ "@jbrowse/cigar-utils": "^5.0.0-beta.13",
35
+ "@jbrowse/add-track-core": "^5.0.0-beta.13",
36
+ "@jbrowse/text-indexing-core": "^5.0.0-beta.13"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public"