@jbrowse/core 5.0.0-beta.1 → 5.0.0-beta.3
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 +112 -43
- package/esm/BaseFeatureWidget/BaseFeatureDetail/FeatureDetails.d.ts +2 -3
- package/esm/BaseFeatureWidget/BaseFeatureDetail/ParentFeatureLine.d.ts +4 -0
- package/esm/BaseFeatureWidget/BaseFeatureDetail/ParentFeatureLine.js +17 -0
- package/esm/BaseFeatureWidget/BaseFeatureDetail/index.js +4 -3
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.d.ts +2 -2
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +2 -2
- package/esm/BaseFeatureWidget/index.d.ts +1 -1
- package/esm/BaseFeatureWidget/stateModelFactory.d.ts +8 -1
- package/esm/BaseFeatureWidget/stateModelFactory.js +6 -0
- package/esm/BaseFeatureWidget/types.d.ts +10 -2
- package/esm/PluginLoader.d.ts +1 -0
- package/esm/PluginLoader.js +52 -8
- package/esm/PluginManager.d.ts +99 -17
- package/esm/PluginManager.js +194 -26
- package/esm/ReExports/BaseFeatureDetails.js +1 -1
- package/esm/ReExports/MuiDataGridReExports.d.ts +1 -0
- package/esm/ReExports/MuiDataGridReExports.js +2 -2
- package/esm/ReExports/barrelOnlyNames.d.ts +5 -0
- package/esm/ReExports/barrelOnlyNames.js +63 -0
- package/esm/ReExports/modules.js +31 -109
- package/esm/ReExports/publicTracks.d.ts +1 -0
- package/esm/ReExports/publicTracks.js +14 -0
- package/esm/ReExports/publicUi.d.ts +4 -0
- package/esm/ReExports/publicUi.js +33 -0
- package/esm/ReExports/publicUtil.d.ts +1 -1
- package/esm/ReExports/publicUtil.js +8 -4
- package/esm/ReExports/sharedModules.d.ts +96 -0
- package/esm/ReExports/sharedModules.js +66 -0
- package/esm/ReExports/uiStub.d.ts +2 -0
- package/esm/ReExports/uiStub.js +11 -0
- package/esm/ReExports/workerModules.d.ts +2 -0
- package/esm/ReExports/workerModules.js +15 -0
- package/esm/ReExports/workerNamespaceNames.d.ts +11 -0
- package/esm/ReExports/workerNamespaceNames.js +281 -0
- package/esm/configuration/configurationSchema.d.ts +5 -38
- package/esm/configuration/configurationSchema.js +64 -40
- package/esm/configuration/index.d.ts +2 -2
- package/esm/configuration/index.js +1 -1
- package/esm/configuration/promotableDefaults.d.ts +54 -19
- package/esm/configuration/promotableDefaults.js +117 -48
- package/esm/configuration/promotablePin.d.ts +50 -40
- package/esm/configuration/promotableResolve.d.ts +1 -1
- package/esm/configuration/promotableResolve.js +1 -1
- package/esm/configuration/tabixIndexFields.d.ts +2 -2
- package/esm/configuration/tabixIndexFields.js +2 -2
- package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +12 -0
- package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +35 -4
- package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +41 -0
- package/esm/data_adapters/BaseAdapter/featureDensity.js +32 -0
- package/esm/data_adapters/BaseAdapter/index.d.ts +2 -1
- package/esm/data_adapters/BaseAdapter/index.js +1 -0
- package/esm/data_adapters/BaseAdapter/stats.d.ts +33 -1
- package/esm/data_adapters/BaseAdapter/stats.js +52 -13
- package/esm/data_adapters/BaseAdapter/types.d.ts +3 -8
- package/esm/pluggableElementTypes/AdapterType.d.ts +2 -1
- package/esm/pluggableElementTypes/AdapterType.js +1 -1
- package/esm/pluggableElementTypes/DisplayType.d.ts +4 -12
- package/esm/pluggableElementTypes/DisplayType.js +3 -10
- package/esm/pluggableElementTypes/LazyStateModelElement.d.ts +27 -0
- package/esm/pluggableElementTypes/LazyStateModelElement.js +71 -0
- package/esm/pluggableElementTypes/ViewType.d.ts +20 -4
- package/esm/pluggableElementTypes/ViewType.js +32 -4
- package/esm/pluggableElementTypes/extendElementType.d.ts +1 -2
- package/esm/pluggableElementTypes/extendElementType.js +9 -5
- package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +44 -3
- package/esm/pluggableElementTypes/models/BaseDisplayModel.js +75 -2
- package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +21 -0
- package/esm/pluggableElementTypes/models/BaseTrackModel.js +34 -2
- package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +49 -0
- package/esm/pluggableElementTypes/models/BaseViewModel.js +28 -0
- package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +14 -10
- package/esm/pluggableElementTypes/models/assertDisplayContract.js +25 -32
- package/esm/pluggableElementTypes/models/baseTrackConfig.js +7 -1
- package/esm/rpc/RpcRegistry.d.ts +10 -0
- package/esm/rpc/byteBudget.d.ts +9 -0
- package/esm/rpc/coreRpcMethods.d.ts +1 -0
- package/esm/rpc/coreRpcMethods.js +1 -0
- package/esm/rpc/methods/CoreGetFeatureDensity.d.ts +11 -0
- package/esm/rpc/methods/CoreGetFeatureDensity.js +22 -0
- package/esm/svg/svgReady.d.ts +1 -0
- package/esm/ui/BaseExportSvgDialog.d.ts +1 -1
- package/esm/ui/CascadingMenu.js +30 -8
- package/esm/ui/ErrorMessageStackTraceDialog.js +5 -6
- package/esm/ui/ExportSvgDialog.d.ts +1 -1
- package/esm/ui/LoadingOverlay.d.ts +14 -0
- package/esm/ui/LoadingOverlay.js +1 -1
- package/esm/ui/MenuTypes.d.ts +14 -5
- package/esm/ui/MenuTypes.js +3 -0
- package/esm/ui/PaletteContext.d.ts +11 -4
- package/esm/ui/PaletteContext.js +34 -5
- package/esm/ui/PinAdornment.js +31 -43
- package/esm/ui/ScrollChrome.d.ts +16 -0
- package/esm/ui/ScrollChrome.js +12 -0
- package/esm/ui/ScrollZoomToggle.d.ts +4 -3
- package/esm/ui/ScrollZoomToggle.js +4 -3
- package/esm/ui/SizeSliderRow.js +1 -1
- package/esm/ui/index.d.ts +3 -3
- package/esm/ui/index.js +4 -4
- package/esm/ui/launchViewMenu.js +7 -1
- package/esm/ui/legendMenuItem.d.ts +10 -16
- package/esm/ui/legendMenuItem.js +9 -20
- package/esm/ui/makeSizeMenu.js +1 -1
- package/esm/ui/menuItemAdornment.d.ts +2 -3
- package/esm/ui/menuItemAdornment.js +2 -3
- package/esm/ui/menuItems.d.ts +2 -3
- package/esm/ui/menuItems.js +1 -2
- package/esm/ui/palette.d.ts +11 -11
- package/esm/ui/palette.js +11 -11
- package/esm/ui/promotablePinCoverage.d.ts +16 -2
- package/esm/ui/promotablePinCoverage.js +40 -3
- package/esm/ui/theme.js +5 -0
- package/esm/ui/toggleMenuItems.d.ts +14 -9
- package/esm/ui/toggleMenuItems.js +32 -15
- package/esm/util/Base1DUtils.d.ts +4 -2
- package/esm/util/Base1DUtils.js +12 -4
- package/esm/util/SvgCanvas.js +4 -3
- package/esm/util/adapterConfigKey.d.ts +12 -0
- package/esm/util/adapterConfigKey.js +14 -0
- package/esm/util/adapterMetadata.js +2 -1
- package/esm/util/addAndShowTrack.d.ts +1 -1
- package/esm/util/addAndShowTrack.js +5 -1
- package/esm/util/addTrackFromWidget.js +2 -2
- package/esm/util/analytics.js +4 -5
- package/esm/util/cacheBudgets.d.ts +22 -19
- package/esm/util/cacheBudgets.js +22 -19
- package/esm/util/clampToContig.d.ts +17 -0
- package/esm/util/clampToContig.js +19 -0
- package/esm/util/colorBits.d.ts +2 -1
- package/esm/util/colorBits.js +2 -5
- package/esm/util/createStopTokenRotation.d.ts +9 -7
- package/esm/util/createStopTokenRotation.js +7 -27
- package/esm/util/featureTypes.d.ts +24 -0
- package/esm/util/featureTypes.js +35 -0
- package/esm/util/fetchContext.d.ts +2 -2
- package/esm/util/fetchContext.js +2 -2
- package/esm/util/fetchPhases.d.ts +10 -10
- package/esm/util/hooks.d.ts +8 -0
- package/esm/util/hooks.js +27 -0
- package/esm/util/index.d.ts +6 -5
- package/esm/util/index.js +5 -4
- package/esm/util/installFetch.d.ts +71 -34
- package/esm/util/installFetch.js +57 -29
- package/esm/util/installInitAutorun.d.ts +14 -13
- package/esm/util/installInitAutorun.js +17 -17
- package/esm/util/installPrerequisiteFetch.d.ts +86 -0
- package/esm/util/installPrerequisiteFetch.js +73 -0
- package/esm/util/lazyAfterAttach.d.ts +13 -0
- package/esm/util/lazyAfterAttach.js +31 -0
- package/esm/util/legendCandidates.d.ts +50 -0
- package/esm/util/legendCandidates.js +87 -0
- package/esm/util/mstUtils.d.ts +12 -1
- package/esm/util/mstUtils.js +20 -2
- package/esm/util/openFeatureWidget.d.ts +5 -0
- package/esm/util/openFeatureWidget.js +1 -0
- package/esm/util/openViews.d.ts +16 -0
- package/esm/util/openViews.js +22 -0
- package/esm/util/parentWalk.d.ts +1 -1
- package/esm/util/parentWalk.js +1 -1
- package/esm/util/pluginStore.d.ts +1 -0
- package/esm/util/pluginStore.js +8 -1
- package/esm/util/range.d.ts +13 -9
- package/esm/util/range.js +17 -8
- package/esm/util/renderToStaticMarkup.js +10 -0
- package/esm/util/rxjs.d.ts +16 -0
- package/esm/util/rxjs.js +49 -0
- package/esm/util/scheduleDetachedDestroy.d.ts +18 -0
- package/esm/util/scheduleDetachedDestroy.js +24 -0
- package/esm/util/scrollLatch.d.ts +1 -0
- package/esm/util/scrollLatch.js +8 -0
- package/esm/util/stopToken.d.ts +6 -5
- package/esm/util/tabix.d.ts +10 -4
- package/esm/util/tabix.js +11 -5
- package/esm/util/trackConfigDelta.d.ts +6 -6
- package/esm/util/trackConfigDelta.js +3 -3
- package/esm/util/tracks.d.ts +48 -0
- package/esm/util/tracks.js +192 -66
- package/esm/util/types/index.d.ts +58 -10
- package/esm/util/types/index.js +3 -0
- package/esm/util/unknownSnapshotKeys.d.ts +36 -0
- package/esm/util/unknownSnapshotKeys.js +69 -0
- package/esm/util/usePanelVirtualScroll.d.ts +6 -2
- package/esm/util/usePanelVirtualScroll.js +6 -2
- package/esm/util/useRowVirtualScroll.d.ts +1 -1
- package/esm/util/useRowVirtualScroll.js +1 -1
- package/esm/util/useVirtualScrollWheel.js +11 -1
- package/esm/util/viewStatus.d.ts +23 -0
- package/esm/util/viewStatus.js +17 -0
- package/esm/util/wheelZoom.d.ts +3 -1
- package/esm/util/wheelZoom.js +39 -14
- package/esm/util/whenViewSettled.d.ts +17 -4
- package/esm/util/whenViewSettled.js +19 -6
- package/esm/util/withLaunchInput.d.ts +168 -0
- package/esm/util/withLaunchInput.js +273 -0
- package/package.json +19 -11
- package/esm/ui/promotableMenuItems.d.ts +0 -19
- package/esm/ui/promotableMenuItems.js +0 -89
- package/esm/util/applyInitSettings.d.ts +0 -79
- package/esm/util/applyInitSettings.js +0 -91
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { reportLegacyInit, reportMalformedRows, reportUnknownKeys, } from './unknownSnapshotKeys.js';
|
|
2
|
+
// re-exported here because the surfaces that classify their own keys — a
|
|
3
|
+
// session spec, which launches without ever building a snapshot — reach the
|
|
4
|
+
// partition's vocabulary through this module
|
|
5
|
+
export { legacyInitMessage, unknownKeysMessage } from './unknownSnapshotKeys.js';
|
|
6
|
+
// What afterAttach reports rather than applies. They live in the blob so the
|
|
7
|
+
// partition has one place to put everything, and `pendingLaunch` is what keeps
|
|
8
|
+
// them from reading as work to do.
|
|
9
|
+
const REPORTED = new Set(['unknown', 'malformed', 'legacyInit']);
|
|
10
|
+
/**
|
|
11
|
+
* The blob when it still holds something to apply, else undefined. A view's
|
|
12
|
+
* gates ask this rather than the raw property: a snapshot whose only launch
|
|
13
|
+
* content was a typo has nothing to launch, and a view that thinks otherwise
|
|
14
|
+
* waits on an assembly nobody named and never leaves the spinner.
|
|
15
|
+
*
|
|
16
|
+
* Returns the blob itself, never a copy — the launch autorun clears by
|
|
17
|
+
* identity, and a fresh object per read strands the init it just applied.
|
|
18
|
+
*/
|
|
19
|
+
export function pendingLaunch(launch) {
|
|
20
|
+
return launch && Object.keys(launch).some(k => !REPORTED.has(k))
|
|
21
|
+
? launch
|
|
22
|
+
: undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Declare one view's launch keys. The argument is
|
|
26
|
+
* `Record<keyof Commands, LaunchKeySpec>`, so a command the view interprets and
|
|
27
|
+
* nobody registered is a compile error rather than a key that partitions as a
|
|
28
|
+
* typo and warns.
|
|
29
|
+
*
|
|
30
|
+
* `passThrough` names keys that are neither a launch key nor a declared
|
|
31
|
+
* property and still belong on the snapshot — a legacy spelling the view's own
|
|
32
|
+
* `preProcessSnapshot` converts.
|
|
33
|
+
*/
|
|
34
|
+
export function defineLaunchKeys() {
|
|
35
|
+
return (keys, { passThrough } = {}) => ({
|
|
36
|
+
keys,
|
|
37
|
+
passThrough: passThrough ?? [],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// `id` picks up MST's optional identifier and `type` picks the view, so neither
|
|
41
|
+
// is ever a setting and neither can be lifted off the snapshot.
|
|
42
|
+
const IDENTITY = new Set(['id', 'type']);
|
|
43
|
+
const LAUNCH = 'launch';
|
|
44
|
+
/**
|
|
45
|
+
* The part of a launch spec that belongs on the snapshot: everything but the
|
|
46
|
+
* launch keys, and the identity and `launch` keys no spec may set. For a caller
|
|
47
|
+
* that applies the launch itself and builds the view from a snapshot — a
|
|
48
|
+
* synteny row, whose parent navigates it and opens its tracks — so what it
|
|
49
|
+
* hands MST is settings only, and the view's own partition sorts those into
|
|
50
|
+
* properties and reported typos.
|
|
51
|
+
*/
|
|
52
|
+
export function snapshotSettings(spec, registration) {
|
|
53
|
+
const keys = registration?.keys ?? {};
|
|
54
|
+
return Object.fromEntries(Object.entries(spec).filter(([key]) => !(key in keys) && !IDENTITY.has(key) && key !== LAUNCH));
|
|
55
|
+
}
|
|
56
|
+
function isObject(value) {
|
|
57
|
+
return !!value && typeof value === 'object';
|
|
58
|
+
}
|
|
59
|
+
// Each per-entry kind says only which entries are an authored recipe. A recipe
|
|
60
|
+
// goes to the blob for the launcher to resolve; everything else is a built
|
|
61
|
+
// snapshot and stays on the property, so a mixed array splits rather than
|
|
62
|
+
// picking one meaning for the whole thing.
|
|
63
|
+
function isRecipeEntry(kind, entry) {
|
|
64
|
+
switch (kind) {
|
|
65
|
+
case 'trackEntries':
|
|
66
|
+
// a built track snapshot cannot carry `trackId` — BaseTrackModel does not
|
|
67
|
+
// declare it. `type` does not discriminate: a spec writes display types
|
|
68
|
+
// inline
|
|
69
|
+
return (typeof entry === 'string' || (isObject(entry) && 'trackId' in entry));
|
|
70
|
+
case 'highlightEntries':
|
|
71
|
+
// a string needs coerceHighlight and the assembly manager, neither of
|
|
72
|
+
// which a pure preprocessor can reach; an object is the persisted shape
|
|
73
|
+
return typeof entry === 'string';
|
|
74
|
+
case 'rows':
|
|
75
|
+
// a built row is a view snapshot, and every view's `type` is a required
|
|
76
|
+
// literal
|
|
77
|
+
return !(isObject(entry) && 'type' in entry);
|
|
78
|
+
default:
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function splitEntries(kind, value) {
|
|
83
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
84
|
+
const recipes = entries.filter(e => isRecipeEntry(kind, e));
|
|
85
|
+
const built = entries.filter(e => !isRecipeEntry(kind, e));
|
|
86
|
+
return {
|
|
87
|
+
launch: recipes.length
|
|
88
|
+
? Array.isArray(value)
|
|
89
|
+
? recipes
|
|
90
|
+
: value
|
|
91
|
+
: undefined,
|
|
92
|
+
state: built.length ? (Array.isArray(value) ? built : value) : undefined,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// The properties a snapshot's keys are classified against are the REGISTERED
|
|
96
|
+
// view type's, read at preprocess time: `extendViewType` composes onto the
|
|
97
|
+
// model after `withLaunchInput` wrapped it, and a set frozen at wrap time sent
|
|
98
|
+
// every added property to `unknown` — restored at its default, with a warning
|
|
99
|
+
// naming it a typo. The wrapped model's own properties are the answer when no
|
|
100
|
+
// registry is given, or the snapshot names a type it does not hold, which is
|
|
101
|
+
// how a factory-built model in a test behaves.
|
|
102
|
+
function knownPropsResolver(model, registry) {
|
|
103
|
+
let cachedProps;
|
|
104
|
+
let cachedKeys = new Set();
|
|
105
|
+
return (snap) => {
|
|
106
|
+
const name = snap.type;
|
|
107
|
+
const registered = registry && typeof name === 'string' && registry.viewTypes.has(name)
|
|
108
|
+
? registry.getViewType(name)
|
|
109
|
+
: undefined;
|
|
110
|
+
const props = registered?.isStateModelLoaded
|
|
111
|
+
? registered.stateModel.properties
|
|
112
|
+
: model.properties;
|
|
113
|
+
if (props !== cachedProps) {
|
|
114
|
+
cachedProps = props;
|
|
115
|
+
cachedKeys = new Set(Object.keys(props));
|
|
116
|
+
}
|
|
117
|
+
return cachedKeys;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function classify(snap, { keys, passThrough }, known, out) {
|
|
121
|
+
for (const [key, value] of Object.entries(snap)) {
|
|
122
|
+
const spec = keys[key];
|
|
123
|
+
if (IDENTITY.has(key)) {
|
|
124
|
+
out.state[key] = value;
|
|
125
|
+
}
|
|
126
|
+
else if (spec?.kind === 'launch') {
|
|
127
|
+
out.launch[key] = value;
|
|
128
|
+
}
|
|
129
|
+
else if (spec?.kind === 'replay') {
|
|
130
|
+
out.state[key] = value;
|
|
131
|
+
out.launch[key] = value;
|
|
132
|
+
}
|
|
133
|
+
else if (spec) {
|
|
134
|
+
const { launch, state } = splitEntries(spec.kind, value);
|
|
135
|
+
if (spec.kind === 'rows' && launch !== undefined && state !== undefined) {
|
|
136
|
+
// A row list indexes against `levels` and `tracks[i]`, so lifting half
|
|
137
|
+
// of it renumbers the other half. Nothing here can throw — a pure
|
|
138
|
+
// preprocessor runs against snapshots the union is about to reject — so
|
|
139
|
+
// the whole list goes to the bucket afterAttach reports and the view
|
|
140
|
+
// comes up on its import form rather than on a silently misaligned
|
|
141
|
+
// stack.
|
|
142
|
+
out.malformed[key] = value;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
if (launch !== undefined) {
|
|
146
|
+
out.launch[key] = launch;
|
|
147
|
+
}
|
|
148
|
+
if (state !== undefined) {
|
|
149
|
+
out.state[key] = state;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else if (known.has(key) || passThrough.includes(key)) {
|
|
154
|
+
out.state[key] = value;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
out.unknown[key] = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// BreakpointSplitView's v4 `init` was a bare array of panels rather than an
|
|
162
|
+
// object of settings. A positional list can only be the view's row list, and a
|
|
163
|
+
// view declares at most one, so it is read as that key rather than classified
|
|
164
|
+
// per index — which would name "0" and "1" as unknown keys.
|
|
165
|
+
function legacyInitSnapshot(init, { keys }) {
|
|
166
|
+
const rows = Object.entries(keys).find(([, spec]) => spec.kind === 'rows')?.[0];
|
|
167
|
+
return Array.isArray(init) && rows ? { [rows]: init } : init;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Accept a view's settings written directly on the view object, and move the
|
|
171
|
+
* ones a launcher has to resolve into the internal `launch` property.
|
|
172
|
+
*
|
|
173
|
+
* The partition is a `preProcessSnapshot` and is PURE. A session's view type is
|
|
174
|
+
* a `types.union`, so MST runs every member's preprocessor against every
|
|
175
|
+
* candidate snapshot while deciding which one matches, and runs it about twice
|
|
176
|
+
* more per instantiation; a warning from in here fires against snapshots that
|
|
177
|
+
* are about to be rejected. `afterAttach` — reached only by a snapshot that won
|
|
178
|
+
* — reports what the partition captured.
|
|
179
|
+
*
|
|
180
|
+
* `registry` is the PluginManager. With it the partition classifies against
|
|
181
|
+
* the view type as registered, `extendViewType`'s added properties included;
|
|
182
|
+
* without it, against the model as wrapped here. `materialized` draws the line
|
|
183
|
+
* past which the blob is no longer persisted; see `LaunchInputOptions`.
|
|
184
|
+
*
|
|
185
|
+
* ORDER: MST runs preprocessors in the reverse of the order they were added, so
|
|
186
|
+
* this belongs on the chain BEFORE a view's own legacy-key preprocessor, where
|
|
187
|
+
* it partitions the snapshot MST finally consumes rather than a key that remap
|
|
188
|
+
* is about to convert.
|
|
189
|
+
*
|
|
190
|
+
* The widening cast is the LAST link that may change the creation type: it
|
|
191
|
+
* replaces `CustomC`, so a `.props()` added after it is invisible to
|
|
192
|
+
* `SnapshotIn`. Further `preProcessSnapshot`/`postProcessSnapshot` links are
|
|
193
|
+
* fine — both carry `CustomC` through.
|
|
194
|
+
*
|
|
195
|
+
* Not `types.snapshotProcessor`, which carries the same widened input and stops
|
|
196
|
+
* being a `ModelType`. `PluginManager.pluggableMstType` filters union members on
|
|
197
|
+
* `isModelType`, so a wrapped view is dropped from the session's view union
|
|
198
|
+
* without a word, and `ViewType.stateModel` and every `.properties`
|
|
199
|
+
* introspection site break with it.
|
|
200
|
+
*/
|
|
201
|
+
export function withLaunchInput(model, registration, { registry, materialized }) {
|
|
202
|
+
const knownProps = knownPropsResolver(model, registry);
|
|
203
|
+
const partitioned = model
|
|
204
|
+
.preProcessSnapshot((snap) => {
|
|
205
|
+
if (!isObject(snap)) {
|
|
206
|
+
return snap;
|
|
207
|
+
}
|
|
208
|
+
const known = knownProps(snap);
|
|
209
|
+
const { init, ...rest } = snap;
|
|
210
|
+
const out = {
|
|
211
|
+
state: {},
|
|
212
|
+
launch: {},
|
|
213
|
+
unknown: {},
|
|
214
|
+
malformed: {},
|
|
215
|
+
};
|
|
216
|
+
if (isObject(init)) {
|
|
217
|
+
// v4's nesting, unwrapped rather than refused: a nested key sorts the
|
|
218
|
+
// same three ways a flat one does, so honoring it costs one more pass
|
|
219
|
+
// and a deprecation warning, where dropping it opens an admin's
|
|
220
|
+
// `defaultSession` on its defaults. First, so a snapshot spelling one
|
|
221
|
+
// key both ways resolves to the flat one the docs teach.
|
|
222
|
+
classify(legacyInitSnapshot(init, registration), registration, known, out);
|
|
223
|
+
out.launch.legacyInit = true;
|
|
224
|
+
}
|
|
225
|
+
classify(rest, registration, known, out);
|
|
226
|
+
if (Object.keys(out.unknown).length) {
|
|
227
|
+
out.launch.unknown = out.unknown;
|
|
228
|
+
}
|
|
229
|
+
if (Object.keys(out.malformed).length) {
|
|
230
|
+
out.launch.malformed = out.malformed;
|
|
231
|
+
}
|
|
232
|
+
const persisted = out.state[LAUNCH];
|
|
233
|
+
return Object.keys(out.launch).length
|
|
234
|
+
? {
|
|
235
|
+
...out.state,
|
|
236
|
+
[LAUNCH]: {
|
|
237
|
+
...(isObject(persisted) ? persisted : {}),
|
|
238
|
+
...out.launch,
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
: out.state;
|
|
242
|
+
})
|
|
243
|
+
.actions(self => ({
|
|
244
|
+
afterAttach() {
|
|
245
|
+
const { launch } = self;
|
|
246
|
+
if (launch?.legacyInit) {
|
|
247
|
+
reportLegacyInit(self);
|
|
248
|
+
}
|
|
249
|
+
reportUnknownKeys(self, Object.keys(launch?.unknown ?? {}));
|
|
250
|
+
reportMalformedRows(self, Object.keys(launch?.malformed ?? {}));
|
|
251
|
+
},
|
|
252
|
+
}))
|
|
253
|
+
// Whole while the view has yet to materialize, since the pending launch is
|
|
254
|
+
// then the only thing a reload can rebuild the view from, and gone once it
|
|
255
|
+
// has — the report keys with it, so a saved session never carries a
|
|
256
|
+
// typo report about content it no longer holds. `legacyInit` goes either
|
|
257
|
+
// way: it names the SPELLING of the snapshot this view was opened from,
|
|
258
|
+
// and the saved one does not use it, so persisting it would report nesting
|
|
259
|
+
// on every restore forever (`pendingLaunch` excludes the report keys, so
|
|
260
|
+
// no launch run ever clears it).
|
|
261
|
+
.postProcessSnapshot((snap) => {
|
|
262
|
+
const launch = snap[LAUNCH];
|
|
263
|
+
if (!isObject(launch)) {
|
|
264
|
+
return snap;
|
|
265
|
+
}
|
|
266
|
+
const { legacyInit, ...kept } = launch;
|
|
267
|
+
return Object.keys(kept).length &&
|
|
268
|
+
!materialized(snap)
|
|
269
|
+
? { ...snap, [LAUNCH]: kept }
|
|
270
|
+
: Object.fromEntries(Object.entries(snap).filter(([key]) => key !== LAUNCH));
|
|
271
|
+
});
|
|
272
|
+
return partitioned;
|
|
273
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/core",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "JBrowse 2 core libraries used by plugins",
|
|
6
6
|
"keywords": [
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
"./PluginLoader": "./esm/PluginLoader.js",
|
|
33
33
|
"./PluginManager": "./esm/PluginManager.js",
|
|
34
34
|
"./ReExports/list": "./esm/ReExports/list.js",
|
|
35
|
+
"./ReExports/modules": "./esm/ReExports/modules.js",
|
|
36
|
+
"./ReExports/registry": "./esm/ReExports/registry.js",
|
|
35
37
|
"./TextSearch/BaseResults": "./esm/TextSearch/BaseResults.js",
|
|
36
38
|
"./TextSearch/TextSearchManager": "./esm/TextSearch/TextSearchManager.js",
|
|
37
39
|
"./assemblyManager": "./esm/assemblyManager/index.js",
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
"./data_adapters/BaseAdapter/getAdapterId": "./esm/data_adapters/BaseAdapter/getAdapterId.js",
|
|
45
47
|
"./data_adapters/BaseAdapter/stats": "./esm/data_adapters/BaseAdapter/stats.js",
|
|
46
48
|
"./data_adapters/BaseAdapter/types": "./esm/data_adapters/BaseAdapter/types.js",
|
|
49
|
+
"./data_adapters/adapterSessionRefcount": "./esm/data_adapters/adapterSessionRefcount.js",
|
|
47
50
|
"./data_adapters/dataAdapterCache": "./esm/data_adapters/dataAdapterCache.js",
|
|
48
51
|
"./data_adapters/getFeatureAdapter": "./esm/data_adapters/getFeatureAdapter.js",
|
|
49
52
|
"./data_adapters/getSequenceSubAdapter": "./esm/data_adapters/getSequenceSubAdapter.js",
|
|
@@ -140,14 +143,13 @@
|
|
|
140
143
|
"./util/Base1DUtils": "./esm/util/Base1DUtils.js",
|
|
141
144
|
"./util/Base1DViewModel": "./esm/util/Base1DViewModel.js",
|
|
142
145
|
"./util/FileSaver": "./esm/util/FileSaver/index.js",
|
|
143
|
-
"./util/QuickLRU": "./esm/util/QuickLRU/index.js",
|
|
144
146
|
"./util/SvgCanvas": "./esm/util/SvgCanvas.js",
|
|
145
147
|
"./util/TimeTraveller": "./esm/util/TimeTraveller.js",
|
|
146
148
|
"./util/aborting": "./esm/util/aborting.js",
|
|
149
|
+
"./util/adapterConfigKey": "./esm/util/adapterConfigKey.js",
|
|
147
150
|
"./util/adapterMetadata": "./esm/util/adapterMetadata.js",
|
|
148
151
|
"./util/addRelativeUris": "./esm/util/addRelativeUris.js",
|
|
149
152
|
"./util/analytics": "./esm/util/analytics.js",
|
|
150
|
-
"./util/applyInitSettings": "./esm/util/applyInitSettings.js",
|
|
151
153
|
"./util/assemblyConfigUtils": "./esm/util/assemblyConfigUtils.js",
|
|
152
154
|
"./util/bandHeight": "./esm/util/bandHeight.js",
|
|
153
155
|
"./util/bandLayout": "./esm/util/bandLayout.js",
|
|
@@ -185,6 +187,7 @@
|
|
|
185
187
|
"./util/installClearHoverOnSurfaceMove": "./esm/util/installClearHoverOnSurfaceMove.js",
|
|
186
188
|
"./util/installFetch": "./esm/util/installFetch.js",
|
|
187
189
|
"./util/installInitAutorun": "./esm/util/installInitAutorun.js",
|
|
190
|
+
"./util/installPrerequisiteFetch": "./esm/util/installPrerequisiteFetch.js",
|
|
188
191
|
"./util/io": "./esm/util/io/index.js",
|
|
189
192
|
"./util/io/RemoteFileWithRangeCache": "./esm/util/io/RemoteFileWithRangeCache.js",
|
|
190
193
|
"./util/isDataCurrent": "./esm/util/isDataCurrent.js",
|
|
@@ -194,14 +197,18 @@
|
|
|
194
197
|
"./util/layouts": "./esm/util/layouts/index.js",
|
|
195
198
|
"./util/layouts/GranularRectLayout": "./esm/util/layouts/GranularRectLayout.js",
|
|
196
199
|
"./util/layouts/placeRect": "./esm/util/layouts/placeRect.js",
|
|
200
|
+
"./util/lazyAfterAttach": "./esm/util/lazyAfterAttach.js",
|
|
197
201
|
"./util/leadingEdgeAutorun": "./esm/util/leadingEdgeAutorun.js",
|
|
202
|
+
"./util/legendCandidates": "./esm/util/legendCandidates.js",
|
|
198
203
|
"./util/librpc": "./esm/util/librpc.js",
|
|
199
204
|
"./util/localStorage": "./esm/util/localStorage.js",
|
|
200
205
|
"./util/makeAssembly": "./esm/util/makeAssembly.js",
|
|
206
|
+
"./util/mst-reflection": "./esm/util/mst-reflection.js",
|
|
201
207
|
"./util/mstUtils": "./esm/util/mstUtils.js",
|
|
202
208
|
"./util/nanoid": "./esm/util/nanoid.js",
|
|
203
209
|
"./util/numericUtils": "./esm/util/numericUtils.js",
|
|
204
210
|
"./util/offscreenCanvasPonyfill": "./esm/util/offscreenCanvasPonyfill.js",
|
|
211
|
+
"./util/openViews": "./esm/util/openViews.js",
|
|
205
212
|
"./util/paintLayer": "./esm/util/paintLayer.js",
|
|
206
213
|
"./util/parseLineByLine": "./esm/util/parseLineByLine.js",
|
|
207
214
|
"./util/progress": "./esm/util/progress.js",
|
|
@@ -235,7 +242,8 @@
|
|
|
235
242
|
"./util/viewStatus": "./esm/util/viewStatus.js",
|
|
236
243
|
"./util/virtualRange": "./esm/util/virtualRange.js",
|
|
237
244
|
"./util/wheelZoom": "./esm/util/wheelZoom.js",
|
|
238
|
-
"./util/whenViewSettled": "./esm/util/whenViewSettled.js"
|
|
245
|
+
"./util/whenViewSettled": "./esm/util/whenViewSettled.js",
|
|
246
|
+
"./util/withLaunchInput": "./esm/util/withLaunchInput.js"
|
|
239
247
|
},
|
|
240
248
|
"files": [
|
|
241
249
|
"esm"
|
|
@@ -253,13 +261,13 @@
|
|
|
253
261
|
"@jbrowse/mobx-state-tree": "^6.5.1",
|
|
254
262
|
"@jbrowse/quick-lru": "^7.3.5",
|
|
255
263
|
"@leeoniya/ufuzzy": "^1.0.19",
|
|
256
|
-
"@mui/icons-material": "^9.
|
|
257
|
-
"@mui/material": "^9.
|
|
258
|
-
"@mui/system": "^9.
|
|
259
|
-
"@mui/x-data-grid": "^9.
|
|
264
|
+
"@mui/icons-material": "^9.4.0",
|
|
265
|
+
"@mui/material": "^9.4.0",
|
|
266
|
+
"@mui/system": "^9.4.0",
|
|
267
|
+
"@mui/x-data-grid": "^9.13.0",
|
|
260
268
|
"canvas-sequencer-ts": "^3.1.3",
|
|
261
269
|
"compare-versions": "^6.1.1",
|
|
262
|
-
"dompurify": "^3.4.
|
|
270
|
+
"dompurify": "^3.4.15",
|
|
263
271
|
"generic-filehandle2": "^2.4.0",
|
|
264
272
|
"idb": "^8.0.3",
|
|
265
273
|
"mobx": "^7.0.3",
|
|
@@ -267,8 +275,8 @@
|
|
|
267
275
|
"pako-esm2": "^2.0.2",
|
|
268
276
|
"rxjs": "^7.8.2",
|
|
269
277
|
"source-map-js": "^1.2.1",
|
|
270
|
-
"@jbrowse/add-track-core": "^5.0.0-beta.
|
|
271
|
-
"@jbrowse/render-core": "^5.0.0-beta.
|
|
278
|
+
"@jbrowse/add-track-core": "^5.0.0-beta.3",
|
|
279
|
+
"@jbrowse/render-core": "^5.0.0-beta.3"
|
|
272
280
|
},
|
|
273
281
|
"peerDependencies": {
|
|
274
282
|
"react": ">=19.0.0",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Pin } from '../configuration/promotablePin.ts';
|
|
2
|
-
import type { CheckboxMenuItem, RadioMenuItem } from './MenuTypes.ts';
|
|
3
|
-
import type { RadioOption, SettingRowOptions } from './toggleMenuItems.ts';
|
|
4
|
-
/** #menuBuilder promotableToggleItem | `checkboxItem` plus an apply-to-open-tracks pin */
|
|
5
|
-
export declare function promotableToggleItem({ label, checked, onToggle, pin, ...opts }: {
|
|
6
|
-
label: string;
|
|
7
|
-
checked: boolean;
|
|
8
|
-
onToggle: () => void;
|
|
9
|
-
pin: Pin;
|
|
10
|
-
} & SettingRowOptions): CheckboxMenuItem;
|
|
11
|
-
/** #menuBuilder promotableRadioItem | `radioItem` plus an apply-to-open-tracks pin */
|
|
12
|
-
export declare function promotableRadioItem({ label, checked, onClick, pin, ...opts }: {
|
|
13
|
-
label: string;
|
|
14
|
-
checked: boolean;
|
|
15
|
-
onClick: () => void;
|
|
16
|
-
pin?: Pin;
|
|
17
|
-
} & SettingRowOptions): RadioMenuItem;
|
|
18
|
-
/** #menuBuilder promotableRadioItems | `radioItems` plus a pin per option, from a factory over the value */
|
|
19
|
-
export declare function promotableRadioItems<T extends string>(options: readonly RadioOption<T>[], current: T | undefined, setMode: (m: T) => void, pin: (value: T) => Pin): RadioMenuItem[];
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { checkboxItem, radioItem, radioItems } from './toggleMenuItems.js';
|
|
2
|
-
// Every builder here describes the trailing pin (`pin`) rather than
|
|
3
|
-
// rendering it. That is what keeps this module — and therefore every state
|
|
4
|
-
// model and menu file that calls it — free of React and of MUI's
|
|
5
|
-
// ToggleButton/Tooltip. `menuItemAdornment.tsx` turns the description into
|
|
6
|
-
// `PinAdornment` at the point the menu is drawn. See
|
|
7
|
-
// reference/EAGER_BUNDLE.md; `menuItems.purity.test.ts` holds it.
|
|
8
|
-
//
|
|
9
|
-
// Each is the plain builder in `toggleMenuItems.ts` plus a pin, and each is
|
|
10
|
-
// written that way — `checkboxItem` / `radioItems` build the row — so the
|
|
11
|
-
// promotable and plain forms of a row can differ only by the pin.
|
|
12
|
-
// A promotable setting as one native checkbox menu row: the value toggles the
|
|
13
|
-
// track (inheriting native hover/sizing/keyboard), and a trailing pin
|
|
14
|
-
// (endAdornment) applies this value to every open track of the display type,
|
|
15
|
-
// offering the display-type default as its snackbar's one action. Always shown
|
|
16
|
-
// so the capability is discoverable.
|
|
17
|
-
//
|
|
18
|
-
// **The row itself is `checkboxItem`'s**, so the two builders can only ever
|
|
19
|
-
// differ by the pin. They used to be two literals, and had already drifted: this
|
|
20
|
-
// one accepted `helpText` and `keepMenuOpen` while silently dropping `disabled`
|
|
21
|
-
// and `disabledHelpText`, so a promotable row could not be greyed out the way
|
|
22
|
-
// its plain sibling could. Sharing the body is also what makes a future
|
|
23
|
-
// change to what a checkbox row *is* reach both — the failure `checkboxItem`'s
|
|
24
|
-
// own comment records is a menu regressing by omission.
|
|
25
|
-
//
|
|
26
|
-
// `keepMenuOpen` is left unset by default, so `CascadingMenu` keeps the row open
|
|
27
|
-
// by its checkbox type: these rows only write a setting, users flip several in
|
|
28
|
-
// one visit, and the menu is an observer so the ticks and pins move live. A row
|
|
29
|
-
// whose click opens a dialog instead (colorBy's "Tag...") passes false.
|
|
30
|
-
/** #menuBuilder promotableToggleItem | `checkboxItem` plus an apply-to-open-tracks pin */
|
|
31
|
-
export function promotableToggleItem({ label, checked, onToggle, pin, ...opts }) {
|
|
32
|
-
return {
|
|
33
|
-
...checkboxItem(label, checked, onToggle, opts),
|
|
34
|
-
pin: { control: pin, label },
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
// ONE radio row of a promotable-value group — the escape hatch, for a group the
|
|
38
|
-
// plural builder below can't express. **Reach for `promotableRadioItems` first**:
|
|
39
|
-
// every option in a group has to get a pin, and hand-naming the rows is what
|
|
40
|
-
// leaves one without.
|
|
41
|
-
//
|
|
42
|
-
// The rows that genuinely need this: `pin` is optional here, for a row with no
|
|
43
|
-
// single value to promote yet (the colorBy "Tag..." row before a tag is picked)
|
|
44
|
-
// or a display whose slot isn't promotable at all (the shared colorBy menu on
|
|
45
|
-
// gwas/variants); and the alignments size presets, whose group is gated row by
|
|
46
|
-
// row (`needsContent`) and mixes in a non-promotable "Custom..." peer.
|
|
47
|
-
//
|
|
48
|
-
// **The row itself is `radioItem`'s**, for the reason `promotableToggleItem`'s
|
|
49
|
-
// is `checkboxItem`'s: it was a second literal, and it had drifted the same way
|
|
50
|
-
// its checkbox sibling had — naming `helpText`/`keepMenuOpen` by hand and so
|
|
51
|
-
// silently dropping `disabled` and `disabledHelpText`, which the
|
|
52
|
-
// alignments size presets have to bolt back on afterwards (`needsContent`).
|
|
53
|
-
/** #menuBuilder promotableRadioItem | `radioItem` plus an apply-to-open-tracks pin */
|
|
54
|
-
export function promotableRadioItem({ label, checked, onClick, pin, ...opts }) {
|
|
55
|
-
return {
|
|
56
|
-
...radioItem(label, checked, onClick, opts),
|
|
57
|
-
...(pin && {
|
|
58
|
-
pin: { control: pin, label },
|
|
59
|
-
}),
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
// A whole promotable radio group: `radioItems` plus one pin per option. The
|
|
63
|
-
// plural form of the builder above, and the one to reach for on a group whose
|
|
64
|
-
// options differ only in their value — which is all four of them today
|
|
65
|
-
// (`heightMode`, `displayMode`, `subfeatureLabels`, `sashimiArcsMode`).
|
|
66
|
-
//
|
|
67
|
-
// **Every option in a group gets a pin, the `promotedBase` value included** —
|
|
68
|
-
// once a non-base value is promoted, pinning the base back is the only per-value
|
|
69
|
-
// way to undo it from its own row, and a radio group with one row silently
|
|
70
|
-
// missing its trailing control reads as a bug. Building the group here is what
|
|
71
|
-
// makes that structural instead of a rule to remember: `sashimiArcsMode`'s base
|
|
72
|
-
// looked unpinnable precisely because each row had been named by hand. Prefer
|
|
73
|
-
// this over `.map(promotableRadioItem)` whenever the pin is a function of the
|
|
74
|
-
// option's value.
|
|
75
|
-
//
|
|
76
|
-
// `pin` is a factory rather than an array so it can't be a row short. Pass
|
|
77
|
-
// `value => makePin(self, slot, value)`, or a model member of that shape where
|
|
78
|
-
// the menu module is handed a duck-typed model (alignments).
|
|
79
|
-
//
|
|
80
|
-
// The rows are `radioItems`' own, so the promotable and plain groups can't drift
|
|
81
|
-
// on anything but the pin. `radioItems` maps `options` in order, which is what
|
|
82
|
-
// lets each row be paired back to the option that built it.
|
|
83
|
-
/** #menuBuilder promotableRadioItems | `radioItems` plus a pin per option, from a factory over the value */
|
|
84
|
-
export function promotableRadioItems(options, current, setMode, pin) {
|
|
85
|
-
return radioItems(options, current, setMode).map((item, i) => {
|
|
86
|
-
const { value, label } = options[i];
|
|
87
|
-
return { ...item, pin: { control: pin(value), label } };
|
|
88
|
-
});
|
|
89
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { IAnyModelType, IStateTreeNode, SnapshotIn } from '@jbrowse/mobx-state-tree';
|
|
2
|
-
/**
|
|
3
|
-
* A view's init type, derived: the keys its launcher interprets (`Commands`)
|
|
4
|
-
* plus every declared property of its state model, optional, in the property's
|
|
5
|
-
* own snapshot type. Nothing is restated, so a property is authorable — and
|
|
6
|
-
* TYPED — from the line that declares it.
|
|
7
|
-
*
|
|
8
|
-
* ```ts
|
|
9
|
-
* export type LinearSyntenyViewInit = ViewInit<
|
|
10
|
-
* LinearSyntenyViewStateModel,
|
|
11
|
-
* LinearSyntenyViewCommands
|
|
12
|
-
* >
|
|
13
|
-
* ```
|
|
14
|
-
*
|
|
15
|
-
* THE ONE RULE THIS DEPENDS ON: the model's own `init` property must be typed
|
|
16
|
-
* as the `Commands`, not as this. `init` is a property, so an `init` typed as
|
|
17
|
-
* the whole thing makes the whole thing depend on itself — TS7023 on the
|
|
18
|
-
* factory, then implicit-any across every file that touches the view (ADR-055,
|
|
19
|
-
* same failure the view/display instance pair has). Commands never mention the
|
|
20
|
-
* model, so typing `init` as those breaks the loop, and it costs nothing: the
|
|
21
|
-
* view's own code reads the commands out of `self.init`, and the settings half
|
|
22
|
-
* is read only by `applyInitSettings`, which asks the model rather than a type.
|
|
23
|
-
*/
|
|
24
|
-
export type ViewInit<M extends IAnyModelType, Commands> = Commands & Partial<Omit<SnapshotIn<M>, keyof Commands | (typeof RESERVED_KEYS)[number]>>;
|
|
25
|
-
export declare const RESERVED_KEYS: readonly ['id', 'type', 'init'];
|
|
26
|
-
export interface InitSettingsReport {
|
|
27
|
-
/** keys applied, in the order they were found */
|
|
28
|
-
applied: string[];
|
|
29
|
-
/** keys naming no declared property — a typo, or a prop that never existed */
|
|
30
|
-
unknown: string[];
|
|
31
|
-
/** declared keys whose value the property's own type rejected */
|
|
32
|
-
invalid: string[];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Apply the plain-settings half of an `init` blob: every key naming a declared
|
|
36
|
-
* MST property of `self` is written straight to it, and the caller handles only
|
|
37
|
-
* the keys it names in `commands`.
|
|
38
|
-
*
|
|
39
|
-
* THE POINT IS THAT THERE IS NO PER-SETTING CODE. A view used to need a
|
|
40
|
-
* hand-written `if (init.x !== undefined) self.setX(init.x)` arm per property,
|
|
41
|
-
* so a new toggle worked from its menu the moment it was declared and was
|
|
42
|
-
* dropped in silence from every authored surface — a session spec, a share
|
|
43
|
-
* link, a config `defaultSession` — until someone remembered the second edit.
|
|
44
|
-
* Nobody did: `drawLocationMarkers` shipped unauthorable, and DotplotView's
|
|
45
|
-
* `alpha`/`drawCigar`/`lineWidth`/`lockAspectRatio` never had an arm at all.
|
|
46
|
-
* Reading the property list off the model at runtime means declaring the
|
|
47
|
-
* property IS declaring it authorable, including properties a composed mixin
|
|
48
|
-
* brings in (colorBy, showColorLegend), and there is nothing left to keep in
|
|
49
|
-
* sync.
|
|
50
|
-
*
|
|
51
|
-
* `commands` is what a launcher genuinely has to interpret: keys with no
|
|
52
|
-
* property behind them (`loc`, `autoDiagonalize`), keys whose name collides
|
|
53
|
-
* with a property meaning something else (a spec's `views` is a list of
|
|
54
|
-
* assemblies to open; the model's `views` is the built rows), and keys with an
|
|
55
|
-
* ordering constraint (`levelHeights` must land after the auto-scale pass).
|
|
56
|
-
* Listing one is a decision to write code for it; everything else needs none.
|
|
57
|
-
*
|
|
58
|
-
* WRITTEN AS A PATCH, not through `setX`. Applying init is state restoration,
|
|
59
|
-
* and the authority on that is MST: `applyPatch` validates each value against
|
|
60
|
-
* the property's own type and touches nothing else, where `applySnapshot`
|
|
61
|
-
* would rebuild the view's children. That it skips the setters is correct
|
|
62
|
-
* rather than a shortcut — a session reload restores the same state from a
|
|
63
|
-
* snapshot and calls no setters either, so a property needing a setter's side
|
|
64
|
-
* effect to be correct is already broken on reload, and the effect belongs in a
|
|
65
|
-
* reaction on the property.
|
|
66
|
-
*
|
|
67
|
-
* A value the property's type rejects is dropped and reported rather than
|
|
68
|
-
* thrown: init blobs come from URLs, and one bad number should not cost the
|
|
69
|
-
* reader the whole view.
|
|
70
|
-
*/
|
|
71
|
-
export declare function applyInitSettings(self: IStateTreeNode, init: object | undefined, { commands }?: {
|
|
72
|
-
commands?: readonly string[];
|
|
73
|
-
}): InitSettingsReport;
|
|
74
|
-
/**
|
|
75
|
-
* Say what `applyInitSettings` refused, naming the surface. Split from the
|
|
76
|
-
* apply so a caller under test can assert the report without a console, and so
|
|
77
|
-
* the two messages read the same wherever they come from.
|
|
78
|
-
*/
|
|
79
|
-
export declare function warnInitSettings(surface: string, report: InitSettingsReport): void;
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { applyPatch, getPropertyMembers } from '@jbrowse/mobx-state-tree';
|
|
2
|
-
// Keys that are a view's identity or its plumbing rather than a setting. A
|
|
3
|
-
// launch spec never gets to write these: `type` picks the view, `id` is passed
|
|
4
|
-
// top-level so MST's optional identifier honors it, and `init` inside `init` is
|
|
5
|
-
// the nesting mistake loadSessionSpec already reports.
|
|
6
|
-
export const RESERVED_KEYS = ['id', 'type', 'init'];
|
|
7
|
-
const RESERVED = new Set(RESERVED_KEYS);
|
|
8
|
-
/**
|
|
9
|
-
* Apply the plain-settings half of an `init` blob: every key naming a declared
|
|
10
|
-
* MST property of `self` is written straight to it, and the caller handles only
|
|
11
|
-
* the keys it names in `commands`.
|
|
12
|
-
*
|
|
13
|
-
* THE POINT IS THAT THERE IS NO PER-SETTING CODE. A view used to need a
|
|
14
|
-
* hand-written `if (init.x !== undefined) self.setX(init.x)` arm per property,
|
|
15
|
-
* so a new toggle worked from its menu the moment it was declared and was
|
|
16
|
-
* dropped in silence from every authored surface — a session spec, a share
|
|
17
|
-
* link, a config `defaultSession` — until someone remembered the second edit.
|
|
18
|
-
* Nobody did: `drawLocationMarkers` shipped unauthorable, and DotplotView's
|
|
19
|
-
* `alpha`/`drawCigar`/`lineWidth`/`lockAspectRatio` never had an arm at all.
|
|
20
|
-
* Reading the property list off the model at runtime means declaring the
|
|
21
|
-
* property IS declaring it authorable, including properties a composed mixin
|
|
22
|
-
* brings in (colorBy, showColorLegend), and there is nothing left to keep in
|
|
23
|
-
* sync.
|
|
24
|
-
*
|
|
25
|
-
* `commands` is what a launcher genuinely has to interpret: keys with no
|
|
26
|
-
* property behind them (`loc`, `autoDiagonalize`), keys whose name collides
|
|
27
|
-
* with a property meaning something else (a spec's `views` is a list of
|
|
28
|
-
* assemblies to open; the model's `views` is the built rows), and keys with an
|
|
29
|
-
* ordering constraint (`levelHeights` must land after the auto-scale pass).
|
|
30
|
-
* Listing one is a decision to write code for it; everything else needs none.
|
|
31
|
-
*
|
|
32
|
-
* WRITTEN AS A PATCH, not through `setX`. Applying init is state restoration,
|
|
33
|
-
* and the authority on that is MST: `applyPatch` validates each value against
|
|
34
|
-
* the property's own type and touches nothing else, where `applySnapshot`
|
|
35
|
-
* would rebuild the view's children. That it skips the setters is correct
|
|
36
|
-
* rather than a shortcut — a session reload restores the same state from a
|
|
37
|
-
* snapshot and calls no setters either, so a property needing a setter's side
|
|
38
|
-
* effect to be correct is already broken on reload, and the effect belongs in a
|
|
39
|
-
* reaction on the property.
|
|
40
|
-
*
|
|
41
|
-
* A value the property's type rejects is dropped and reported rather than
|
|
42
|
-
* thrown: init blobs come from URLs, and one bad number should not cost the
|
|
43
|
-
* reader the whole view.
|
|
44
|
-
*/
|
|
45
|
-
export function applyInitSettings(self,
|
|
46
|
-
// `object` rather than a keyed record: an init blob's static type is the
|
|
47
|
-
// view's own `ViewInit`, which has named members and no index signature — by
|
|
48
|
-
// design, so a typo is a compile error there — and that is not assignable to
|
|
49
|
-
// `Record<string, unknown>`. This reads it by enumeration anyway.
|
|
50
|
-
init, { commands = [] } = {}) {
|
|
51
|
-
const report = { applied: [], unknown: [], invalid: [] };
|
|
52
|
-
if (!init) {
|
|
53
|
-
return report;
|
|
54
|
-
}
|
|
55
|
-
const skip = new Set(commands);
|
|
56
|
-
const { properties } = getPropertyMembers(self);
|
|
57
|
-
const patch = [];
|
|
58
|
-
for (const [key, value] of Object.entries(init)) {
|
|
59
|
-
if (skip.has(key) || RESERVED.has(key) || value === undefined) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
const propType = properties[key];
|
|
63
|
-
if (!propType) {
|
|
64
|
-
report.unknown.push(key);
|
|
65
|
-
}
|
|
66
|
-
else if (!propType.is(value)) {
|
|
67
|
-
report.invalid.push(key);
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
patch.push({ op: 'replace', path: `/${key}`, value });
|
|
71
|
-
report.applied.push(key);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (patch.length) {
|
|
75
|
-
applyPatch(self, patch);
|
|
76
|
-
}
|
|
77
|
-
return report;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Say what `applyInitSettings` refused, naming the surface. Split from the
|
|
81
|
-
* apply so a caller under test can assert the report without a console, and so
|
|
82
|
-
* the two messages read the same wherever they come from.
|
|
83
|
-
*/
|
|
84
|
-
export function warnInitSettings(surface, report) {
|
|
85
|
-
if (report.unknown.length) {
|
|
86
|
-
console.warn(`${surface} ignored unknown key(s): ${report.unknown.join(', ')}`);
|
|
87
|
-
}
|
|
88
|
-
if (report.invalid.length) {
|
|
89
|
-
console.warn(`${surface} ignored key(s) with an invalid value: ${report.invalid.join(', ')}`);
|
|
90
|
-
}
|
|
91
|
-
}
|