@jbrowse/core 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.
Files changed (145) hide show
  1. package/README.md +210 -80
  2. package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.js +0 -2
  3. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -0
  4. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +14 -1
  5. package/esm/BaseFeatureWidget/BaseFeatureDetail/BasicValue.js +22 -6
  6. package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.js +3 -4
  7. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.d.ts +2 -0
  8. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +5 -5
  9. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +1 -1
  10. package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.d.ts +3 -1
  11. package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.js +2 -2
  12. package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.d.ts +2 -1
  13. package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.js +7 -14
  14. package/esm/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +3 -3
  15. package/esm/BaseFeatureWidget/SequenceFeatureDetails/useSequenceData.js +8 -5
  16. package/esm/BaseFeatureWidget/stateModelFactory.d.ts +7 -0
  17. package/esm/BaseFeatureWidget/stateModelFactory.js +11 -0
  18. package/esm/BaseFeatureWidget/types.d.ts +2 -0
  19. package/esm/CorePlugin.js +32 -1
  20. package/esm/PluginManager.d.ts +6 -3
  21. package/esm/PluginManager.js +6 -3
  22. package/esm/ReExports/coreModules.generated.js +291 -283
  23. package/esm/ReExports/coreWorkerModules.generated.js +288 -281
  24. package/esm/ReExports/list.js +10 -2
  25. package/esm/TextSearch/TextSearchManager.d.ts +3 -3
  26. package/esm/TextSearch/TextSearchManager.js +6 -13
  27. package/esm/configuration/configurationSchema.d.ts +33 -95
  28. package/esm/configuration/configurationSchema.js +50 -218
  29. package/esm/configuration/configurationSlot.d.ts +18 -64
  30. package/esm/configuration/configurationSlot.js +31 -97
  31. package/esm/configuration/formatDetailsConfigSchema.js +2 -0
  32. package/esm/configuration/getConf.d.ts +7 -36
  33. package/esm/configuration/getConf.js +7 -37
  34. package/esm/configuration/index.d.ts +5 -2
  35. package/esm/configuration/index.js +3 -1
  36. package/esm/configuration/partitionAdvanced.d.ts +12 -0
  37. package/esm/configuration/partitionAdvanced.js +42 -0
  38. package/esm/configuration/plot.d.ts +67 -0
  39. package/esm/configuration/plot.js +175 -0
  40. package/esm/configuration/readConfObject.d.ts +15 -17
  41. package/esm/configuration/readConfObject.js +19 -50
  42. package/esm/configuration/schemaTypes.js +5 -1
  43. package/esm/configuration/snapshotPreprocess.js +1 -3
  44. package/esm/configuration/types.d.ts +60 -159
  45. package/esm/pluggableElementTypes/DisplayType.d.ts +14 -0
  46. package/esm/pluggableElementTypes/DisplayType.js +13 -0
  47. package/esm/pluggableElementTypes/RpcMethodType.d.ts +7 -0
  48. package/esm/pluggableElementTypes/RpcMethodType.js +9 -0
  49. package/esm/pluggableElementTypes/WidgetType.d.ts +6 -0
  50. package/esm/pluggableElementTypes/WidgetType.js +6 -0
  51. package/esm/pluggableElementTypes/addMenuItems.d.ts +2 -1
  52. package/esm/pluggableElementTypes/addMenuItems.js +2 -1
  53. package/esm/pluggableElementTypes/index.js +1 -8
  54. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +58 -1
  55. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +84 -2
  56. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +2 -4
  57. package/esm/pluggableElementTypes/models/BaseTrackModel.js +2 -4
  58. package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +2 -2
  59. package/esm/pluggableElementTypes/models/BaseViewModel.js +1 -1
  60. package/esm/pluggableElementTypes/models/baseTrackConfig.js +3 -0
  61. package/esm/rpc/RpcRegistry.d.ts +2 -1
  62. package/esm/rpc/byteBudget.d.ts +7 -0
  63. package/esm/rpc/byteBudget.js +10 -0
  64. package/esm/rpc/coreRpcMethods.d.ts +1 -1
  65. package/esm/rpc/coreRpcMethods.js +1 -1
  66. package/esm/rpc/explainTransferError.js +3 -3
  67. package/esm/rpc/methods/CoreLoadTrackCode.d.ts +17 -0
  68. package/esm/rpc/methods/CoreLoadTrackCode.js +24 -0
  69. package/esm/ui/AppLogo.js +43 -3
  70. package/esm/ui/JexlFilterDialog.d.ts +1 -1
  71. package/esm/ui/JexlFilterDialog.js +2 -3
  72. package/esm/ui/buildExtraTrackMenuItems.d.ts +2 -2
  73. package/esm/ui/colors.d.ts +41 -30
  74. package/esm/ui/colors.js +81 -44
  75. package/esm/ui/multiTrackMenuItems.d.ts +2 -2
  76. package/esm/util/TimeTraveller.d.ts +3 -0
  77. package/esm/util/TimeTraveller.js +34 -2
  78. package/esm/util/alignedBytes.d.ts +19 -0
  79. package/esm/util/alignedBytes.js +37 -0
  80. package/esm/util/analytics.d.ts +5 -6
  81. package/esm/util/analytics.js +5 -3
  82. package/esm/util/baseDisplayConfig.d.ts +8 -0
  83. package/esm/util/baseDisplayConfig.js +27 -0
  84. package/esm/util/bezierConnector.js +5 -1
  85. package/esm/util/cachedSetup.d.ts +16 -6
  86. package/esm/util/cachedSetup.js +108 -38
  87. package/esm/util/categoricalField.d.ts +23 -3
  88. package/esm/util/categoricalField.js +36 -3
  89. package/esm/util/cellMatches.d.ts +23 -0
  90. package/esm/util/cellMatches.js +264 -0
  91. package/esm/util/cellsStep.d.ts +27 -0
  92. package/esm/util/cellsStep.js +439 -0
  93. package/esm/util/color-bits/parse.js +22 -11
  94. package/esm/util/colorRamp.js +6 -0
  95. package/esm/util/colorScale.d.ts +9 -2
  96. package/esm/util/colorScale.js +33 -2
  97. package/esm/util/colorSchemes.d.ts +4 -2
  98. package/esm/util/colorSchemes.js +4 -1
  99. package/esm/util/copyToClipboard.js +8 -1
  100. package/esm/util/createAbortRotation.js +3 -3
  101. package/esm/util/featureTransforms.d.ts +7 -6
  102. package/esm/util/featureTransforms.js +131 -479
  103. package/esm/util/fetchPhases.d.ts +1 -1
  104. package/esm/util/formatBytes.js +1 -1
  105. package/esm/util/formatGuessers.d.ts +25 -14
  106. package/esm/util/formatGuessers.js +42 -27
  107. package/esm/util/formatRelativeTime.js +1 -1
  108. package/esm/util/getLocationUri.d.ts +2 -1
  109. package/esm/util/getLocationUri.js +8 -2
  110. package/esm/util/groupKeys.d.ts +4 -5
  111. package/esm/util/groupKeys.js +7 -6
  112. package/esm/util/intervals.js +9 -4
  113. package/esm/util/io/index.js +3 -1
  114. package/esm/util/isoformRank.d.ts +39 -0
  115. package/esm/util/isoformRank.js +118 -0
  116. package/esm/util/jexlFilters.d.ts +31 -49
  117. package/esm/util/jexlFilters.js +29 -39
  118. package/esm/util/locString.js +5 -2
  119. package/esm/util/markEncoding.js +48 -8
  120. package/esm/util/markEncodingTypes.d.ts +11 -2
  121. package/esm/util/quantileExtent.d.ts +22 -0
  122. package/esm/util/quantileExtent.js +24 -1
  123. package/esm/util/springAnimate.js +1 -1
  124. package/esm/util/stepTables.d.ts +14 -0
  125. package/esm/util/stepTables.js +38 -4
  126. package/esm/util/systemColorScheme.d.ts +4 -0
  127. package/esm/util/systemColorScheme.js +21 -0
  128. package/esm/util/tabix.d.ts +7 -3
  129. package/esm/util/tabix.js +3 -3
  130. package/esm/util/temporaryAssembly.d.ts +2 -2
  131. package/esm/util/temporaryAssembly.js +2 -3
  132. package/esm/util/trackConfigDelta.d.ts +1 -1
  133. package/esm/util/tracks.d.ts +28 -45
  134. package/esm/util/tracks.js +71 -82
  135. package/esm/util/types/index.d.ts +15 -7
  136. package/esm/util/types/index.js +21 -2
  137. package/esm/util/unknownSnapshotKeys.d.ts +3 -1
  138. package/esm/util/unknownSnapshotKeys.js +5 -11
  139. package/esm/util/usePanZoom.js +10 -3
  140. package/esm/util/useRafCommit.js +1 -0
  141. package/esm/util/useRowVirtualScroll.d.ts +1 -0
  142. package/esm/util/withFeatureDetails.js +4 -0
  143. package/package.json +13 -9
  144. package/esm/rpc/methods/CoreLoadAdapterCode.d.ts +0 -13
  145. package/esm/rpc/methods/CoreLoadAdapterCode.js +0 -16
