@jbrowse/core 5.0.0-beta.11 → 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.
Files changed (99) hide show
  1. package/README.md +97 -75
  2. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -0
  3. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +14 -1
  4. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.d.ts +2 -0
  5. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +5 -5
  6. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +1 -1
  7. package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.d.ts +3 -1
  8. package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.js +2 -2
  9. package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.d.ts +2 -1
  10. package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.js +7 -14
  11. package/esm/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +3 -3
  12. package/esm/BaseFeatureWidget/stateModelFactory.d.ts +7 -0
  13. package/esm/BaseFeatureWidget/stateModelFactory.js +11 -0
  14. package/esm/BaseFeatureWidget/types.d.ts +2 -0
  15. package/esm/PluginManager.d.ts +6 -3
  16. package/esm/PluginManager.js +6 -3
  17. package/esm/ReExports/coreModules.generated.js +291 -285
  18. package/esm/ReExports/coreWorkerModules.generated.js +288 -283
  19. package/esm/ReExports/list.js +7 -0
  20. package/esm/TextSearch/TextSearchManager.d.ts +3 -3
  21. package/esm/TextSearch/TextSearchManager.js +6 -13
  22. package/esm/configuration/configurationSchema.d.ts +33 -95
  23. package/esm/configuration/configurationSchema.js +50 -218
  24. package/esm/configuration/configurationSlot.d.ts +18 -64
  25. package/esm/configuration/configurationSlot.js +31 -97
  26. package/esm/configuration/formatDetailsConfigSchema.js +2 -0
  27. package/esm/configuration/getConf.d.ts +7 -36
  28. package/esm/configuration/getConf.js +7 -37
  29. package/esm/configuration/index.d.ts +3 -2
  30. package/esm/configuration/index.js +2 -1
  31. package/esm/configuration/partitionAdvanced.d.ts +12 -0
  32. package/esm/configuration/partitionAdvanced.js +42 -0
  33. package/esm/configuration/plot.d.ts +6 -0
  34. package/esm/configuration/plot.js +34 -1
  35. package/esm/configuration/readConfObject.d.ts +15 -17
  36. package/esm/configuration/readConfObject.js +19 -50
  37. package/esm/configuration/schemaTypes.js +5 -1
  38. package/esm/configuration/snapshotPreprocess.js +1 -3
  39. package/esm/configuration/types.d.ts +60 -159
  40. package/esm/pluggableElementTypes/DisplayType.d.ts +7 -0
  41. package/esm/pluggableElementTypes/DisplayType.js +7 -0
  42. package/esm/pluggableElementTypes/RpcMethodType.d.ts +7 -0
  43. package/esm/pluggableElementTypes/RpcMethodType.js +9 -0
  44. package/esm/pluggableElementTypes/addMenuItems.d.ts +2 -1
  45. package/esm/pluggableElementTypes/addMenuItems.js +2 -1
  46. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +3 -2
  47. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +5 -5
  48. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +2 -4
  49. package/esm/pluggableElementTypes/models/BaseTrackModel.js +2 -4
  50. package/esm/pluggableElementTypes/models/baseTrackConfig.js +3 -0
  51. package/esm/rpc/RpcRegistry.d.ts +2 -1
  52. package/esm/rpc/byteBudget.d.ts +7 -0
  53. package/esm/rpc/byteBudget.js +10 -0
  54. package/esm/rpc/coreRpcMethods.d.ts +1 -1
  55. package/esm/rpc/coreRpcMethods.js +1 -1
  56. package/esm/rpc/explainTransferError.js +3 -3
  57. package/esm/rpc/methods/CoreLoadTrackCode.d.ts +17 -0
  58. package/esm/rpc/methods/CoreLoadTrackCode.js +24 -0
  59. package/esm/ui/AppLogo.js +43 -3
  60. package/esm/ui/buildExtraTrackMenuItems.d.ts +2 -2
  61. package/esm/ui/colors.d.ts +41 -30
  62. package/esm/ui/colors.js +81 -44
  63. package/esm/ui/multiTrackMenuItems.d.ts +2 -2
  64. package/esm/util/alignedBytes.d.ts +19 -0
  65. package/esm/util/alignedBytes.js +37 -0
  66. package/esm/util/analytics.d.ts +5 -6
  67. package/esm/util/analytics.js +5 -3
  68. package/esm/util/bezierConnector.js +5 -1
  69. package/esm/util/categoricalField.d.ts +23 -3
  70. package/esm/util/categoricalField.js +36 -3
  71. package/esm/util/cellMatches.js +14 -14
  72. package/esm/util/cellsStep.d.ts +3 -5
  73. package/esm/util/cellsStep.js +31 -29
  74. package/esm/util/color-bits/parse.js +22 -11
  75. package/esm/util/colorRamp.js +6 -0
  76. package/esm/util/colorScale.d.ts +9 -2
  77. package/esm/util/colorScale.js +33 -2
  78. package/esm/util/colorSchemes.d.ts +4 -2
  79. package/esm/util/colorSchemes.js +4 -1
  80. package/esm/util/isoformRank.d.ts +39 -0
  81. package/esm/util/isoformRank.js +118 -0
  82. package/esm/util/markEncodingTypes.d.ts +3 -1
  83. package/esm/util/quantileExtent.d.ts +22 -0
  84. package/esm/util/quantileExtent.js +24 -1
  85. package/esm/util/systemColorScheme.d.ts +4 -0
  86. package/esm/util/systemColorScheme.js +21 -0
  87. package/esm/util/tabix.d.ts +1 -1
  88. package/esm/util/tabix.js +1 -1
  89. package/esm/util/temporaryAssembly.d.ts +2 -2
  90. package/esm/util/temporaryAssembly.js +2 -3
  91. package/esm/util/trackConfigDelta.d.ts +1 -1
  92. package/esm/util/tracks.d.ts +25 -15
  93. package/esm/util/tracks.js +67 -43
  94. package/esm/util/types/index.d.ts +15 -7
  95. package/esm/util/types/index.js +21 -2
  96. package/esm/util/useRowVirtualScroll.d.ts +1 -0
  97. package/package.json +7 -4
  98. package/esm/rpc/methods/CoreLoadAdapterCode.d.ts +0 -13
  99. package/esm/rpc/methods/CoreLoadAdapterCode.js +0 -16
