@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.
- package/README.md +210 -80
- package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.js +0 -2
- package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -0
- package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +14 -1
- package/esm/BaseFeatureWidget/BaseFeatureDetail/BasicValue.js +22 -6
- package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.js +3 -4
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.d.ts +2 -0
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +5 -5
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +1 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.d.ts +3 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.js +2 -2
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.d.ts +2 -1
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.js +7 -14
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +3 -3
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/useSequenceData.js +8 -5
- package/esm/BaseFeatureWidget/stateModelFactory.d.ts +7 -0
- package/esm/BaseFeatureWidget/stateModelFactory.js +11 -0
- package/esm/BaseFeatureWidget/types.d.ts +2 -0
- package/esm/CorePlugin.js +32 -1
- package/esm/PluginManager.d.ts +6 -3
- package/esm/PluginManager.js +6 -3
- package/esm/ReExports/coreModules.generated.js +291 -283
- package/esm/ReExports/coreWorkerModules.generated.js +288 -281
- package/esm/ReExports/list.js +10 -2
- package/esm/TextSearch/TextSearchManager.d.ts +3 -3
- package/esm/TextSearch/TextSearchManager.js +6 -13
- package/esm/configuration/configurationSchema.d.ts +33 -95
- package/esm/configuration/configurationSchema.js +50 -218
- package/esm/configuration/configurationSlot.d.ts +18 -64
- package/esm/configuration/configurationSlot.js +31 -97
- package/esm/configuration/formatDetailsConfigSchema.js +2 -0
- package/esm/configuration/getConf.d.ts +7 -36
- package/esm/configuration/getConf.js +7 -37
- package/esm/configuration/index.d.ts +5 -2
- package/esm/configuration/index.js +3 -1
- package/esm/configuration/partitionAdvanced.d.ts +12 -0
- package/esm/configuration/partitionAdvanced.js +42 -0
- package/esm/configuration/plot.d.ts +67 -0
- package/esm/configuration/plot.js +175 -0
- package/esm/configuration/readConfObject.d.ts +15 -17
- package/esm/configuration/readConfObject.js +19 -50
- package/esm/configuration/schemaTypes.js +5 -1
- package/esm/configuration/snapshotPreprocess.js +1 -3
- package/esm/configuration/types.d.ts +60 -159
- package/esm/pluggableElementTypes/DisplayType.d.ts +14 -0
- package/esm/pluggableElementTypes/DisplayType.js +13 -0
- package/esm/pluggableElementTypes/RpcMethodType.d.ts +7 -0
- package/esm/pluggableElementTypes/RpcMethodType.js +9 -0
- package/esm/pluggableElementTypes/WidgetType.d.ts +6 -0
- package/esm/pluggableElementTypes/WidgetType.js +6 -0
- package/esm/pluggableElementTypes/addMenuItems.d.ts +2 -1
- package/esm/pluggableElementTypes/addMenuItems.js +2 -1
- package/esm/pluggableElementTypes/index.js +1 -8
- package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +58 -1
- package/esm/pluggableElementTypes/models/BaseDisplayModel.js +84 -2
- package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +2 -4
- package/esm/pluggableElementTypes/models/BaseTrackModel.js +2 -4
- package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +2 -2
- package/esm/pluggableElementTypes/models/BaseViewModel.js +1 -1
- package/esm/pluggableElementTypes/models/baseTrackConfig.js +3 -0
- package/esm/rpc/RpcRegistry.d.ts +2 -1
- package/esm/rpc/byteBudget.d.ts +7 -0
- package/esm/rpc/byteBudget.js +10 -0
- package/esm/rpc/coreRpcMethods.d.ts +1 -1
- package/esm/rpc/coreRpcMethods.js +1 -1
- package/esm/rpc/explainTransferError.js +3 -3
- package/esm/rpc/methods/CoreLoadTrackCode.d.ts +17 -0
- package/esm/rpc/methods/CoreLoadTrackCode.js +24 -0
- package/esm/ui/AppLogo.js +43 -3
- package/esm/ui/JexlFilterDialog.d.ts +1 -1
- package/esm/ui/JexlFilterDialog.js +2 -3
- package/esm/ui/buildExtraTrackMenuItems.d.ts +2 -2
- package/esm/ui/colors.d.ts +41 -30
- package/esm/ui/colors.js +81 -44
- package/esm/ui/multiTrackMenuItems.d.ts +2 -2
- package/esm/util/TimeTraveller.d.ts +3 -0
- package/esm/util/TimeTraveller.js +34 -2
- package/esm/util/alignedBytes.d.ts +19 -0
- package/esm/util/alignedBytes.js +37 -0
- package/esm/util/analytics.d.ts +5 -6
- package/esm/util/analytics.js +5 -3
- package/esm/util/baseDisplayConfig.d.ts +8 -0
- package/esm/util/baseDisplayConfig.js +27 -0
- package/esm/util/bezierConnector.js +5 -1
- package/esm/util/cachedSetup.d.ts +16 -6
- package/esm/util/cachedSetup.js +108 -38
- package/esm/util/categoricalField.d.ts +23 -3
- package/esm/util/categoricalField.js +36 -3
- package/esm/util/cellMatches.d.ts +23 -0
- package/esm/util/cellMatches.js +264 -0
- package/esm/util/cellsStep.d.ts +27 -0
- package/esm/util/cellsStep.js +439 -0
- package/esm/util/color-bits/parse.js +22 -11
- package/esm/util/colorRamp.js +6 -0
- package/esm/util/colorScale.d.ts +9 -2
- package/esm/util/colorScale.js +33 -2
- package/esm/util/colorSchemes.d.ts +4 -2
- package/esm/util/colorSchemes.js +4 -1
- package/esm/util/copyToClipboard.js +8 -1
- package/esm/util/createAbortRotation.js +3 -3
- package/esm/util/featureTransforms.d.ts +7 -6
- package/esm/util/featureTransforms.js +131 -479
- package/esm/util/fetchPhases.d.ts +1 -1
- package/esm/util/formatBytes.js +1 -1
- package/esm/util/formatGuessers.d.ts +25 -14
- package/esm/util/formatGuessers.js +42 -27
- package/esm/util/formatRelativeTime.js +1 -1
- package/esm/util/getLocationUri.d.ts +2 -1
- package/esm/util/getLocationUri.js +8 -2
- package/esm/util/groupKeys.d.ts +4 -5
- package/esm/util/groupKeys.js +7 -6
- package/esm/util/intervals.js +9 -4
- package/esm/util/io/index.js +3 -1
- package/esm/util/isoformRank.d.ts +39 -0
- package/esm/util/isoformRank.js +118 -0
- package/esm/util/jexlFilters.d.ts +31 -49
- package/esm/util/jexlFilters.js +29 -39
- package/esm/util/locString.js +5 -2
- package/esm/util/markEncoding.js +48 -8
- package/esm/util/markEncodingTypes.d.ts +11 -2
- package/esm/util/quantileExtent.d.ts +22 -0
- package/esm/util/quantileExtent.js +24 -1
- package/esm/util/springAnimate.js +1 -1
- package/esm/util/stepTables.d.ts +14 -0
- package/esm/util/stepTables.js +38 -4
- package/esm/util/systemColorScheme.d.ts +4 -0
- package/esm/util/systemColorScheme.js +21 -0
- package/esm/util/tabix.d.ts +7 -3
- package/esm/util/tabix.js +3 -3
- package/esm/util/temporaryAssembly.d.ts +2 -2
- package/esm/util/temporaryAssembly.js +2 -3
- package/esm/util/trackConfigDelta.d.ts +1 -1
- package/esm/util/tracks.d.ts +28 -45
- package/esm/util/tracks.js +71 -82
- package/esm/util/types/index.d.ts +15 -7
- package/esm/util/types/index.js +21 -2
- package/esm/util/unknownSnapshotKeys.d.ts +3 -1
- package/esm/util/unknownSnapshotKeys.js +5 -11
- package/esm/util/usePanZoom.js +10 -3
- package/esm/util/useRafCommit.js +1 -0
- package/esm/util/useRowVirtualScroll.d.ts +1 -0
- package/esm/util/withFeatureDetails.js +4 -0
- package/package.json +13 -9
- package/esm/rpc/methods/CoreLoadAdapterCode.d.ts +0 -13
- package/esm/rpc/methods/CoreLoadAdapterCode.js +0 -16
|
@@ -10,22 +10,13 @@ import { preProcessSnapshotWith } from './snapshotPreprocess.js';
|
|
|
10
10
|
function hookList(hook) {
|
|
11
11
|
return hook === undefined ? [] : Array.isArray(hook) ? hook : [hook];
|
|
12
12
|
}
|
|
13
|
-
// base first, then child. Returns the single function unchanged when only one
|
|
14
|
-
// side declares the hook, so the common case never allocates a chain.
|
|
15
13
|
function chainHooks(base, child) {
|
|
16
14
|
return base && child
|
|
17
15
|
? [...hookList(base), ...hookList(child)]
|
|
18
16
|
: (child ?? base);
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* are added and sub-schema entries replaced wholesale, but a slot the subclass
|
|
23
|
-
* **redeclares merges field-by-field over the base's** — so an override states
|
|
24
|
-
* only what actually differs and inherits the rest. (`type` comes along
|
|
25
|
-
* regardless: it is what marks an entry as a slot rather than a nested
|
|
26
|
-
* sub-schema, per `isSlotDefinitionEntry`.) The cases are pinned by the
|
|
27
|
-
* "baseConfiguration slot override merge" tests.
|
|
28
|
-
*/
|
|
18
|
+
// A redeclared slot merges field-by-field over the base's, so an override states
|
|
19
|
+
// only what differs; a sub-schema or a constant is replaced whole.
|
|
29
20
|
function mergeSchemaDefinition(baseDefinition, childDefinition) {
|
|
30
21
|
const merged = {
|
|
31
22
|
...baseDefinition,
|
|
@@ -33,8 +24,6 @@ function mergeSchemaDefinition(baseDefinition, childDefinition) {
|
|
|
33
24
|
};
|
|
34
25
|
for (const [slot, childEntry] of Object.entries(childDefinition)) {
|
|
35
26
|
const baseEntry = baseDefinition[slot];
|
|
36
|
-
// both sides must be slot definitions: a sub-schema (or a constant) is an
|
|
37
|
-
// opaque entry with no fields to fold, so it keeps replace semantics
|
|
38
27
|
if (isSlotDefinitionEntry(baseEntry) && isSlotDefinitionEntry(childEntry)) {
|
|
39
28
|
merged[slot] = { ...baseEntry, ...childEntry };
|
|
40
29
|
}
|
|
@@ -58,32 +47,19 @@ function preprocessConfigurationSchemaArguments(modelName, inputSchemaDefinition
|
|
|
58
47
|
if (typeof modelName !== 'string') {
|
|
59
48
|
throw new Error('first arg must be string name of the model that this config schema goes with');
|
|
60
49
|
}
|
|
61
|
-
// if we have a base configuration schema that we are
|
|
62
|
-
// extending, grab the slot definitions from that
|
|
63
50
|
let schemaDefinition = inputSchemaDefinition;
|
|
64
51
|
let options = inputOptions;
|
|
65
52
|
const baseMeta = inputOptions.baseConfiguration
|
|
66
53
|
? getConfigurationSchemaMetadata(inputOptions.baseConfiguration)
|
|
67
54
|
: undefined;
|
|
68
|
-
// A base with no registry entry used to be skipped in silence, producing a
|
|
69
|
-
// schema missing every inherited slot with nothing thrown anywhere, and
|
|
70
|
-
// reachable without doing anything obviously wrong:
|
|
71
|
-
// `pluginManager.pluggableConfigSchemaType(…)` hands back a `types.union`,
|
|
72
|
-
// which carries no slot table of its own.
|
|
73
55
|
if (inputOptions.baseConfiguration && !baseMeta) {
|
|
74
56
|
throw new Error(`${modelName}'s baseConfiguration is not a configuration schema: it has no registered slot table, so every slot it was meant to inherit would be dropped silently. Pass the type ConfigurationSchema() returned, not a union (pluginManager.pluggableConfigSchemaType) or a plain MST model.`);
|
|
75
57
|
}
|
|
76
58
|
if (baseMeta) {
|
|
77
59
|
schemaDefinition = mergeSchemaDefinition(baseMeta.definition, schemaDefinition);
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
// replace-semantics meant no track config schema could ever declare its
|
|
82
|
-
// own without silently dropping display-stub injection and the legacy-key
|
|
83
|
-
// migration, and a subclass stating one requirement would have dropped
|
|
84
|
-
// every one its base stated. They compose instead, base first, and
|
|
85
|
-
// `retired` merges per key so a subclass adds a spelling without dropping
|
|
86
|
-
// the ones its base retired. See MergedConfigurationSchemaOptions.
|
|
60
|
+
// The hooks, `requires` and `retired` compose, base first, where every
|
|
61
|
+
// other option is replaced by the child's: a track schema's own
|
|
62
|
+
// preProcessSnapshot must not drop the base's display-stub injection.
|
|
87
63
|
const basePreProcess = baseMeta.options.preProcessSnapshot;
|
|
88
64
|
const childPreProcess = inputOptions.preProcessSnapshot;
|
|
89
65
|
const requires = [
|
|
@@ -109,7 +85,6 @@ function preprocessConfigurationSchemaArguments(modelName, inputSchemaDefinition
|
|
|
109
85
|
return { schemaDefinition, options };
|
|
110
86
|
}
|
|
111
87
|
function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
112
|
-
// now assemble the MST model of the configuration schema
|
|
113
88
|
const modelDefinition = {};
|
|
114
89
|
if (options.explicitlyTyped) {
|
|
115
90
|
modelDefinition.type = types.optional(types.literal(modelName), modelName);
|
|
@@ -118,30 +93,19 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
118
93
|
if (identifier) {
|
|
119
94
|
modelDefinition[identifier] = types.identifier;
|
|
120
95
|
}
|
|
121
|
-
// String/number entries in the schema definition become volatile instance
|
|
122
|
-
// constants (read via `model.someName`). Per-slot metadata lives in the
|
|
123
|
-
// schema registry (a WeakMap keyed by the MST type, see schemaRegistry.ts),
|
|
124
|
-
// not on the instance.
|
|
125
96
|
const volatileConstants = {};
|
|
126
|
-
// The members `setSubschema` replaces: a single sub-schema, swapped for a
|
|
127
|
-
// node built from the data, or a collection of them, assigned whole so MST
|
|
128
|
-
// reconciles the entries. Collected as the loop classifies each entry.
|
|
129
97
|
const subSchemaKeys = new Set();
|
|
130
98
|
const collectionKeys = new Set();
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
// which setSlot may write. Same collect-as-you-classify as subSchemaKeys.
|
|
99
|
+
// narrower than `modelDefinition`, which also holds the identifier and the
|
|
100
|
+
// sub-schemas, neither of which setSlot may write
|
|
134
101
|
const slotKeys = new Set();
|
|
135
102
|
const storesNull = new Set();
|
|
136
103
|
const featureFields = new Set();
|
|
137
104
|
const takesNoCallback = new Map();
|
|
138
105
|
for (const [slotName, slotDefinition] of Object.entries(schemaDefinition)) {
|
|
139
106
|
if (isConfigurationSchemaType(slotDefinition)) {
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// wrapped so an empty collection is likewise omitted from the snapshot,
|
|
143
|
-
// unless it is wrapped in a default of its own: a display built on
|
|
144
|
-
// another names its own default plot that way.
|
|
107
|
+
// an empty collection strips from the snapshot as an all-default
|
|
108
|
+
// sub-schema does, unless it names a default of its own
|
|
145
109
|
if (isArrayType(slotDefinition) || isMapType(slotDefinition)) {
|
|
146
110
|
modelDefinition[slotName] = isOptionalType(slotDefinition)
|
|
147
111
|
? slotDefinition
|
|
@@ -157,7 +121,6 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
157
121
|
volatileConstants[slotName] = slotDefinition;
|
|
158
122
|
}
|
|
159
123
|
else if (isSlotDefinitionEntry(slotDefinition)) {
|
|
160
|
-
// slotDefinition is narrowed to ConfigSlotDefinition here (no cast)
|
|
161
124
|
slotKeys.add(slotName);
|
|
162
125
|
try {
|
|
163
126
|
modelDefinition[slotName] = ConfigSlot(slotDefinition);
|
|
@@ -176,8 +139,6 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
176
139
|
}
|
|
177
140
|
}
|
|
178
141
|
else if (typeof slotDefinition === 'object') {
|
|
179
|
-
// an object that's neither a sub-schema nor a slot is almost always a
|
|
180
|
-
// slot definition missing its required `type` field
|
|
181
142
|
throw new Error(`no type set for config slot ${modelName}.${slotName}`);
|
|
182
143
|
}
|
|
183
144
|
else {
|
|
@@ -188,14 +149,11 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
188
149
|
checkShorthand(modelName, schemaDefinition, options.shorthand);
|
|
189
150
|
let completeModel = types
|
|
190
151
|
.model(`${modelName}ConfigurationSchema`, modelDefinition)
|
|
191
|
-
// annotated so `ConfigurationSchemaType['Type']`, which
|
|
192
|
-
//
|
|
193
|
-
// nothing derived off them keeps a signature), cannot drift from them
|
|
152
|
+
// annotated so `ConfigurationSchemaType['Type']`, which names these by
|
|
153
|
+
// hand, cannot drift from them
|
|
194
154
|
.actions((self) => ({
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
// collection it is the whole list or map, and `null` resets it to its
|
|
198
|
-
// default, empty unless the collection names one.
|
|
155
|
+
// `data` is whatever the sub-schema's preProcessSnapshot takes; a
|
|
156
|
+
// collection takes the whole list or map, and `null` resets it
|
|
199
157
|
setSubschema(slotName, data) {
|
|
200
158
|
if (collectionKeys.has(slotName)) {
|
|
201
159
|
self[slotName] = data ?? undefined;
|
|
@@ -210,28 +168,12 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
210
168
|
self[slotName] = newSchema;
|
|
211
169
|
return newSchema;
|
|
212
170
|
},
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
// **Don't weaken the membership check to a warning, and don't check
|
|
218
|
-
// against `modelDefinition`** — that also holds the identifier and the
|
|
219
|
-
// sub-schema properties, neither of which is a write this action is for.
|
|
220
|
-
// Slot-name safety is a write guard (ADR-052); `setSlot`'s tests in
|
|
221
|
-
// `configurationSchema.test.ts` pin both halves. `slotKeys` already has
|
|
222
|
-
// base-schema slots merged in, so an inherited slot passes.
|
|
223
|
-
// `null` resets, the same thing `undefined` does, because a JSON-borne
|
|
224
|
-
// caller cannot spell `undefined`: a session spec, share link or agent
|
|
225
|
-
// call can set a slot and then has no way to put it back. Omitting the
|
|
226
|
-
// key is not that — this action is the merge path, where an absent key
|
|
227
|
-
// means "leave it alone". A sub-schema reads `null` the same way, and
|
|
228
|
-
// no slot stores `null` as a value.
|
|
171
|
+
// The name check is a write guard (ADR-052), never a warning. `null`
|
|
172
|
+
// resets, as `undefined` does, since JSON cannot spell `undefined`
|
|
173
|
+
// (ADR-146).
|
|
229
174
|
setSlot(slotName, rawValue) {
|
|
230
175
|
const value = rawValue ?? undefined;
|
|
231
176
|
if (!slotKeys.has(slotName)) {
|
|
232
|
-
// the sub-schema branch re-classifies off the definition rather than
|
|
233
|
-
// carrying a second Set: it runs only on the way to a throw, and the
|
|
234
|
-
// predicate is the same one the loop above classified with
|
|
235
177
|
throw new Error(isConfigurationSchemaType(schemaDefinition[slotName])
|
|
236
178
|
? `${slotName} is a sub-schema on ${modelName}, not a config slot — replace it with setSubschema`
|
|
237
179
|
: `${modelName} has no config slot "${slotName}". Valid slots: ${[
|
|
@@ -240,10 +182,7 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
240
182
|
.sort()
|
|
241
183
|
.join(', ')}`);
|
|
242
184
|
}
|
|
243
|
-
// MST skips its own type check in a production build
|
|
244
|
-
// slot's union cannot take is then dropped with no throw: the write
|
|
245
|
-
// reported success and the slot kept its old value. `is` still runs
|
|
246
|
-
// there, so the guard holds in both builds.
|
|
185
|
+
// MST skips its own type check in a production build; `is` does not
|
|
247
186
|
if (!modelDefinition[slotName].is(value)) {
|
|
248
187
|
const declared = schemaDefinition[slotName];
|
|
249
188
|
const slotType = isSlotDefinitionEntry(declared)
|
|
@@ -263,8 +202,6 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
263
202
|
if (Object.keys(volatileConstants).length) {
|
|
264
203
|
completeModel = completeModel.volatile(( /* self */) => volatileConstants);
|
|
265
204
|
}
|
|
266
|
-
// one MST call per entry, base's before the subclass's — chaining is what
|
|
267
|
-
// makes the base's members visible on `self` inside the subclass's function
|
|
268
205
|
for (const hook of hookList(options.actions)) {
|
|
269
206
|
completeModel = completeModel.actions(hook);
|
|
270
207
|
}
|
|
@@ -287,18 +224,15 @@ function makeConfigurationSchemaModel(modelName, schemaDefinition, options) {
|
|
|
287
224
|
const modelDefault = options.explicitlyTyped
|
|
288
225
|
? { type: modelName, ...identifierDefault }
|
|
289
226
|
: identifierDefault;
|
|
290
|
-
// stripDefault
|
|
291
|
-
//
|
|
292
|
-
// collapses to its default, and the parent's stripDefault drops the key.
|
|
227
|
+
// stripDefault, not optional, so an all-default sub-schema leaves its
|
|
228
|
+
// parent's snapshot
|
|
293
229
|
registerConfigurationSchema(completeModel, metadata);
|
|
294
230
|
return types.stripDefault(completeModel, modelDefault);
|
|
295
231
|
}
|
|
296
|
-
// Assigning an array makes MST reconcile it,
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
// `push` overflows the stack past about 120,000 names. An unchanged array is
|
|
301
|
-
// left alone, so it fires no observer.
|
|
232
|
+
// Assigning an array makes MST reconcile it entry by entry, seconds for a
|
|
233
|
+
// reorder of 5,000 row names; refilling a scalar array takes 50 ms.
|
|
234
|
+
// spliceWithArray, since spreading into `push` overflows the stack past about
|
|
235
|
+
// 120,000 entries.
|
|
302
236
|
function refillArray(held, value) {
|
|
303
237
|
if (held.length !== value.length || held.some((v, i) => v !== value[i])) {
|
|
304
238
|
held.clear();
|
|
@@ -309,39 +243,22 @@ export function ConfigurationSchema(modelName, inputSchemaDefinition, inputOptio
|
|
|
309
243
|
const { schemaDefinition, options } = preprocessConfigurationSchemaArguments(modelName, inputSchemaDefinition, inputOptions);
|
|
310
244
|
return makeConfigurationSchemaModel(modelName, schemaDefinition, options);
|
|
311
245
|
}
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
// drift on which env they build with. The memo it goes through belongs to the
|
|
315
|
-
// PluginManager instance (ADR-031): a node built with one instance's env must
|
|
316
|
-
// never be handed to another, and `pluginManager.hydratedTrackConfig` scopes
|
|
317
|
-
// that structurally rather than by convention.
|
|
246
|
+
// memoized per PluginManager, so a node never crosses to another instance's env
|
|
247
|
+
// (ADR-031)
|
|
318
248
|
function hydrateInto(pluginManager, schemaType, frozen) {
|
|
319
249
|
return pluginManager.hydratedTrackConfig(schemaType, frozen, () => schemaType.create(frozen, { pluginManager }));
|
|
320
250
|
}
|
|
321
251
|
/**
|
|
322
252
|
* #api core/configuration
|
|
323
|
-
* Hydrate a plain track config
|
|
324
|
-
* `type` to find the schema.
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
* `preProcessSnapshot` has not run, and nothing that walks a live node applies
|
|
328
|
-
* to it.
|
|
329
|
-
*
|
|
330
|
-
* Use it where a caller needs the resolved config and may be handed either
|
|
331
|
-
* form. The About dialog's "Copy config" is reached from two menus, and one of
|
|
332
|
-
* them passes a `session.tracks` entry.
|
|
253
|
+
* Hydrate a plain track config, such as a `session.tracks` entry, into a live
|
|
254
|
+
* config node, dispatching on its `type` to find the schema. A plain entry
|
|
255
|
+
* holds only what was authored: a slot at its default is absent and
|
|
256
|
+
* `preProcessSnapshot` has not run.
|
|
333
257
|
*
|
|
334
|
-
* Returns **undefined** when
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* Shares `TrackConfigurationReference`'s per-PluginManager cache, so hydrating
|
|
340
|
-
* the same entry twice returns the same node, and in an admin session a track
|
|
341
|
-
* opened later resolves to that same node. A non-admin's open track
|
|
342
|
-
* resolves to the session's private working copy (ADR-032), and this function
|
|
343
|
-
* returns the pristine mirror beside it. The two have the same content;
|
|
344
|
-
* `CopyConfigEntryPoints.test.ts` tests both cases.
|
|
258
|
+
* Returns **undefined** when no plugin registered the type or `create` rejects
|
|
259
|
+
* the config, which has never been validated; callers fall back to the plain
|
|
260
|
+
* object. Hydrating one entry twice returns one node. A shown track resolves to
|
|
261
|
+
* the session's working copy instead (ADR-032), which has the same content.
|
|
345
262
|
*/
|
|
346
263
|
export function hydrateTrackConfig(pluginManager, config) {
|
|
347
264
|
const type = config.type;
|
|
@@ -349,10 +266,6 @@ export function hydrateTrackConfig(pluginManager, config) {
|
|
|
349
266
|
return undefined;
|
|
350
267
|
}
|
|
351
268
|
try {
|
|
352
|
-
// getTrackType throws on an unregistered name rather than returning
|
|
353
|
-
// undefined, so it is inside the guard with `create` — the two failures are
|
|
354
|
-
// the same failure (nothing here can build this config) and neither should
|
|
355
|
-
// reach a dialog that is only trying to show it
|
|
356
269
|
const { configSchema } = pluginManager.getTrackType(type);
|
|
357
270
|
return hydrateInto(pluginManager, configSchema, config);
|
|
358
271
|
}
|
|
@@ -361,52 +274,24 @@ export function hydrateTrackConfig(pluginManager, config) {
|
|
|
361
274
|
return undefined;
|
|
362
275
|
}
|
|
363
276
|
}
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
// side — the explicit snapshot dispatcher (string → ref, object → schema)
|
|
368
|
-
// disambiguates. Shared by TrackConfigurationReference/DisplayConfigurationReference,
|
|
369
|
-
// and by neither accident nor oversight NOT by the plain branch — see the note
|
|
370
|
-
// on it in `ConfigurationReference`.
|
|
371
|
-
//
|
|
372
|
-
// One consequence, because both refs' `set` callbacks read as if it were not
|
|
373
|
-
// true: assigning a config **node** here does not store a reference to it. The
|
|
374
|
-
// dispatcher sees a non-string and picks `schemaType`, so MST tries to adopt the
|
|
375
|
-
// node as an inline child — which throws if it already has a parent. `set` is
|
|
376
|
-
// therefore unreachable on both refs (checked by making it throw: 231 suites
|
|
377
|
-
// stayed green), and exists only because MST rejects a custom reference
|
|
378
|
-
// declaring `get` without it. The plain branch, with no dispatcher, does the
|
|
379
|
-
// opposite and stores the reference.
|
|
277
|
+
// An id string resolves through `ref`; an object is an inline config. So
|
|
278
|
+
// assigning a node here adopts it as a child rather than storing a reference,
|
|
279
|
+
// which makes both refs' `set` unreachable: MST only requires one beside `get`.
|
|
380
280
|
function idOrSnapshotUnion(ref, schemaType) {
|
|
381
281
|
return types.union({
|
|
382
282
|
dispatcher: snapshot => (typeof snapshot === 'string' ? ref : schemaType),
|
|
383
283
|
}, ref, schemaType);
|
|
384
284
|
}
|
|
385
285
|
/**
|
|
386
|
-
* Reference to a track configuration
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
* string id OR a full config.** A view that synthesizes a track nobody else can
|
|
390
|
-
* draw — a read-vs-ref synteny band, an SV inspector row, a circular view's
|
|
391
|
-
* added track — writes the config here rather than registering it in
|
|
392
|
-
* `session.tracks`, and it then lives and dies with the track that holds it.
|
|
393
|
-
* `ReadVsRef.test.tsx` and `SVInspector.test.tsx` are the canaries.
|
|
394
|
-
*
|
|
395
|
-
* NOTE: don't add `as SCHEMATYPE` to the return value. It narrows SnapshotIn
|
|
396
|
-
* to just the object branch, forcing callers to wrap string ids in
|
|
397
|
-
* `@ts-expect-error`. The inferred union SnapshotIn is `string | SnapshotIn<schema>`.
|
|
286
|
+
* Reference to a track configuration, snapshotted as its trackId. It also takes
|
|
287
|
+
* a whole config: a view's synthesized track (a read-vs-ref band, an SV
|
|
288
|
+
* inspector row) holds its config inline and lives and dies with it. ADR-084.
|
|
398
289
|
*/
|
|
399
290
|
function TrackConfigurationReference(schemaType) {
|
|
400
291
|
const trackRef = types.reference(schemaType, {
|
|
401
292
|
get(id, parent) {
|
|
402
293
|
const session = getSession(parent);
|
|
403
294
|
const trackId = String(id);
|
|
404
|
-
// Per-id lookup: subscribes only to this trackId's derivation, so
|
|
405
|
-
// resolving one track's config doesn't re-render the others. A session
|
|
406
|
-
// with track deltas hands back a private, per-track working copy so a
|
|
407
|
-
// shown track's in-place quick-edits mutate that copy and never the
|
|
408
|
-
// shared frozen base (ADR-032); any other session falls through to the
|
|
409
|
-
// frozen hydration cache (ADR-031).
|
|
410
295
|
const ret = isSessionWithEditableTrackConfig(session)
|
|
411
296
|
? session.getEditableTrackConfig(trackId, schemaType)
|
|
412
297
|
: session.getTrackById(trackId);
|
|
@@ -424,52 +309,19 @@ function TrackConfigurationReference(schemaType) {
|
|
|
424
309
|
return idOrSnapshotUnion(trackRef, schemaType);
|
|
425
310
|
}
|
|
426
311
|
/**
|
|
427
|
-
* Reference to a display configuration
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* 2. by `parent.type` — handles old sessions where the saved displayId
|
|
433
|
-
* no longer matches but a display of the same type exists on the track
|
|
434
|
-
*
|
|
435
|
-
* Step 2 is the safety net because `baseTrackConfig.preProcessSnapshot`
|
|
436
|
-
* already injects a stub display for every registered displayType on the
|
|
437
|
-
* track, so a same-type lookup always succeeds at runtime for properly
|
|
438
|
-
* loaded tracks. It is what carries a **renamed display type** across for a
|
|
439
|
-
* catalog track: the injected stub is named for the new type, the display
|
|
440
|
-
* model's own `preProcessSnapshot` rewrites the state model's, and the saved
|
|
441
|
-
* `configuration` id still spells the old name, so only the type match
|
|
442
|
-
* reconnects them. Not every pre-rename session reaches it — a track config
|
|
443
|
-
* that itself declares the old display entry keeps that entry's `displayId`
|
|
444
|
-
* through the alias rewrite (`{ ...d, type: canonical }`) and wins the
|
|
445
|
-
* first-wins dedupe, so its session resolves by id. An older third step
|
|
446
|
-
* auto-created a *detached* config when neither matched — that produced an
|
|
447
|
-
* orphaned MST node whose edits silently didn't persist. Removed in favor of a
|
|
448
|
-
* clear throw.
|
|
449
|
-
*
|
|
450
|
-
* The union's schemaType branch is symmetry with `TrackConfigurationReference`
|
|
451
|
-
* rather than a path anything in tree takes: the two production writers,
|
|
452
|
-
* `showTrackGeneric` and `BaseTrackModel.replaceDisplay`, both write a
|
|
453
|
-
* displayId string, because a display config belongs in its track config's
|
|
454
|
-
* `displays` array. It stays because it costs nothing and because dropping it
|
|
455
|
-
* narrows `SnapshotIn` for every caller.
|
|
312
|
+
* Reference to a display configuration in the containing track config's
|
|
313
|
+
* `displays`, snapshotted as its displayId. An id that matches nothing falls
|
|
314
|
+
* back to the display of the parent's type, which the track config always
|
|
315
|
+
* holds a stub of: that is what reconnects a session saved before a display
|
|
316
|
+
* type was renamed.
|
|
456
317
|
*/
|
|
457
318
|
function DisplayConfigurationReference(schemaType) {
|
|
458
319
|
const displayRef = types.reference(schemaType, {
|
|
459
320
|
get(id, parent) {
|
|
460
|
-
// track.configuration is a hydrated MST node (hydrated lazily via
|
|
461
|
-
// TrackConfigurationReference), so its displays array contains MST nodes.
|
|
462
321
|
const track = getContainingTrack(parent);
|
|
463
322
|
const displays = track.configuration.displays;
|
|
464
323
|
const displayType = parent.type;
|
|
465
324
|
let ret = displays.find(d => d.displayId === id);
|
|
466
|
-
// Fallback: match by display type when the displayId isn't found.
|
|
467
|
-
// baseTrackConfig.preProcessSnapshot injects a display entry for every
|
|
468
|
-
// registered displayType for the track, so id-mismatch (e.g. an old
|
|
469
|
-
// session with a different displayId convention) finds a same-type
|
|
470
|
-
// entry here. The `if (displayType)` guard prevents an undefined
|
|
471
|
-
// parent.type from silently matching a display whose `.type` is also
|
|
472
|
-
// undefined.
|
|
473
325
|
if (!ret && displayType) {
|
|
474
326
|
ret = displays.find(d => d.type === displayType);
|
|
475
327
|
}
|
|
@@ -486,21 +338,10 @@ function DisplayConfigurationReference(schemaType) {
|
|
|
486
338
|
return idOrSnapshotUnion(displayRef, schemaType);
|
|
487
339
|
}
|
|
488
340
|
/**
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
* Display schemas must declare `explicitIdentifier: 'displayId'` (directly or
|
|
494
|
-
* via `baseConfiguration: baseLinearDisplayConfigSchema`, which merges its
|
|
495
|
-
* options through `preprocessConfigurationSchemaArguments`).
|
|
496
|
-
*
|
|
497
|
-
* The return is annotated `IConfigurationReference<SCHEMATYPE>` (see its doc)
|
|
498
|
-
* so `self.configuration` carries the concrete schema. The three runtime
|
|
499
|
-
* branches produce MST reference/union types over `schemaType` that are
|
|
500
|
-
* assignable to that annotation, so `return ref` needs no cast — and must not
|
|
501
|
-
* get one: a prior `as SCHEMATYPE` was dropped because it narrowed `SnapshotIn`
|
|
502
|
-
* to just the object branch and forced callers pushing string ids to
|
|
503
|
-
* `@ts-expect-error` (see `TrackConfigurationReference`'s note).
|
|
341
|
+
* A reference resolved by the schema's identifier: a `trackId` through the
|
|
342
|
+
* session, a `displayId` through the containing track's `displays`, anything
|
|
343
|
+
* else by MST's own identifier lookup. No `as SCHEMATYPE` on the return: it
|
|
344
|
+
* would narrow `SnapshotIn` to the object branch and refuse a string id.
|
|
504
345
|
*/
|
|
505
346
|
export function ConfigurationReference(schemaType) {
|
|
506
347
|
const id = getConfigurationSchemaMetadata(schemaType)?.options.explicitIdentifier;
|
|
@@ -508,17 +349,8 @@ export function ConfigurationReference(schemaType) {
|
|
|
508
349
|
? TrackConfigurationReference(schemaType)
|
|
509
350
|
: id === 'displayId'
|
|
510
351
|
? DisplayConfigurationReference(schemaType)
|
|
511
|
-
: //
|
|
512
|
-
//
|
|
513
|
-
// session. **Deliberately not `idOrSnapshotUnion`**, even though it is
|
|
514
|
-
// the same two members: this branch is also handed a *live, in-tree*
|
|
515
|
-
// config node (`initializeInternetAccount` pushes
|
|
516
|
-
// `jbrowse.internetAccounts[i]` straight through), and an undispatched
|
|
517
|
-
// union sends that to the reference member because
|
|
518
|
-
// `BaseReferenceType.isAssignableFrom` defers to its target type. A
|
|
519
|
-
// `typeof snapshot === 'string'` dispatcher sends it to the schema
|
|
520
|
-
// member instead, and MST refuses to adopt a node that already has a
|
|
521
|
-
// parent. `InternetAccounts.test.ts` is the canary.
|
|
352
|
+
: // not idOrSnapshotUnion: this branch is handed live in-tree nodes
|
|
353
|
+
// (an internet account), which its dispatcher would try to adopt
|
|
522
354
|
types.union(types.reference(schemaType), schemaType);
|
|
523
355
|
return ref;
|
|
524
356
|
}
|
|
@@ -261,29 +261,15 @@ declare const slotTypes: {
|
|
|
261
261
|
fallbackDefault: {};
|
|
262
262
|
};
|
|
263
263
|
};
|
|
264
|
-
/**
|
|
265
|
-
* The builtin table's own names — every slot type whose MST model this module
|
|
266
|
-
* supplies, so excluding the two enum types. Exported for `SlotValueByType` in
|
|
267
|
-
* `types.ts`, which has to name the same set and is checked against this.
|
|
268
|
-
*/
|
|
264
|
+
/** the types whose model this module supplies; `SlotValueByType` names the same set */
|
|
269
265
|
export type BuiltinSlotTypeName = keyof typeof slotTypes;
|
|
270
266
|
declare const ENUM_SLOT_TYPES: readonly ['stringEnum', 'maybeStringEnum', 'stringEnumArray'];
|
|
271
267
|
/**
|
|
272
|
-
* Every legal `type` on a slot definition
|
|
273
|
-
* the
|
|
274
|
-
*
|
|
275
|
-
* Closed on purpose. A name outside this set still *works* at runtime as long
|
|
276
|
-
* as a `model` is given — which is exactly the trap: the value round-trips
|
|
277
|
-
* fine, and the only symptom is that everything keyed off the type name stops
|
|
278
|
-
* recognising the slot (`slotFacade` hands the editor no `choices`, so an enum
|
|
279
|
-
* renders as a free text box).
|
|
268
|
+
* Every legal `type` on a slot definition. Closed, since a name outside it
|
|
269
|
+
* would still load with a `model` given, and every surface keyed on the name
|
|
270
|
+
* would stop recognising the slot.
|
|
280
271
|
*/
|
|
281
272
|
export type ConfigSlotType = keyof typeof slotTypes | (typeof ENUM_SLOT_TYPES)[number];
|
|
282
|
-
/**
|
|
283
|
-
* The same set as `MAYBE_TYPES`, at the type level, and derived from the same
|
|
284
|
-
* property of the same table so the two cannot disagree: an entry with no
|
|
285
|
-
* `fallbackDefault` is one whose fixed form is "unset".
|
|
286
|
-
*/
|
|
287
273
|
type MaybeBuiltinSlotTypeName = {
|
|
288
274
|
[K in keyof typeof slotTypes]: 'fallbackDefault' extends keyof (typeof slotTypes)[K] ? never : K;
|
|
289
275
|
}[keyof typeof slotTypes];
|
|
@@ -301,31 +287,19 @@ interface ConfigSlotDefinitionCommon {
|
|
|
301
287
|
*/
|
|
302
288
|
contextVariable?: string[];
|
|
303
289
|
/**
|
|
304
|
-
*
|
|
305
|
-
*
|
|
290
|
+
* put this slot behind a toggle so common slots aren't crowded out by
|
|
291
|
+
* rarely-changed ones: "Show advanced settings" in the config editor, and
|
|
292
|
+
* the collapsed Advanced card in a track's About dialog, which also takes
|
|
293
|
+
* every slot with a `contextVariable`
|
|
306
294
|
*/
|
|
307
295
|
advanced?: boolean;
|
|
308
296
|
}
|
|
309
297
|
/**
|
|
310
|
-
* A slot definition
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* across the repo carry a `defaultValue: undefined` line that said nothing.
|
|
316
|
-
*
|
|
317
|
-
* A `maybe*` slot's only legal `defaultValue` is the sentinel itself, but the
|
|
318
|
-
* field stays `unknown` rather than being pinned to `undefined`: the spread in
|
|
319
|
-
* `mergeSchemaDefinition` combines a base and a child whose halves may disagree,
|
|
320
|
-
* so pinning it forces a cast through the one place slot definitions are
|
|
321
|
-
* combined. `ConfigSlot` throws on a concrete one instead — and has to anyway,
|
|
322
|
-
* since no type can see what a `baseConfiguration` merged in, which is the way
|
|
323
|
-
* this mistake actually happens.
|
|
324
|
-
*
|
|
325
|
-
* Stating the sentinel stays meaningful in a subclass override, where the merge
|
|
326
|
-
* is a spread and an omitted key inherits the base's value. The same spread is
|
|
327
|
-
* why an enum slot's `model` stays optional here though `ConfigSlot` requires
|
|
328
|
-
* one: a subclass restating only the default inherits the vocabulary.
|
|
298
|
+
* A slot definition: a `maybe*` type's `defaultValue` is optional, every
|
|
299
|
+
* other's required. A `maybe*` default stays `unknown` rather than `undefined`
|
|
300
|
+
* because a subclass override is spread over its base's, which no type can see;
|
|
301
|
+
* `ConfigSlot` throws on a concrete one. An enum's `model` is optional for the
|
|
302
|
+
* same spread: an override restating only the default inherits the vocabulary.
|
|
329
303
|
*/
|
|
330
304
|
export type ConfigSlotDefinition = (ConfigSlotDefinitionCommon & {
|
|
331
305
|
/** a `maybe*` slot type, whose unset state is `undefined` */
|
|
@@ -339,33 +313,13 @@ export type ConfigSlotDefinition = (ConfigSlotDefinitionCommon & {
|
|
|
339
313
|
defaultValue: unknown;
|
|
340
314
|
});
|
|
341
315
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
346
|
-
* holds a `jexl:` expression as its value, which the display evaluates per
|
|
347
|
-
* feature. The value lives directly on the parent configuration model — there
|
|
348
|
-
* is no per-slot sub-model. `types.stripDefault` omits the property from the
|
|
349
|
-
* parent snapshot when it equals the default, so saved sessions stay minimal.
|
|
350
|
-
* Per-slot metadata (type/description/defaultValue/contextVariable) lives in
|
|
351
|
-
* the schema registry (a WeakMap keyed by the MST type, see
|
|
352
|
-
* schemaRegistry.ts); jexl callbacks are evaluated on read by
|
|
353
|
-
* `readConfObject`.
|
|
354
|
-
*
|
|
355
|
-
* Interning the returned type on `(type, model, defaultValue)` was prototyped
|
|
356
|
-
* and declined: a session builds 1197 slot types for 222 distinct ones, and
|
|
357
|
-
* removing the repeats moves neither the time nor the heap
|
|
358
|
-
* (`config-schema-construction`, published in `reference/EAGER_BUNDLE.md`).
|
|
316
|
+
* The MST property a slot is: its value type, or that or a `jexl:` callback
|
|
317
|
+
* where the slot declares a `contextVariable` (ADR-155), stripped from the
|
|
318
|
+
* snapshot at its default. Interning these was measured and declined
|
|
319
|
+
* (`config-schema-construction` in `reference/EAGER_BUNDLE.md`).
|
|
359
320
|
*/
|
|
360
321
|
export default function ConfigSlot(definition: ConfigSlotDefinition): import("@jbrowse/mobx-state-tree").IOptionalIType<IAnyType, [undefined]>;
|
|
361
|
-
/**
|
|
362
|
-
* New value when converting a fixed-value slot to a jexl callback. Already-
|
|
363
|
-
* callback values are returned unchanged.
|
|
364
|
-
*
|
|
365
|
-
* A single JSON.stringify covers every type: `jexl:${42}` and
|
|
366
|
-
* `jexl:${JSON.stringify(42)}` are identical for numbers/booleans, and the rest
|
|
367
|
-
* need the quoting/serialization anyway.
|
|
368
|
-
*/
|
|
322
|
+
/** A fixed value as the `jexl:` callback that returns it. */
|
|
369
323
|
export declare function toCallbackValue(value: unknown): string;
|
|
370
324
|
/**
|
|
371
325
|
* New value when converting a jexl callback back to a fixed value: try
|