@@ -3,28 +3,22 @@ import { isCssColor } from '../util/cssColorParse.js';
3
3
  import { isJexl, stringToJexlExpression } from '../util/jexlStrings.js';
4
4
  import { FileLocation, JexlExpressionString } from '../util/types/mst.js';
5
5
  import { isCallbackValue } from './slotValueUtils.js';
6
- // What a color slot admits is what the painters parse (`isCssColor`), plus the
7
- // empty string a slot such as `outlineColor` spells "none" with; a field name
8
- // written where a color goes used to load and paint the invalid sentinel.
9
6
  function notAColor(value) {
10
- return `${JSON.stringify(value)} is not a color. A color is a CSS color: a name like "red" or "steelblue", "#rgb" / "#rrggbb" / "#rrggbbaa", "rgb()" / "rgba()" / "hsl()" / "hsla()", or "transparent"; a color computed per feature is a "jexl:" callback, in a slot that takes one`;
7
+ return `${JSON.stringify(value)} is not a color. A color is a CSS color: a name like "red" or "steelblue", "#rgb" / "#rrggbb" / "#rrggbbaa", "rgb()" / "rgba()" / "hsl()" / "hsla()", "transparent" or a BED triple like "255,0,0"; a color computed per feature is a "jexl:" callback, in a slot that takes one`;
11
8
  }