@@ -1,27 +1,14 @@
1
1
  /**
2
2
  * @module
3
- * The config **readers**: `readConfObject` off a live MST config node, and
4
- * `readConfigValue` off a plain snapshot object in a worker or a renderer. Both
5
- * exist to evaluate a slot's `jexl:...` callback on read; they differ only in
6
- * where the jexl instance comes from, the node's env or an explicit argument.
7
- * A `featureField` slot's `jexl:` expression is the display's to evaluate per
8
- * feature, so `readConfObject` hands it over as written.
3
+ * The config readers: `readConfObject` off a live config node, whose env
4
+ * supplies the jexl instance, and `readConfigValue` off a plain snapshot in a
5
+ * worker or renderer, which is handed one.
9
6
  */
10
7
  import { getEnv, getSnapshot, getType, isStateTreeNode, } from '@jbrowse/mobx-state-tree';
11
8
  import { getConfigurationSchemaMetadata } from './schemaRegistry.js';
12
9
  import { evaluateJexl, isCallbackValue } from './slotValueUtils.js';
13
- // Evaluate a slot's `jexl:...` callback string against the realm's single jexl
14
- // instance (carrying plugin-registered functions), read from the config node's
15
- // env. readConfObject only ever operates on live MST configs — nested sub-config
16
- // reads stay MST (env resolves to the root), and frozen track configs hydrate to
17
- // MST before any callback is read — so the env instance is always present here.
18
10
  function evalConfigCallback(expr, args, confObject) {
19
11
  if (!isStateTreeNode(confObject)) {
20
- // A jexl slot needs the realm's jexl instance, which is read from a live
21
- // config node's env. A plain config snapshot (e.g. an un-hydrated
22
- // session.tracks entry) carries no env — read it through a hydrated model,
23
- // or use readConfigValue(config, key, feature, jexl) which takes jexl
24
- // explicitly.
25
12
  throw new Error(`cannot evaluate jexl config callback ${JSON.stringify(expr)}: config is a plain snapshot, not a live model (no env to resolve the jexl instance)`);
26
13
  }
27
14
  const jexl = getEnv(confObject)
@@ -35,11 +22,7 @@ function isFeatureField(confObject, slotName) {
35
22
  return (isStateTreeNode(confObject) &&
36
23
  !!getConfigurationSchemaMetadata(getType(confObject))?.featureFields.has(slotName));
37
24
  }
38
- // Read and resolve a single slot: raw value, jexl callback evaluation, then a
39
- // referentially-stable snapshot for sub-config nodes.
40
25
  function readSlot(confObject, slotName, args) {
41
- // strict undefined check, not truthiness — a slot value can legitimately be
42
- // falsy (0, '', false, null)
43
26
  const value = confObject[slotName];
44
27
  if (value === undefined) {
45
28
  return undefined;
@@ -47,42 +30,25 @@ function readSlot(confObject, slotName, args) {
47
30
  const val = isCallbackValue(value) && !isFeatureField(confObject, slotName)
48
31
  ? evalConfigCallback(value, args, confObject)
49
32
  : value;
50
- // Fast path for primitives (most common case)
51
33
  if (val === null || typeof val !== 'object') {
52
34
  return val;
53
35
  }
54
- // Return the live, referentially-stable snapshot (frozen in dev) rather than
55
- // a per-read clone: stable identity lets downstream computeds memoize, and
56
- // the old structuredClone was both a hot-path allocation and a source of
57
- // spurious recomputation. Treat as read-only.
36
+ // the live snapshot, stable across reads so downstream computeds memoize;
37
+ // read-only
58
38
  return isStateTreeNode(val) ? getSnapshot(val) : val;
59
39
  }
60
- // loose implementation signature: the body returns values that are `any` by
61
- // nature (raw slot values, snapshots); the typed overloads above are what
62
- // callers see. No looser overload admits the model: one that did was a
63
- // catch-all a slot-name typo fell through to, compiling clean as `any`.
40
+ // No looser overload admits the model: a catch-all lets a slot-name typo
41
+ // compile as `any`.
64
42
  export function readConfObject(confObject, slotPath, args = {}) {
65
- // the single-slot read, first and allocation-free: it is the shape of nearly
66
- // every one of the ~1300 call sites, so it doesn't get normalized into a
67
- // one-element array on the way past
68
43
  if (typeof slotPath === 'string') {
69
44
  return readSlot(confObject, slotPath, args);
70
45
  }
71
46
  if (slotPath !== undefined && !Array.isArray(slotPath)) {
72
47
  throw new TypeError('slotPath must be a string or array');
73
48
  }
74
- // No path, or an empty one — which means the same thing, and used not to: `[]`
75
- // is truthy, so it reached the walk below and read `confObject[undefined]`.
76
- //
77
- // Returns the whole config as a plain object: the live, referentially-stable
78
- // snapshot (frozen in dev), not a fresh clone — treat as read-only.
79
49
  if (!slotPath?.length) {
80
50
  return isStateTreeNode(confObject) ? getSnapshot(confObject) : confObject;
81
51
  }
82
- // every segment but the last names a sub-config, and only the last is a slot
83
- // read — which is where jexl evaluation and snapshotting happen, and nowhere
84
- // else. Iterative rather than a self-call per segment, which re-entered the
85
- // whole argument-shape preamble above at each level.
86
52
  let conf = confObject;
87
53
  for (let i = 0; i < slotPath.length - 1; i++) {
88
54
  const subConf = conf[slotPath[i]];
@@ -93,9 +59,6 @@ export function readConfObject(confObject, slotPath, args = {}) {
93
59
  }
94
60
  return readSlot(conf, slotPath[slotPath.length - 1], args);
95
61
  }
96
- // The plain-object half of the slot walk, for readConfigValue. The MST half is
97
- // readSlot/rawSlotValue above. A plain object has no map entries and no
98
- // sub-config nodes to snapshot, so it is just a path reduce.
99
62
  function resolveConfigValue(config, key) {
100
63
  if (Array.isArray(key)) {
101
64
  let val = config;
@@ -107,12 +70,18 @@ function resolveConfigValue(config, key) {
107
70
  return config[key];
108
71
  }
109
72
  /**
110
- * Read a value from a plain config snapshot object. Automatically evaluates
111
- * "jexl:..." strings per-feature. Works without MST — intended for use in
112
- * rendering code (GPU, Canvas2D, workers). Pass the realm's `pluginManager.jexl`
113
- * so plugin-registered functions (e.g. in a custom `mouseover` slot) resolve.
73
+ * Read a value from a plain config snapshot, evaluating a `jexl:` callback
74
+ * against `feature`. For rendering code and workers; pass the realm's
75
+ * `pluginManager.jexl` so plugin-registered functions resolve.
114
76
  */
115
77
  export function readConfigValue(config, key, feature, jexl) {
116
- const raw = resolveConfigValue(config, key);
117
- return (isCallbackValue(raw) ? evaluateJexl(raw, { feature }, jexl) : raw);
78
+ return evaluateForFeature(resolveConfigValue(config, key), feature, jexl);
79
+ }
80
+ /**
81
+ * #api core/configuration
82
+ * A config value as `feature` reads it: a `jexl:` callback evaluated against
83
+ * the feature, anything else as written.
84
+ */
85
+ export function evaluateForFeature(value, feature, jexl) {
86
+ return isCallbackValue(value) ? evaluateJexl(value, { feature }, jexl) : value;
118
87
  }
@@ -67,7 +67,11 @@ export function isSlotDefinitionEntry(def) {
67
67
  export function isConfigurationModel(thing) {
68
68
  return isStateTreeNode(thing) && isConfigurationSchemaType(getType(thing));
69
69
  }
70
- const NUMBER_SLOT_TYPES = new Set(['number', 'integer', 'maybeNumber']);
70
+ const NUMBER_SLOT_TYPES = new Set([
71
+ 'number',
72
+ 'integer',
73
+ 'maybeNumber',
74
+ ]);
71
75
  /**
72
76
  * The slot a bare value of each form lifts into, off a `shorthand` option:
73
77
  * each slot it names takes the form its own type holds, a number where it
@@ -1,4 +1,5 @@
1
1
  import { isJexl } from '../util/jexlStrings.js';
2
+ import { isPlainObject } from '../util/objectUtils.js';
2
3
  import { slotWriteRefusal } from './configurationSlot.js';
3
4
  import { getConfigurationSchemaMetadata } from './schemaRegistry.js';
4
5
  import { bareFormOf, isConstantEntry, shorthandTargets } from './schemaTypes.js';
@@ -60,9 +61,6 @@ function isOwnSnapshot({ name, options }, snapshot) {
60
61
  snapshot.type === undefined ||
61
62
  snapshot.type === name);
62
63
  }
63
- function isPlainObject(value) {
64
- return typeof value === 'object' && value !== null && !Array.isArray(value);
65
- }
66
64
  // `lifted` under `written`: what the snapshot spells wins, member by member
67
65
  // where both are objects.
68
66
  function underWritten(written, lifted) {
@@ -5,42 +5,23 @@ import type { BuiltinSlotTypeName } from './configurationSlot.ts';
5
5
  import type { IAnyType, IArrayType, IOptionalIType, ISimpleType, IStateTreeNode, Instance, SnapshotOut } from '@jbrowse/mobx-state-tree';
6
6
  export type ConfigurationSchemaForModel<MODEL> = IsAny<MODEL> extends true ? AnyConfigurationSchemaType : MODEL extends IStateTreeNode<infer T> ? T extends AnyConfigurationSchemaType ? T : T extends IOptionalIType<infer INNER extends AnyConfigurationSchemaType, any> ? INNER : AnyConfigurationSchemaType : never;
7
7
  /**
8
- * A schema's definition as **stored**: the author's own entries over the ones
9
- * its `baseConfiguration` contributed, the type-level twin of
10
- * `mergeSchemaDefinition`. The base arrives already merged with its own base,
11
- * so one level here covers a chain of any depth.
12
- *
13
- * A flat mapped type over the union of both key sets, not
14
- * `Omit<BASE, keyof D> & D`: an intersection's `keyof` is a union of key sets,
15
- * and mapping over a union that includes `string` — which
16
- * `ConfigurationSchemaDefinition`'s index signature contributes — collapses
17
- * back to an index signature, which is the thing `ConfigNodeProps` exists to
18
- * get rid of.
8
+ * A schema's definition over its `baseConfiguration`'s, the type-level twin of
9
+ * `mergeSchemaDefinition`. A flat mapped type rather than
10
+ * `Omit<BASE, keyof D> & D`, which states it less clearly. ADR-145.
19
11
  */
20
12
  export type MergeConfigDef<D, BASE> = BASE extends ConfigurationSchemaType<infer BD, any> ? {
21
13
  [K in keyof BD | keyof D]: NormalizeSlotDef<K extends keyof D ? K extends keyof BD ? MergeSlotDef<BD[K], D[K]> : D[K] : K extends keyof BD ? BD[K] : never>;
22
14
  } : {
23
15
  [K in keyof D]: NormalizeSlotDef<D[K]>;
24
16
  };
25
- /**
26
- * A redeclared slot merges field-by-field over the base's; a sub-schema or a
27
- * constant keeps replace semantics, matching `mergeSchemaDefinition`'s
28
- * `isSlotDefinitionEntry` guard.
29
- */
30
17
  type MergeSlotDef<BASE_ENTRY, ENTRY> = BASE_ENTRY extends {
31
18
  type: string;
32
19
  } ? ENTRY extends {
33
20
  type: string;
34
21
  } ? Omit<BASE_ENTRY, keyof ENTRY> & ENTRY : ENTRY : ENTRY;
35
22
  /**
36
- * What the type level reads off a slot definition: the slot's `type`, a
37
- * `stringEnum`'s `model`, and whether a `defaultValue` is there at all — widened
38
- * off its literal. The description, `advanced`, `contextVariable` and the
39
- * literal default are documentation and runtime, and carrying them makes two
40
- * schemas that differ only in one of them mutually unassignable: a subclass
41
- * raising `height` from 100 to 250 stopped satisfying a parameter pinned to its
42
- * base. `SlotValueRawFromDef` re-widens a scalar default anyway, and reaches
43
- * `any` for everything else, so nothing downstream loses precision.
23
+ * Only `type`, `model` and a widened `defaultValue`: carrying a description or
24
+ * a literal default makes a subclass that changes one unassignable to its base.
44
25
  */
45
26
  type NormalizeSlotDef<E> = E extends AnyConfigurationSchemaType ? E : E extends {
46
27
  type: infer T;
@@ -58,106 +39,58 @@ type NormalizeSlotDef<E> = E extends AnyConfigurationSchemaType ? E : E extends
58
39
  type WidenDefaultValue<V> = [V] extends [boolean] ? boolean : [V] extends [string] ? string : [V] extends [number] ? number : unknown;
59
40
  type ConfigNodeValue<DEF> = IsAny<DEF> extends true ? any : DEF extends AnyConfigurationSchemaType ? DEF['Type'] : DEF extends IArrayType<any> | IOptionalIType<IArrayType<any>, any> ? DEF['Type'] : DEF extends string ? string : DEF extends number ? number : SlotValueRawFromDef<DEF>;
60
41
  /**
61
- * The slots a config node presents, read off the schema's own DEFINITION rather
62
- * than the `Record<string, any>` `makeConfigurationSchemaModel` assembles its
63
- * MST props from. Those props carry an index signature, so every
64
- * `node.anything` resolves to `any`; mapping the definition instead is what
65
- * makes `node.colorr` a compile error.
66
- *
67
- * A plain mapped type, and it has to stay one. Wrapping it in a conditional —
68
- * to answer early for a widened definition, say — makes TypeScript measure
69
- * DEFINITION as covariant instead of invariant, and it refuses a failed
70
- * covariant check on a type argument outright where a failed invariant one
71
- * falls back to comparing the two schemas structurally. Structural is the
72
- * comparison that matters: a subclass schema's node has every slot the base's
73
- * does, while its *definition* restates `defaultValue` and `description` and so
74
- * matches nothing. The widened case belongs on `AnyConfigurationModel`.
42
+ * A config node's slots, off the schema's definition, so `node.colorr` is a
43
+ * compile error (ADR-145). It must stay a plain mapped type: a conditional
44
+ * wrapper makes TypeScript refuse a subclass schema where the base is expected
45
+ * instead of comparing the two structurally.
75
46
  */
76
47
  export type ConfigNodeProps<DEFINITION> = {
77
48
  [K in keyof DEFINITION]: ConfigNodeValue<DEFINITION[K]>;
78
49
  };
79
50
  /**
80
- * The two write actions every config node carries, which `setConf` is the
81
- * spelling of. A type alias rather than an interface because it annotates the
82
- * `.actions` block: MST constrains that to `ModelActions`, an index signature
83
- * of functions, and only a type alias of an object literal gets the implicit
84
- * index signature that satisfies it.
51
+ * The two write actions under `setConf`. An alias, not an interface: only an
52
+ * alias gets the implicit index signature MST's `ModelActions` requires.
85
53
  */
86
54
  export type ConfigNodeActions = {
87
55
  setSubschema: (slotName: string, data: unknown) => unknown;
88
56
  setSlot: (slotName: string, value: unknown) => void;
89
57
  };
90
58
  /**
91
- * MST's node brand, restated. A config node carries its own schema here by
92
- * polymorphic `this`, and `ConfigurationSchemaForModel` walks it back out —
93
- * which is what every `getConf` / `readConfObject` / `setConf` slot-name
94
- * constraint hangs off, so losing it switches the read-side check off with no
95
- * error anywhere. `scripts/audit-config-read-types.ts` is the only thing that
96
- * reports that; run it after touching this.
97
- *
98
- * Structurally identical to `IStateTreeNode<IT>`, but an alias rather than an
99
- * interface: TypeScript infers an implicit index signature for an intersection
100
- * only when every constituent is an alias or a mapped type, and that is what
101
- * leaves a concrete config node — whose props are a mapped type with no index
102
- * signature of its own — assignable to `AnyConfigurationModel`.
59
+ * MST's node brand restated as an alias, which keeps a concrete node assignable
60
+ * to `AnyConfigurationModel`. It carries the schema by polymorphic `this`, and
61
+ * every slot-name check hangs off `ConfigurationSchemaForModel` walking it back
62
+ * out: run `scripts/audit-config-read-types.ts` after touching it.
103
63
  */
104
64
  export type ConfigNodeBrand<IT extends IAnyType> = {
105
65
  readonly $treenode?: any;
106
66
  readonly $__mstStateTreeNodeType__?: [IT] | [any];
107
67
  };
108
68
  /**
109
- * The identifier a schema's `explicitIdentifier` installs, folded into the
110
- * definition as if it were a declared string slot — which is what it is at
111
- * runtime, `makeConfigurationSchemaModel` just builds it from the options rather
112
- * than from the table. Folding it in rather than intersecting it onto `Type`
113
- * keeps it out of `ConfigurationSchemaType`'s parameter list: as a parameter it
114
- * reads as invariant (it lands in a `Record<K, …>` key position), which stops a
115
- * concrete schema widening to `AnyConfigurationSchemaType`. Inheritance comes
116
- * free, since `MergeConfigDef` already folds the base's whole definition in.
69
+ * The `explicitIdentifier` prop, folded into the definition as a string slot
70
+ * rather than a type parameter, which would read as invariant and stop a
71
+ * concrete schema widening to `AnyConfigurationSchemaType`.
117
72
  */
118
73
  export interface IdentifierSlotDef {
119
74
  type: 'string';
120
75
  defaultValue: '';
121
76
  }
122
- /**
123
- * The `type` prop an `explicitlyTyped` schema installs, folded into the
124
- * definition the same way and for the same reasons. Separate from
125
- * `IdentifierSlotDef` despite the identical shape, because the runtime prop is
126
- * `types.literal(modelName)` and this is where that would be said if the model
127
- * name ever became a type parameter.
128
- */
77
+ /** An `explicitlyTyped` schema's `type` prop, folded in the same way. */
129
78
  export interface TypeSlotDef {
130
79
  type: 'string';
131
80
  defaultValue: '';
132
81
  }
133
82
  export type ConfigurationSlotName<SCHEMA> = IsAny<SCHEMA> extends true ? string : SCHEMA extends ConfigurationSchemaType<infer D, any> ? keyof D & string : never;
134
83
  /**
135
- * The names a config snapshot for `SCHEMA` may use — its own slots, its
136
- * sub-schemas recursively, its identifier and everything it inherits — with
137
- * every value left `unknown`.
138
- *
139
- * Names are the whole point of it. A slot value can be its own type, a `jexl:`
140
- * callback string, or (for a `maybe*` slot) absent, and nothing here tries to
141
- * check that; a *name* the schema does not declare is dropped on the way in
142
- * without a word, so `preferance` is a setting that reads as applied and never
143
- * applies. Against an object literal — which is how an embedder writes one —
144
- * TypeScript's excess-property check turns that into a compile error.
84
+ * The names a config snapshot for `SCHEMA` may use, sub-schemas included, with
85
+ * every value `unknown`. MST drops an undeclared name silently; against an
86
+ * object literal, the excess-property check makes it a compile error.
145
87
  */
146
88
  export type ConfigurationSnapshot<SCHEMA> = SCHEMA extends undefined ? never : SCHEMA extends ConfigurationSchemaType<infer D, any> ? {
147
89
  [K in keyof D]?: D[K] extends ConfigurationSchemaType<any, any> ? ConfigurationSnapshot<D[K]> : unknown;
148
90
  } : unknown;
149
91
  /**
150
- * The value each builtin slot `type` reads as — the type-level twin of
151
- * `slotTypes` in `configurationSlot.ts`, which is the same list as MST models.
152
- * A table rather than the fourteen-deep conditional staircase this used to be:
153
- * adding a slot type is now one row here and one there, and the two tables sit
154
- * side by side where a name present in one and missing from the other is
155
- * visible. Missing a row is not silent either — it drops the slot to the
156
- * `defaultValue` fallback below, which is what `SlotValueRawFromDef` documents.
157
- *
158
- * `stringEnum`/`maybeStringEnum` are deliberately absent: their value type comes
159
- * from the author's own `types.enumeration`, read off `model` before this table
160
- * is consulted.
92
+ * What each builtin slot type reads as, beside `slotTypes` in
93
+ * `configurationSlot.ts`. The enum types read their members off `model`.
161
94
  */
162
95
  interface SlotValueByType {
163
96
  stringArray: string[];
@@ -171,15 +104,7 @@ interface SlotValueByType {
171
104
  maybeBoolean: boolean | undefined;
172
105
  maybeColor: string | undefined;
173
106
  maybeString: string | undefined;
174
- /**
175
- * `frozen` and its `maybe*` form are `any` deliberately — arbitrary dynamic
176
- * JSON whose shape the caller asserts at the read boundary. `unknown` would
177
- * only add cast ceremony on values that are legitimately dynamic.
178
- *
179
- * Stated here rather than left to the `defaultValue` fallback, which reaches
180
- * `any` for these only by accident: no `frozen` slot in the repo happens to
181
- * default to a scalar.
182
- */
107
+ /** arbitrary JSON, whose shape the caller asserts where it reads it */
183
108
  frozen: any;
184
109
  maybeFrozen: any;
185
110
  number: number;
@@ -191,19 +116,6 @@ interface SlotValueByType {
191
116
  color: string;
192
117
  colorArray: string[];
193
118
  }
194
- /**
195
- * The two tables name the same builtin slot types, checked rather than
196
- * remembered.
197
- *
198
- * A row present in one and missing from the other is otherwise **not** an
199
- * error: the slot drops through to `SlotValueRawFromDef`'s `defaultValue`
200
- * fallback, which re-widens a scalar default but reaches `any` for everything
201
- * else — so a new `stringArray`-shaped type would silently read as `any` at
202
- * every call site. The "every builtin slot type reads as its declared value
203
- * type" case in `configTypeNarrowing.test.ts` lists the rows by hand, so it
204
- * only catches the drift if whoever adds the type also edits the test; this
205
- * catches it either way.
206
- */
207
119
  type MutuallyExtends<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false;
208
120
  type Assert<T extends true> = T;
209
121
  export type SlotValueTableCoversBuiltinSlotTypes = Assert<MutuallyExtends<BuiltinSlotTypeName, keyof SlotValueByType>>;
@@ -225,14 +137,10 @@ type SubSchemaSlotPath<SCHEMA> = SCHEMA extends ConfigurationSchemaType<infer D,
225
137
  } ? readonly [K, ...string[]] : never;
226
138
  }[keyof D & string] : readonly string[];
227
139
  /**
228
- * The array paths a read may take into `SCHEMA`. Past one segment each is
229
- * checked: a sub-schema's name, then one of its slot names or a deeper path. A
230
- * head naming something other than a sub-schema — a pluggable adapter union, an
231
- * array or map of sub-schemas, a `frozen` slot — keeps an unchecked tail, and a
232
- * widened schema admits any `string[]`.
233
- *
234
- * A one-segment path stays unchecked: it is how a generic class body, where
235
- * `SCHEMA` is unresolved, reads a slot at all (`FastaAdapterBase`).
140
+ * The array paths a read may take into `SCHEMA`, checked segment by segment
141
+ * through sub-schemas. A pluggable union, a collection or a `frozen` slot keeps
142
+ * an unchecked tail. A one-segment path stays unchecked, since a generic class
143
+ * body with `SCHEMA` unresolved reads a slot that way (`FastaAdapterBase`).
236
144
  */
237
145
  export type ConfigurationSlotPath<SCHEMA> = readonly [] | readonly [string] | SubSchemaSlotPath<SCHEMA>;
238
146
  /** what a read of `PATH` into `SCHEMA` yields */
@@ -243,17 +151,8 @@ export type ConfigurationSlotPathValue<SCHEMA, PATH> = PATH extends readonly [
243
151
  /** what a raw read (`getConf` / `readConfObject`) of this slot yields */
244
152
  export type ConfigurationSlotValue<SCHEMA, K extends string> = SCHEMA extends ConfigurationSchemaType<infer D, any> ? K extends keyof D ? SlotValueRawFromDef<D[K]> : any : any;
245
153
  /**
246
- * Naming convention for config types, paired per schema:
247
- * - `XConfigSchema` is the MST IType (the schema itself). Use it for
248
- * `getConf`, `ConfigurationReference`, and factory params — anywhere a schema
249
- * is expected.
250
- * - `XConfigModel` is `Instance<XConfigSchema>` (a resolved config node). Use it
251
- * for `readConfObject` results, model fields, and values read off a session.
252
- *
253
- * Prefer a named `XConfigModel` alias over inlining `Instance<XConfigSchema>` at
254
- * call sites. Two historical names predate this convention and stay as-is:
255
- * `BaseTrackConfig` (the track instance type) and `AnyConfiguration` (a
256
- * model-or-snapshot union, not a plain instance).
154
+ * Naming: `XConfigSchema` is the schema (an MST type), for factory params and
155
+ * `ConfigurationReference`; `XConfigModel` is `Instance<XConfigSchema>`, a node.
257
156
  */
258
157
  export type AnyConfigurationSchemaType = ConfigurationSchemaType<any, any>;
259
158
  export type AnyConfigurationModel = Instance<AnyConfigurationSchemaType> & Record<string, any>;
@@ -266,22 +165,11 @@ export interface PluggableConfigNode extends AnyConfigurationModel {
266
165
  type: string;
267
166
  }
268
167
  /**
269
- * The `XConfigModel` for a **field table** — a `*ConfigSchemaFields` export —
270
- * rather than for a whole assembled schema. What it is for is the cast a
271
- * cross-cutting mixin uses to reach the `configuration` its composing display
272
- * supplies but its own `self` cannot see: cast to `AnyConfigurationModel` there
273
- * and `getConf`/`setConf` derive their slot-name constraint from a schema
274
- * widened to `any`, so every name typechecks and a misspelled *read* returns
275
- * `undefined` with no diagnostic at any layer. Name the mixin's own field table
276
- * instead and the check comes back, scoped to exactly the slots that mixin owns.
277
- *
278
- * `BASE` is for the mixin that also reads a slot off the schema its displays
279
- * inherit from — `ConfigurationSlotName` walks the base chain, so passing it
280
- * admits those names too and nothing else (`HeightModeMixin` and `height`).
281
- *
282
- * The pairing this depends on is a mixin and its field table living together:
283
- * `RowHeightMixin` + `rowHeightConfigSchemaFields` is the worked example, and
284
- * `packages/tree-sidebar/CLAUDE.md` has the reasoning.
168
+ * The node type for a mixin's field table (a `*ConfigSchemaFields` export), for
169
+ * the cast a mixin uses to reach its host's `configuration`. A cast to
170
+ * `AnyConfigurationModel` there checks no slot name, so a misspelled read
171
+ * returns `undefined` silently. `BASE` admits the slots of the schema the
172
+ * mixin's displays inherit (`HeightModeMixin`'s `height`).
285
173
  */
286
174
  export type ConfigModelForFields<FIELDS extends ConfigurationSchemaDefinition, BASE extends AnyConfigurationSchemaType | undefined = undefined> = Instance<ConfigurationSchemaType<MergeConfigDef<FIELDS, BASE>, ConfigurationSchemaOptions<BASE, undefined>>>;
287
175
  /**
@@ -292,11 +180,8 @@ export type ConfigModelForFields<FIELDS extends ConfigurationSchemaDefinition, B
292
180
  * const pin: HostChecksSlotNames<HeightModeHost> = true
293
181
  * ```
294
182
  *
295
- * Worth a named type because the widened form has **no symptom** — a mixin host
296
- * cast to `AnyConfigurationModel` compiles and runs and checks nothing. Every
297
- * misspelled slot name below it typechecks, and a misspelled *read* returns
298
- * `undefined` with no diagnostic at any layer, so only a sabotage or this
299
- * assertion tells the two apart.
183
+ * The widened form has no symptom: a host cast to `AnyConfigurationModel`
184
+ * compiles, runs and checks nothing.
300
185
  */
301
186
  export type HostChecksSlotNames<HOST extends {
302
187
  configuration: AnyConfigurationModel;
@@ -305,12 +190,28 @@ export type HostChecksSlotNames<HOST extends {
305
190
  export type AnyConfigurationSnapshot = SnapshotOut<AnyConfigurationModel>;
306
191
  /**
307
192
  * A value readable as configuration: either a live configuration model or a
308
- * plain snapshot of one. `session.tracks` holds plain base or merged configs
309
- * that hydrate to MST only on first reference access, `getTrackById` also
310
- * answers live assembly-sequence and connection nodes, and `readConfObject`
311
- * reads both — so this is the honest type at those boundaries. Reserve
312
- * `AnyConfigurationModel` for values that must be live (actions, identity,
313
- * reference resolution).
193
+ * plain snapshot of one, such as a config a caller hands to an add action.
194
+ * Reserve `AnyConfigurationModel` for values that must be live (actions,
195
+ * identity, reference resolution).
314
196
  */
315
197
  export type AnyConfiguration = AnyConfigurationModel | AnyConfigurationSnapshot;
198
+ /**
199
+ * A track config as `jbrowse.tracks` and `session.tracks` hold it: a frozen
200
+ * plain object, which hydrates to a config node only when a track reads its
201
+ * `configuration`. It holds what was written, so a slot at its default is
202
+ * absent. `readConfObject` refuses one for that reason; read `trackId` and
203
+ * `type` off it directly, and a slot through a helper that supplies the
204
+ * default, such as `getConfAssemblyNamesOrNone`.
205
+ */
206
+ export interface TrackConfigEntry {
207
+ trackId: string;
208
+ type: string;
209
+ [key: string]: unknown;
210
+ }
211
+ /**
212
+ * What `getTrackById` answers: a {@link TrackConfigEntry}, or a live node for
213
+ * an assembly's sequence track or a track a connection holds. A raw member
214
+ * read works on both, and finds a slot at its default only on the node.
215
+ */
216
+ export type AnyTrackConfig = TrackConfigEntry | AnyConfigurationModel;
316
217
  export {};
@@ -53,6 +53,12 @@ export default class DisplayType extends LazyStateModelElement {
53
53
  * not given one.
54
54
  */
55
55
  adapterCapabilities: readonly string[];
56
+ /**
57
+ * The RPC methods this display's first fetch calls. A track launching with
58
+ * it loads their code in its worker beside the adapter's
59
+ * (`RpcMethodType.preload`).
60
+ */
61
+ rpcMethods: readonly string[];
56
62
  retiredTypes: readonly RetiredDisplayType[];
57
63
  retiredState?: RetiredDisplayState;
58
64
  /**
@@ -70,6 +76,7 @@ export default class DisplayType extends LazyStateModelElement {
70
76
  ReactComponent: AnyReactComponentType;
71
77
  helpText?: string;
72
78
  adapterCapabilities?: readonly string[];
79
+ rpcMethods?: readonly string[];
73
80
  retiredTypes?: readonly RetiredDisplayType[];
74
81
  retiredState?: RetiredDisplayState;
75
82
  plotExamples?: readonly PlotExample[];
@@ -24,6 +24,12 @@ export default class DisplayType extends LazyStateModelElement {
24
24
  * not given one.
25
25
  */
26
26
  adapterCapabilities;
27
+ /**
28
+ * The RPC methods this display's first fetch calls. A track launching with
29
+ * it loads their code in its worker beside the adapter's
30
+ * (`RpcMethodType.preload`).
31
+ */
32
+ rpcMethods;
27
33
  retiredTypes;
28
34
  retiredState;
29
35
  /**
@@ -42,6 +48,7 @@ export default class DisplayType extends LazyStateModelElement {
42
48
  this.viewType = stuff.viewType;
43
49
  this.helpText = stuff.helpText;
44
50
  this.adapterCapabilities = stuff.adapterCapabilities ?? [];
51
+ this.rpcMethods = stuff.rpcMethods ?? [];
45
52
  this.retiredTypes = stuff.retiredTypes ?? [];
46
53
  this.retiredState = stuff.retiredState;
47
54
  this.plotExamples = stuff.plotExamples ?? [];
@@ -70,6 +70,13 @@ export default abstract class RpcMethodType<MethodName extends string = string>
70
70
  name: MethodName;
71
71
  pluginManager: PluginManager;
72
72
  constructor(pluginManager: PluginManager);
73
+ /**
74
+ * Start loading the code `execute` runs, for a method whose body is a dynamic
75
+ * import. A display that names the method in `rpcMethods` has its worker call
76
+ * this when its track launches, so the import lands beside the adapter's code
77
+ * instead of after the first request's refName lookup.
78
+ */
79
+ preload(): Promise<unknown>;
73
80
  serializeArguments(args: object): Promise<Record<string, unknown>>;
74
81
  /**
75
82
  * The reference sequence of the genome a call names in `assemblyName`, as a
@@ -167,6 +167,15 @@ export default class RpcMethodType extends PluggableElementBase {
167
167
  super();
168
168
  this.pluginManager = pluginManager;
169
169
  }
170
+ /**
171
+ * Start loading the code `execute` runs, for a method whose body is a dynamic
172
+ * import. A display that names the method in `rpcMethods` has its worker call
173
+ * this when its track launches, so the import lands beside the adapter's code
174
+ * instead of after the first request's refName lookup.
175
+ */
176
+ async preload() {
177
+ return undefined;
178
+ }
170
179
  async serializeArguments(args) {
171
180
  const augmented = await this.augmentLocationObjects(this.attachReference(args));
172
181
  return {
@@ -71,7 +71,8 @@ export declare function addViewMenuItems<N extends ViewTypeName, K extends MenuM
71
71
  * Add items to a menu on a display type belonging to another plugin, e.g. an
72
72
  * "open this read in a dotplot" entry on the alignments display's context menu.
73
73
  * See {@link addViewMenuItems}; the name is checked against
74
- * `DisplayTypeRegistry`.
74
+ * `DisplayTypeRegistry`. It matches the registered name only, so an item added
75
+ * to `LinearMarkDisplay` misses `LinearManhattanDisplay`, which subtypes it.
75
76
  *
76
77
  * ```ts
77
78
  * addDisplayMenuItems(pluginManager, 'LinearAlignmentsDisplay', {
@@ -63,7 +63,8 @@ export function addViewMenuItems(pluginManager, name, options) {
63
63
  * Add items to a menu on a display type belonging to another plugin, e.g. an
64
64
  * "open this read in a dotplot" entry on the alignments display's context menu.
65
65
  * See {@link addViewMenuItems}; the name is checked against
66
- * `DisplayTypeRegistry`.
66
+ * `DisplayTypeRegistry`. It matches the registered name only, so an item added
67
+ * to `LinearMarkDisplay` misses `LinearManhattanDisplay`, which subtypes it.
67
68
  *
68
69
  * ```ts
69
70
  * addDisplayMenuItems(pluginManager, 'LinearAlignmentsDisplay', {
@@ -759,8 +759,9 @@ export declare const BaseDisplay: import("@jbrowse/mobx-state-tree").IModelType<
759
759
  /**
760
760
  * #method
761
761
  * What a draft would report once applied, throwing a refusal as
762
- * `liftPlot` does. A display with a rule list over its settings
763
- * overrides it; the draft still applies, and draws what it can.
762
+ * `liftPlot` does: what its colour objects and `scales.y` say together
763
+ * that cannot be drawn as written. A display with a rule list over its
764
+ * settings overrides it; the draft still applies, and draws what it can.
764
765
  */
765
766
  plotProblems(draft: Plot): string[];
766
767
  /**
@@ -1,6 +1,6 @@
1
1
  import { hasParent, isAlive, types } from '@jbrowse/mobx-state-tree';
2
2
  import { applyConfSettings, getConfigurationSchemaMetadata, getConf, setConf, } from '../../configuration/index.js';
3
- import { liftPlot as liftPlotDraft, plotKeysOf, plotOf, plotWrites as plotWritesOf, } from '../../configuration/plot.js';
3
+ import { liftPlot as liftPlotDraft, plotKeysOf, plotOf, plotWrites as plotWritesOf, schemaPlotProblems, } from '../../configuration/plot.js';
4
4
  import { getContainingTrack, getEnv, statusFraction, statusMessageText, } from '../../util/index.js';
5
5
  import { ElementId } from '../../util/types/mst.js';
6
6
  import { displaySetterName } from '../../util/unknownSnapshotKeys.js';
@@ -324,12 +324,12 @@ function stateModelFactory() {
324
324
  /**
325
325
  * #method
326
326
  * What a draft would report once applied, throwing a refusal as
327
- * `liftPlot` does. A display with a rule list over its settings
328
- * overrides it; the draft still applies, and draws what it can.
327
+ * `liftPlot` does: what its colour objects and `scales.y` say together
328
+ * that cannot be drawn as written. A display with a rule list over its
329
+ * settings overrides it; the draft still applies, and draws what it can.
329
330
  */
330
331
  plotProblems(draft) {
331
- this.liftPlot(draft);
332
- return [];
332
+ return schemaPlotProblems(liftPlotDraft(self.configuration, draft));
333
333
  },
334
334
  /**
335
335
  * #method
@@ -200,10 +200,8 @@ export declare function createBaseTrackModel(pm: PluginManager, trackType: strin
200
200
  * completely untouched ones — `reaction` only fires on an actual change.
201
201
  *
202
202
  * `equals: compareStructural` is load-bearing, not an optimization:
203
- * `self.configuration` is a re-resolving reference, and persisting a save
204
- * can swap the resolved node identity (the delta path reconciles in place
205
- * but still churns once, and a session without deltas replaces the frozen
206
- * `jbrowse.tracks` entry, rehydrating a brand-new MST node every write).
203
+ * `self.configuration` is a re-resolving reference, and an undo or a
204
+ * session restore swaps the resolved node for a new working copy.
207
205
  * Referential comparison would treat every such swap as a fresh change
208
206
  * and re-fire the save, an unbounded debounced loop. Structural
209
207
  * comparison settles once the content stops changing.