9
+ // '' is a color slot's "none", as in `outlineColor`
12
10
  const CssColorType = types.refinement('CssColor', types.string, value => value === '' || isCssColor(value), notAColor);
13
- // An entry of a colour list has no "none" to spell, so '' is refused with the
14
- // rest of what is not a colour.
11
+ // a colour list's entry has no "none" to spell
15
12
  const CssColorEntryType = types.refinement('CssColorEntry', types.string, isCssColor, notAColor);
16
13
  const MaybeFileLocation = types.snapshotProcessor(types.maybe(FileLocation), {
17
- preProcessor: (snap) => snap && 'uri' in snap && snap.uri === '' ? undefined : snap,
14
+ preProcessor: (snap) => snap &&
15
+ (('uri' in snap && snap.uri === '') ||
16
+ ('localPath' in snap && snap.localPath === ''))
17
+ ? undefined
18
+ : snap,
18
19
  });
19
- // Single source of truth for the builtin slot type names, pairing each with its
20
- // MST value type and its editor fallback default. Keeping model + fallback in
21
- // one table means adding a slot type is one edit and can't half-register.
22
- //
23
- // `satisfies` rather than an annotation: a `Record<string, …>` annotation
24
- // widens the keys to `string`, which is what let `type: 'enum'` — a name this
25
- // table has never had — compile for years and silently lose the config
26
- // editor's dropdown. The literal keys are what `ConfigSlotType` below is made
27
- // of.
20
+ // The slot type vocabulary. `satisfies`, not an annotation, so the literal keys
21
+ // make up `ConfigSlotType`; every surface naming a type checks against it.
28
22
  const slotTypes = {
29
23
  stringArray: { model: types.array(types.string), fallbackDefault: [] },
30
24
  expressionArray: {
@@ -42,23 +36,17 @@ const slotTypes = {
42
36
  color: { model: CssColorType, fallbackDefault: 'black' },
43
37
  integer: { model: types.integer, fallbackDefault: 1 },
44
38
  number: { model: types.number, fallbackDefault: 1 },
45
- // The `maybe*` types spend `undefined` on "not explicitly set", which is the
46
- // one value no config can spell and so the only reliable way to say it — a
47
- // computed/auto fallback, e.g. a drag-resized track height.
39
+ // a `maybe*` type's unset is `undefined`, the one value no config spells:
40
+ // "decide from the data", as an auto-fitted height or a BED itemRgb colour
48
41
  maybeNumber: { model: types.maybe(types.number) },
49
42
  maybeBoolean: { model: types.maybe(types.boolean) },
50
- // for a slot whose unset state means "decide from the data" — a feature's own
51
- // BED itemRgb, say
52
43
  maybeColor: { model: types.maybe(CssColorType) },
53
- // object-valued, e.g. alignments `sortedBy`
54
44
  maybeFrozen: { model: types.maybe(types.frozen()) },
55
- // for text whose unset state is a third thing beside some text and none: an
56
- // axis title is derived while unset, and drawn bare once it is ''
57
45
  maybeString: { model: types.maybe(types.string) },
58
46
  string: { model: types.string, fallbackDefault: '' },
59
47
  text: { model: types.string, fallbackDefault: '' },
60
- // a field the display reads per feature: a name, a dotted path, or a
61
- // `jexl:` expression, which the display evaluates and the reader never does
48
+ // a field the display reads per feature; its `jexl:` expression is the
49
+ // display's to evaluate, never the reader's
62
50
  featureField: { model: types.string, fallbackDefault: '' },
63
51
  fileLocation: {
64
52
  model: FileLocation,
@@ -67,48 +55,25 @@ const slotTypes = {
67
55
  locationType: 'UriLocation',
68
56
  },
69
57
  },
70
- // for a sidecar the adapter works without: unset means no file is
71
- // configured, and so does the `{ uri: '' }` a cleared URL field in the
72
- // config editor writes
58
+ // a sidecar the adapter works without; the editor's cleared `{ uri: '' }`
59
+ // reads as unset
73
60
  maybeFileLocation: { model: MaybeFileLocation },
74
61
  frozen: { model: types.frozen(), fallbackDefault: {} },
75
62
  };
76
- // The types with no builtin table entry, because the author supplies the
77
- // `types.enumeration` as `model` and `ConfigSlot` wraps it: in `types.maybe`
78
- // for `maybeStringEnum`, in `types.array` for `stringEnumArray`. Named once,
79
- // and spliced into both the type union and the runtime set below so those two
80
- // cannot drift apart.
63
+ // the author supplies the `types.enumeration` as `model`, and `ConfigSlot`
64
+ // wraps it in `types.maybe` or `types.array`
81
65
  const ENUM_SLOT_TYPES = [
82
66
  'stringEnum',
83
67
  'maybeStringEnum',
84
68
  'stringEnumArray',
85
69
  ];
86
- /**
87
- * The same closed set, at runtime, because the type alone does not close it for
88
- * the callers that matter most: `ConfigSlot` is plugin-facing, and a JS plugin
89
- * or one built against an older core reaches it with no checking at all. Those
90
- * are exactly the callers that hit the silent-degradation trap above, and
91
- * `type: 'enum'` — a name this table has never had — is the one that did.
92
- *
93
- * Typed `Set<string>` rather than `Set<ConfigSlotType>` so the membership test
94
- * stays a question TypeScript has not already answered for in-tree callers. It
95
- * is a real runtime check, not a restatement of the signature.
96
- */
70
+ // the same set at runtime, for a JS plugin the type never checked
97
71
  const CONFIG_SLOT_TYPE_NAMES = new Set([
98
72
  ...Object.keys(slotTypes),
99
73
  ...ENUM_SLOT_TYPES,
100
74
  ]);
101
- // Lookup view over the table. The two enum types are legal `ConfigSlotType`s
102
- // with no entry, so indexing by the union has to tolerate a miss — this states
103
- // that in the type rather than casting it away at each call.
104
75
  const builtinSlotTypes = slotTypes;
105
- // The slot types whose default is `undefined` — the genuine "unset" state,
106
- // distinguishable from every value a config can spell. Mostly derived from the
107
- // table above (they're exactly the entries with no `fallbackDefault`, since
108
- // "unset" is their fixed form) so the two can't drift. `maybeStringEnum` is
109
- // named explicitly because, like plain `stringEnum`, it has no builtin model:
110
- // the author supplies the enumeration and `ConfigSlot` wraps it in
111
- // `types.maybe`.
76
+ // an entry with no `fallbackDefault` is one whose fixed form is unset
112
77
  const MAYBE_TYPES = new Set([
113
78
  ...Object.entries(slotTypes)
114
79
  .filter(([, spec]) => spec.fallbackDefault === undefined)
@@ -135,23 +100,10 @@ function refusingCallbacks(model) {
135
100
  return types.refinement(model, value => !isJexl(value), notACallbackSlot);
136
101
  }
137
102
  /**
138
- * A configuration slot is a plain MST property holding the slot's value type,
139
- * or, where the slot declares a `contextVariable`, that or a `jexl:...`
140
- * callback string. Every other slot refuses a `jexl:` string, so a callback
141
- * the reader would evaluate is one the schema says it takes. A `featureField`
142
- * holds a `jexl:` expression as its value, which the display evaluates per
143
- * feature. The value lives directly on the parent configuration model — there
144
- * is no per-slot sub-model. `types.stripDefault` omits the property from the
145
- * parent snapshot when it equals the default, so saved sessions stay minimal.
146
- * Per-slot metadata (type/description/defaultValue/contextVariable) lives in
147
- * the schema registry (a WeakMap keyed by the MST type, see
148
- * schemaRegistry.ts); jexl callbacks are evaluated on read by
149
- * `readConfObject`.
150
- *
151
- * Interning the returned type on `(type, model, defaultValue)` was prototyped
152
- * and declined: a session builds 1197 slot types for 222 distinct ones, and
153
- * removing the repeats moves neither the time nor the heap
154
- * (`config-schema-construction`, published in `reference/EAGER_BUNDLE.md`).
103
+ * The MST property a slot is: its value type, or that or a `jexl:` callback
104
+ * where the slot declares a `contextVariable` (ADR-155), stripped from the
105
+ * snapshot at its default. Interning these was measured and declined
106
+ * (`config-schema-construction` in `reference/EAGER_BUNDLE.md`).
155
107
  */
156
108
  export default function ConfigSlot(definition) {
157
109
  const { model, type, defaultValue } = definition;
@@ -162,24 +114,15 @@ export default function ConfigSlot(definition) {
162
114
  if (!valueModel) {
163
115
  throw new Error(`no builtin config slot type "${type}", and no 'model' param provided`);
164
116
  }
165
- // the `maybe*` types intentionally default to `undefined` (the "unset"
166
- // state); every other slot type must declare a concrete default so a missing
167
- // one is caught as an authoring mistake.
117
+ if (defaultValue === null) {
118
+ throw new Error(`a slot cannot default to null, which a write reads as a reset to the default (ADR-146): a slot meaning "unset" is a maybe* type, such as maybeFrozen`);
119
+ }
168
120
  if (defaultValue === undefined && !MAYBE_TYPES.has(type)) {
169
121
  throw new Error("no 'defaultValue' provided");
170
122
  }
171
- // The inverse of the `defaultValue === undefined` check above, and the half
172
- // that had no guard. A `maybe*` slot whose default is concrete can never *be*
173
- // unset — no config can spell `undefined` — so the unset state such a slot
174
- // exists to express (auto-fit, decide-from-the-data, inherit) is unreachable
175
- // and the branch reading it never runs. There is no symptom: the slot reads as
176
- // a perfectly good value everywhere.
177
- //
178
- // The way this happens is **inheritance**, which is why the type can't catch
179
- // it: a `maybe*` override of a plain base slot inherits the base's concrete
180
- // default through the definition spread, and the override's own literal looks
181
- // right. `LinearMafDisplay.height` over `BaseLinearDisplay`'s `number`/100 is
182
- // the case in the repo, and only that display's own tests noticed.
123
+ // A concrete default makes a maybe* slot's unset state unreachable, with no
124
+ // symptom. It arrives by inheritance: an override spread over a base slot's
125
+ // default.
183
126
  if (defaultValue !== undefined && MAYBE_TYPES.has(type)) {
184
127
  throw new Error(`a "${type}" slot cannot have a concrete defaultValue (${JSON.stringify(defaultValue)}): unset is the state a maybe* slot exists for, and no config can spell undefined, so it would be unreachable. If this slot overrides a base slot, the base's defaultValue merged in — state 'defaultValue: undefined' to overwrite it. Otherwise use the non-maybe form of the type.`);
185
128
  }
@@ -197,8 +140,6 @@ export default function ConfigSlot(definition) {
197
140
  ? value
198
141
  : refusingCallbacks(value), defaultValue);
199
142
  }
200
- // The author writes an enum slot's plain vocabulary, and the slot's shape
201
- // around it is added here.
202
143
  function enumShaped(type, model) {
203
144
  return type === 'maybeStringEnum'
204
145
  ? types.maybe(model)
@@ -206,14 +147,7 @@ function enumShaped(type, model) {
206
147
  ? types.array(model)
207
148
  : model;
208
149
  }
209
- /**
210
- * New value when converting a fixed-value slot to a jexl callback. Already-
211
- * callback values are returned unchanged.
212
- *
213
- * A single JSON.stringify covers every type: `jexl:${42}` and
214
- * `jexl:${JSON.stringify(42)}` are identical for numbers/booleans, and the rest
215
- * need the quoting/serialization anyway.
216
- */
150
+ /** A fixed value as the `jexl:` callback that returns it. */
217
151
  export function toCallbackValue(value) {
218
152
  return isCallbackValue(value) ? value : `jexl:${JSON.stringify(value)}`;
219
153
  }
@@ -72,6 +72,7 @@ export function FormatDetailsConfigSchemaFactory() {
72
72
  depth: {
73
73
  type: 'maybeNumber',
74
74
  description: 'levels of subfeature the formatDetails.subfeatures callback runs on, default 2',
75
+ advanced: true,
75
76
  },
76
77
  /**
77
78
  * #slot formatDetails.maxDepth
@@ -82,6 +83,7 @@ export function FormatDetailsConfigSchemaFactory() {
82
83
  maxDepth: {
83
84
  type: 'maybeNumber',
84
85
  description: 'hide subfeatures nested deeper than this, default no limit',
86
+ advanced: true,
85
87
  },
86
88
  });
87
89
  }
@@ -1,15 +1,8 @@
1
1
  import type { AnyConfigurationModel, AnyConfigurationSnapshot, ConfigurationSchemaForModel, ConfigurationSlotName, ConfigurationSlotPath, ConfigurationSlotPathValue, ConfigurationSlotValue } from './types.ts';
2
2
  /**
3
3
  * #api core/configuration
4
- * Reads a configuration value from a state model that has a `.configuration`
5
- * member (a track or display state model). For a raw configuration model, use
6
- * `readConfObject` instead.
7
- *
8
- * **This is exactly `readConfObject(model.configuration, path)`** — sugar for
9
- * the `.configuration` hop, and nothing more. The two readers carry the same
10
- * slot-name check, so reaching for the other one does not get a typo past tsc.
11
- * It does not consult the session and has no per-slot behavior; what you read is
12
- * what the track stores.
4
+ * Reads a configuration value from a track or display state model: exactly
5
+ * `readConfObject(model.configuration, path)`, with the same slot-name check.
13
6
  *
14
7
  * @param model - object containing a 'configuration' member
15
8
  * @param slotPaths - array of paths to read
@@ -29,33 +22,11 @@ export declare function getConf<CONFMODEL extends AnyConfigurationModel, const S
29
22
  * `setConf(self, ['scales', 'y', 'domainMin'], 5)`. Takes the display or track
30
23
  * model, or a config node directly.
31
24
  *
32
- * **The path length says what the write replaces.** A path ending on a slot
33
- * writes that slot and leaves the object around it alone; a path ending on a
34
- * sub-schema replaces the whole object, which is how a channel whose members
35
- * move together — a facet's field and the domain that field's values order —
36
- * gets written without a stale member surviving underneath.
37
- *
38
- * **Prefer this over a bare `self.configuration.setSlot('x', v)`.** The
39
- * constraint here mirrors `getConf`'s, so on a model with a concrete schema an
40
- * unknown slot name is a compile error. `setSlot` itself stays untyped on
41
- * purpose — the config editor's slot facade routes dynamic slot names through
42
- * it (`configurationSchema.ts`) — and guards the name at runtime instead
43
- * (ADR-052), so a misspelled write is diagnosed one way or the other.
44
- *
45
- * **The read is the half with no diagnostic at all.** `getConf` for a name the
46
- * schema doesn't declare returns `undefined` and reports nothing, at any layer,
47
- * so the slot keeps reading as its default forever. Which makes the
48
- * compile-time constraint worth keeping *reachable*: it is only as good as the
49
- * schema of the holder handed in, and a holder widened to
50
- * `AnyConfigurationModel` switches it off entirely — the trap a mixin casting
51
- * to reach its host walks into. Every such cast names a concrete schema instead
52
- * (`ConfigModelForFields`, or the base schema when the slot is the base's), and
53
- * `HostChecksSlotNames` pins each one.
54
- *
55
- * A wrong *value* type still throws at runtime (MST type-checks the assignment)
56
- * rather than at compile time. `value` is deliberately `unknown` because
57
- * `undefined` is a legitimate write — it resets a slot to its schema default —
58
- * and the declared slot value type doesn't include it.
25
+ * A path ending on a slot writes that slot; one ending on a sub-schema replaces
26
+ * the whole object, so a channel's members (a facet's field and its domain)
27
+ * move together. On a concrete schema an unknown slot name is a compile error,
28
+ * and `setSlot` refuses one at runtime (ADR-052). A wrong value type throws at
29
+ * runtime; `null` or `undefined` resets the slot to its default (ADR-146).
59
30
  *
60
31
  * @param target - a model with a `configuration` member, or a config node
61
32
  * @param slotPath - the slot to write, or the path to one
@@ -14,33 +14,11 @@ export function getConf(model, slotPath, args = {}) {
14
14
  * `setConf(self, ['scales', 'y', 'domainMin'], 5)`. Takes the display or track
15
15
  * model, or a config node directly.
16
16
  *
17
- * **The path length says what the write replaces.** A path ending on a slot
18
- * writes that slot and leaves the object around it alone; a path ending on a
19
- * sub-schema replaces the whole object, which is how a channel whose members
20
- * move together — a facet's field and the domain that field's values order —
21
- * gets written without a stale member surviving underneath.
22
- *
23
- * **Prefer this over a bare `self.configuration.setSlot('x', v)`.** The
24
- * constraint here mirrors `getConf`'s, so on a model with a concrete schema an
25
- * unknown slot name is a compile error. `setSlot` itself stays untyped on
26
- * purpose — the config editor's slot facade routes dynamic slot names through
27
- * it (`configurationSchema.ts`) — and guards the name at runtime instead
28
- * (ADR-052), so a misspelled write is diagnosed one way or the other.
29
- *
30
- * **The read is the half with no diagnostic at all.** `getConf` for a name the
31
- * schema doesn't declare returns `undefined` and reports nothing, at any layer,
32
- * so the slot keeps reading as its default forever. Which makes the
33
- * compile-time constraint worth keeping *reachable*: it is only as good as the
34
- * schema of the holder handed in, and a holder widened to
35
- * `AnyConfigurationModel` switches it off entirely — the trap a mixin casting
36
- * to reach its host walks into. Every such cast names a concrete schema instead
37
- * (`ConfigModelForFields`, or the base schema when the slot is the base's), and
38
- * `HostChecksSlotNames` pins each one.
39
- *
40
- * A wrong *value* type still throws at runtime (MST type-checks the assignment)
41
- * rather than at compile time. `value` is deliberately `unknown` because
42
- * `undefined` is a legitimate write — it resets a slot to its schema default —
43
- * and the declared slot value type doesn't include it.
17
+ * A path ending on a slot writes that slot; one ending on a sub-schema replaces
18
+ * the whole object, so a channel's members (a facet's field and its domain)
19
+ * move together. On a concrete schema an unknown slot name is a compile error,
20
+ * and `setSlot` refuses one at runtime (ADR-052). A wrong value type throws at
21
+ * runtime; `null` or `undefined` resets the slot to its default (ADR-146).
44
22
  *
45
23
  * @param target - a model with a `configuration` member, or a config node
46
24
  * @param slotPath - the slot to write, or the path to one
@@ -49,13 +27,7 @@ export function getConf(model, slotPath, args = {}) {
49
27
  export function setConf(target, slotPath, value) {
50
28
  writeConfPath(isConfigurationModel(target) ? target : target.configuration, typeof slotPath === 'string' ? [slotPath] : slotPath, value);
51
29
  }
52
- /**
53
- * Walk to the node the last segment names a member of, then write that member
54
- * through whichever of the node's two write actions the member is: a
55
- * sub-schema, single or a collection, is replaced whole, and a slot goes
56
- * through `setSlot` so ADR-052's name guard and the value-type guard both run.
57
- * MST's `applyPatch` takes the same path and runs neither guard.
58
- */
30
+ // through setSlot or setSubschema, whose guards MST's `applyPatch` would skip
59
31
  function writeConfPath(conf, path, value) {
60
32
  if (!path.length) {
61
33
  throw new Error('setConf needs a slot name or a path to one');
@@ -69,9 +41,7 @@ function writeConfPath(conf, path, value) {
69
41
  node = child;
70
42
  }
71
43
  const leaf = path.at(-1);
72
- // Classify off the declaration, never off what the member holds: a
73
- // `stringArray` slot holds an array node, and an optional sub-schema nobody
74
- // has written yet holds nothing.
44
+ // off the declaration: a stringArray slot holds an array node too
75
45
  const declared = getConfigurationSchemaDefinition(node)?.[leaf];
76
46
  if (isConfigurationSchemaType(declared)) {
77
47
  // eslint-disable-next-line no-restricted-syntax -- this is setConf
@@ -3,14 +3,15 @@ export type { ConfigurationSchemaDefinition, RetiredSpelling, } from './configur
3
3
  export { ConfigurationSchemaUnion, arraySlotUnion, } from './configurationSchemaUnion.ts';
4
4
  export type { ConfigurationSchemaUnionType } from './configurationSchemaUnion.ts';
5
5
  export type { ConfigSlotDefinition } from './configurationSlot.ts';
6
- export type { AnyConfiguration, AnyConfigurationModel, AnyConfigurationSchemaType, AnyConfigurationSnapshot, ConfigModelForFields, ConfigNodeActions, ConfigNodeBrand, ConfigNodeProps, ConfigurationSchemaForModel, ConfigurationSlotName, ConfigurationSlotPath, ConfigurationSlotPathValue, ConfigurationSlotValue, ConfigurationSnapshot, HostChecksSlotNames, IdentifierSlotDef, PluggableConfigNode, TypeSlotDef, } from './types.ts';
6
+ export type { AnyConfiguration, AnyConfigurationModel, AnyConfigurationSchemaType, AnyConfigurationSnapshot, AnyTrackConfig, ConfigModelForFields, ConfigNodeActions, ConfigNodeBrand, ConfigNodeProps, ConfigurationSchemaForModel, ConfigurationSlotName, ConfigurationSlotPath, ConfigurationSlotPathValue, ConfigurationSlotValue, ConfigurationSnapshot, HostChecksSlotNames, IdentifierSlotDef, PluggableConfigNode, TrackConfigEntry, TypeSlotDef, } from './types.ts';
7
7
  export { getConfigurationSchemaDefinition, getConfigurationSchemaMetadata, getConfigurationSchemaUnion, } from './schemaRegistry.ts';
8
8
  export type { ConfigurationSchemaUnionMetadata } from './schemaRegistry.ts';
9
9
  export { getTypeNamesFromExplicitlyTypedUnion, isBareConfigurationSchemaType, isConfigurationModel, isConfigurationSchemaType, isConstantEntry, isSlotDefinitionEntry, } from './schemaTypes.ts';
10
+ export { partitionAdvanced } from './partitionAdvanced.ts';
10
11
  export { FormatAboutConfigSchemaFactory } from './formatAboutConfigSchema.ts';
11
12
  export { DEFAULT_FORMAT_DETAILS_DEPTH, FormatDetailsConfigSchemaFactory, } from './formatDetailsConfigSchema.ts';
12
13
  export { mergeFormatCallbacks } from './mergeFormatCallbacks.ts';
13
- export { readConfObject, readConfigValue } from './readConfObject.ts';
14
+ export { evaluateForFeature, readConfObject, readConfigValue, } from './readConfObject.ts';
14
15
  export { applyConfSettings, getConf, setConf } from './getConf.ts';
15
16
  export type { ConfSettingsReport } from './getConf.ts';
16
17
  export { fillLocations } from './fillLocations.ts';
@@ -30,3 +31,5 @@ export { toCallbackValue, toFixedValue } from './configurationSlot.ts';
30
31
  export { getSlotDefinition, isConfigurationSlot, makeSlotFacade, slotChoices, slotValueRefusal, } from './slotFacade.ts';
31
32
  export type { SlotFacade } from './slotFacade.ts';
32
33
  export { fullConfSnapshot } from './fullConfSnapshot.ts';
34
+ export { PLOT_VOCABULARY, liftPlot, parsePlot, plotKeysOf, plotOf, plotWrites, } from './plot.ts';
35
+ export type { Plot, PlotExample } from './plot.ts';
@@ -7,10 +7,11 @@ export { getConfigurationSchemaDefinition,
7
7
  // way to write the config
8
8
  getConfigurationSchemaMetadata, getConfigurationSchemaUnion, } from './schemaRegistry.js';
9
9
  export { getTypeNamesFromExplicitlyTypedUnion, isBareConfigurationSchemaType, isConfigurationModel, isConfigurationSchemaType, isConstantEntry, isSlotDefinitionEntry, } from './schemaTypes.js';
10
+ export { partitionAdvanced } from './partitionAdvanced.js';
10
11
  export { FormatAboutConfigSchemaFactory } from './formatAboutConfigSchema.js';
11
12
  export { DEFAULT_FORMAT_DETAILS_DEPTH, FormatDetailsConfigSchemaFactory, } from './formatDetailsConfigSchema.js';
12
13
  export { mergeFormatCallbacks } from './mergeFormatCallbacks.js';
13
- export { readConfObject, readConfigValue } from './readConfObject.js';
14
+ export { evaluateForFeature, readConfObject, readConfigValue, } from './readConfObject.js';
14
15
  export { applyConfSettings, getConf, setConf } from './getConf.js';
15
16
  export { fillLocations } from './fillLocations.js';
16
17
  export { applyRetiredSpellings, liftRetiredSpellings, preProcessConfigSnapshot, preProcessSnapshotWith, } from './snapshotPreprocess.js';
@@ -25,3 +26,4 @@ export { evaluateJexl, isCallbackValue } from './slotValueUtils.js';
25
26
  export { toCallbackValue, toFixedValue } from './configurationSlot.js';
26
27
  export { getSlotDefinition, isConfigurationSlot, makeSlotFacade, slotChoices, slotValueRefusal, } from './slotFacade.js';
27
28
  export { fullConfSnapshot } from './fullConfSnapshot.js';
29
+ export { PLOT_VOCABULARY, liftPlot, parsePlot, plotKeysOf, plotOf, plotWrites, } from './plot.js';
@@ -0,0 +1,12 @@
1
+ import type { AnyConfigurationModel } from './types.ts';
2
+ /**
3
+ * Splits a config snapshot in two: the slots a schema flags `advanced` or gives
4
+ * a `contextVariable` (a `jexl:` callback), and everything else. A sub-schema
5
+ * member splits member by member and appears on a side only if something landed
6
+ * there. A key the schema does not define as a slot (`trackId`, a typed
7
+ * schema's `type`, an array of sub-schemas) stays with the rest.
8
+ */
9
+ export declare function partitionAdvanced(node: AnyConfigurationModel, snapshot: Record<string, unknown>): {
10
+ rest: Record<string, unknown>;
11
+ advanced: Record<string, unknown>;
12
+ };
@@ -0,0 +1,42 @@
1
+ import { isPlainObject } from '../util/objectUtils.js';
2
+ import { getConfigurationSchemaDefinition } from './schemaRegistry.js';
3
+ import { isConfigurationModel, isSlotDefinitionEntry } from './schemaTypes.js';
4
+ // wider than the config editor's toggle, which reads only `advanced`: a callback
5
+ // is editable there, and only jexl noise to a reader of the About dialog
6
+ function foldsIntoAdvancedCard(def) {
7
+ return !!def.advanced || !!def.contextVariable?.length;
8
+ }
9
+ /**
10
+ * Splits a config snapshot in two: the slots a schema flags `advanced` or gives
11
+ * a `contextVariable` (a `jexl:` callback), and everything else. A sub-schema
12
+ * member splits member by member and appears on a side only if something landed
13
+ * there. A key the schema does not define as a slot (`trackId`, a typed
14
+ * schema's `type`, an array of sub-schemas) stays with the rest.
15
+ */
16
+ export function partitionAdvanced(node, snapshot) {
17
+ const definition = getConfigurationSchemaDefinition(node);
18
+ const rest = {};
19
+ const advanced = {};
20
+ for (const [key, value] of Object.entries(snapshot)) {
21
+ const def = definition?.[key];
22
+ if (isSlotDefinitionEntry(def)) {
23
+ const side = foldsIntoAdvancedCard(def) ? advanced : rest;
24
+ side[key] = value;
25
+ continue;
26
+ }
27
+ const child = node[key];
28
+ if (isPlainObject(value) && isConfigurationModel(child)) {
29
+ const split = partitionAdvanced(child, value);
30
+ if (Object.keys(split.rest).length) {
31
+ rest[key] = split.rest;
32
+ }
33
+ if (Object.keys(split.advanced).length) {
34
+ advanced[key] = split.advanced;
35
+ }
36
+ }
37
+ else {
38
+ rest[key] = value;
39
+ }
40
+ }
41
+ return { rest, advanced };
42
+ }
@@ -0,0 +1,67 @@
1
+ import type { AnyConfigurationModel } from './types.ts';
2
+ /**
3
+ * #api core/configuration
4
+ * The grammar's settings, by the slot name every display that has one gives
5
+ * it, and what each holds: what "Edit plot..." shows and an agent reads as a
6
+ * display's `plot`. A display's plot is the ones its config declares
7
+ * (`plotKeysOf`).
8
+ */
9
+ export declare const PLOT_VOCABULARY: Readonly<Record<string, string>>;
10
+ /**
11
+ * #api core/configuration
12
+ * A display's plot settings as written: a key left out is left alone, and
13
+ * `null` resets that setting. Untyped, since a value may be a shorthand the
14
+ * schema lifts, and the schema is what judges it.
15
+ */
16
+ export type Plot = Record<string, unknown>;
17
+ /**
18
+ * #api core/configuration
19
+ * A worked example of a display's plot: the text it fills the editor with,
20
+ * and what it does.
21
+ */
22
+ export interface PlotExample {
23
+ plot: string;
24
+ description: string;
25
+ }
26
+ /**
27
+ * #api core/configuration
28
+ * The plot keys a display config declares.
29
+ */
30
+ export declare function plotKeysOf(conf: AnyConfigurationModel): string[];
31
+ /**
32
+ * #api core/configuration
33
+ * A display's plot as declared, defaults left off. A list sitting at a
34
+ * default it shares with no other display, a default plot's marks, shows its
35
+ * entries, since they are what is drawn.
36
+ */
37
+ export declare function plotOf(conf: AnyConfigurationModel): Plot;
38
+ /**
39
+ * #api core/configuration
40
+ * The text as a plot, refusing a key the display's plot does not hold. The
41
+ * schema is the parser past this point.
42
+ */
43
+ export declare function parsePlot(text: string, keys: readonly string[]): Plot;
44
+ /**
45
+ * #api core/configuration
46
+ * A draft as the display's config would hold it, through the schema's own
47
+ * lift and checks: a shorthand becomes its object, a default falls off, and
48
+ * what a config file is refused for throws, a key outside the plot included.
49
+ * The node is never attached, so nothing on the display is touched; a display
50
+ * reads its typed members off it. The draft is copied first, since MST
51
+ * freezes what it creates from.
52
+ */
53
+ export declare function liftPlot(conf: AnyConfigurationModel, draft: Plot): AnyConfigurationModel;
54
+ /**
55
+ * #api core/configuration
56
+ * What applying a draft writes: each setting whose lifted value differs from
57
+ * the plot, as the lifted value, and `null` for one it resets. A value
58
+ * spelled another way but lifting to the same setting writes nothing.
59
+ * Throws a refusal before anything is written.
60
+ */
61
+ export declare function plotWrites(conf: AnyConfigurationModel, draft: Plot): Record<string, unknown>;
62
+ /**
63
+ * What a lifted plot's colour objects and `scales.y` say together that the
64
+ * display cannot draw as written, as corner-notice lines: each colour object
65
+ * under its schema's `fieldPresets`, as `jbrowse validate` judges it.
66
+ */
67
+ export declare function schemaPlotProblems(lifted: AnyConfigurationModel): string[];