@jbrowse/product-core 4.3.0 → 5.0.0-beta.2
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/esm/RootModel/BaseRootModel.d.ts +629 -304
- package/esm/RootModel/BaseRootModel.js +139 -14
- package/esm/RootModel/HierarchicalConfig.d.ts +57 -21
- package/esm/RootModel/HierarchicalConfig.js +36 -0
- package/esm/RootModel/InternetAccounts.d.ts +18 -1
- package/esm/RootModel/InternetAccounts.js +120 -22
- package/esm/RootModel/PreferencesConfig.d.ts +68 -0
- package/esm/RootModel/PreferencesConfig.js +76 -0
- package/esm/RootModel/createConfigModel.d.ts +240 -0
- package/esm/RootModel/createConfigModel.js +56 -0
- package/esm/RootModel/index.d.ts +10 -5
- package/esm/RootModel/index.js +10 -5
- package/esm/RootModel/menuItems.d.ts +24 -0
- package/esm/RootModel/menuItems.js +143 -0
- package/esm/Session/BaseSession.d.ts +312 -11
- package/esm/Session/BaseSession.js +458 -14
- package/esm/Session/Connections.d.ts +114 -8
- package/esm/Session/Connections.js +195 -74
- package/esm/Session/DrawerWidgets.d.ts +115 -12
- package/esm/Session/DrawerWidgets.js +161 -42
- package/esm/Session/MultipleViews.d.ts +1281 -1128
- package/esm/Session/MultipleViews.js +396 -64
- package/esm/Session/Preferences.d.ts +1667 -0
- package/esm/Session/Preferences.js +59 -0
- package/esm/Session/ReferenceManagement.d.ts +28 -3
- package/esm/Session/ReferenceManagement.js +56 -41
- package/esm/Session/SessionTracks.d.ts +1277 -1138
- package/esm/Session/SessionTracks.js +540 -35
- package/esm/Session/Themes.d.ts +87 -2
- package/esm/Session/Themes.js +156 -23
- package/esm/Session/TrackMenu.d.ts +112 -0
- package/esm/Session/TrackMenu.js +200 -0
- package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
- package/esm/Session/TrackMenuSessionMixin.js +41 -0
- package/esm/Session/Tracks.d.ts +1171 -1117
- package/esm/Session/Tracks.js +183 -20
- package/esm/Session/assertSessionModel.d.ts +21 -0
- package/esm/Session/assertSessionModel.js +1 -0
- package/esm/Session/developerMode.d.ts +7 -0
- package/esm/Session/developerMode.js +43 -0
- package/esm/Session/finalizeSession.d.ts +16 -0
- package/esm/Session/finalizeSession.js +29 -0
- package/esm/Session/index.d.ts +22 -10
- package/esm/Session/index.js +13 -10
- package/esm/Session/readdedTrackConf.d.ts +8 -0
- package/esm/Session/readdedTrackConf.js +25 -0
- package/esm/Session/shareableSnapshot.d.ts +77 -0
- package/esm/Session/shareableSnapshot.js +196 -0
- package/esm/Session/temporaryAssemblyTracks.d.ts +24 -0
- package/esm/Session/temporaryAssemblyTracks.js +37 -0
- package/esm/assertExtends.d.ts +55 -0
- package/esm/assertExtends.js +1 -0
- package/esm/controllerTracks.d.ts +56 -0
- package/esm/controllerTracks.js +90 -0
- package/esm/destroyViewState.d.ts +21 -0
- package/esm/destroyViewState.js +29 -0
- package/esm/index.d.ts +31 -4
- package/esm/index.js +31 -4
- package/esm/loadPlugins.d.ts +33 -0
- package/esm/loadPlugins.js +41 -0
- package/esm/localFiles.d.ts +74 -0
- package/esm/localFiles.js +156 -0
- package/esm/observeSession.d.ts +56 -0
- package/esm/observeSession.js +91 -0
- package/esm/pluginInput.d.ts +32 -0
- package/esm/pluginInput.js +19 -0
- package/esm/pruneUnbuildableNodes.d.ts +109 -0
- package/esm/pruneUnbuildableNodes.js +568 -0
- package/esm/resolveAssemblies.d.ts +44 -0
- package/esm/resolveAssemblies.js +55 -0
- package/esm/rpcWorker.d.ts +26 -0
- package/esm/rpcWorker.js +98 -16
- package/esm/scheduleDetachedDestroy.d.ts +18 -0
- package/esm/scheduleDetachedDestroy.js +24 -0
- package/esm/sessionMigrations/index.d.ts +53 -0
- package/esm/sessionMigrations/index.js +432 -0
- package/esm/sessionUrl.d.ts +41 -0
- package/esm/sessionUrl.js +53 -0
- package/esm/sessionUtils.d.ts +69 -0
- package/esm/sessionUtils.js +613 -0
- package/esm/siblingCast.d.ts +14 -0
- package/esm/siblingCast.js +16 -0
- package/esm/snapshotUtils.d.ts +8 -0
- package/esm/snapshotUtils.js +12 -0
- package/esm/ui/AboutDialog.d.ts +3 -6
- package/esm/ui/AboutDialog.js +4 -4
- package/esm/ui/AboutDialogContents.d.ts +2 -6
- package/esm/ui/AboutDialogContents.js +34 -13
- package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
- package/esm/ui/AssemblyInfoPanel.js +69 -0
- package/esm/ui/DisplayDefaultsSection.d.ts +36 -0
- package/esm/ui/DisplayDefaultsSection.js +65 -0
- package/esm/ui/Drawer.d.ts +21 -0
- package/esm/ui/Drawer.js +60 -0
- package/esm/ui/DrawerControls.d.ts +5 -0
- package/esm/ui/DrawerControls.js +28 -0
- package/esm/ui/DrawerHeader.d.ts +6 -0
- package/esm/ui/DrawerHeader.js +36 -0
- package/esm/ui/DrawerWidget.d.ts +5 -0
- package/esm/ui/DrawerWidget.js +12 -0
- package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
- package/esm/ui/DrawerWidgetSelector.js +40 -0
- package/esm/ui/FileInfoPanel.d.ts +3 -6
- package/esm/ui/FileInfoPanel.js +35 -26
- package/esm/ui/HeaderButtons.d.ts +7 -1
- package/esm/ui/HeaderButtons.js +23 -18
- package/esm/ui/ModalWidget.d.ts +12 -0
- package/esm/ui/ModalWidget.js +33 -0
- package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
- package/esm/ui/ModalWidgetAppBar.js +9 -0
- package/esm/ui/PreferencesDialog.d.ts +56 -0
- package/esm/ui/PreferencesDialog.js +145 -0
- package/esm/ui/PreferencesResetDialog.d.ts +8 -0
- package/esm/ui/PreferencesResetDialog.js +19 -0
- package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
- package/esm/ui/RefNameAliasesDialog.js +65 -0
- package/esm/ui/RefNameInfoDialog.d.ts +3 -6
- package/esm/ui/RefNameInfoDialog.js +57 -63
- package/esm/ui/WidgetBody.d.ts +18 -0
- package/esm/ui/WidgetBody.js +22 -0
- package/esm/ui/WidgetHeading.d.ts +7 -0
- package/esm/ui/WidgetHeading.js +7 -0
- package/esm/ui/drawerLayout.d.ts +18 -0
- package/esm/ui/drawerLayout.js +22 -0
- package/esm/ui/index.d.ts +13 -1
- package/esm/ui/index.js +12 -1
- package/esm/ui/util.d.ts +37 -4
- package/esm/ui/util.js +35 -38
- package/esm/useEngineLifecycle.d.ts +19 -0
- package/esm/useEngineLifecycle.js +44 -0
- package/package.json +12 -15
- package/esm/RootModel/FormatAbout.d.ts +0 -12
- package/esm/RootModel/FormatAbout.js +0 -15
- package/esm/RootModel/FormatDetails.d.ts +0 -24
- package/esm/RootModel/FormatDetails.js +0 -27
- package/esm/Session/DialogQueue.d.ts +0 -17
- package/esm/Session/DialogQueue.js +0 -31
package/esm/Session/Tracks.js
CHANGED
|
@@ -1,49 +1,212 @@
|
|
|
1
|
+
import { expandLooseTrackConfig } from '@jbrowse/core/util/tracks';
|
|
1
2
|
import { types } from '@jbrowse/mobx-state-tree';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { computed } from 'mobx';
|
|
4
|
+
import { BaseSessionModel, isBaseSession } from './BaseSession.js';
|
|
5
|
+
import { isSessionWithConnections } from './Connections.js';
|
|
6
|
+
import { ReferenceManagementSessionMixin } from './ReferenceManagement.js';
|
|
7
|
+
import { assertNotReaddedDifferently } from './readdedTrackConf.js';
|
|
8
|
+
import { assertTrackConfOutlivesItsAssemblies } from './temporaryAssemblyTracks.js';
|
|
9
|
+
/**
|
|
10
|
+
* #stateModel TracksManagerSessionMixin
|
|
11
|
+
*/
|
|
4
12
|
export function TracksManagerSessionMixin(pluginManager) {
|
|
5
13
|
return types
|
|
6
14
|
.compose('TracksManagerSessionMixin', BaseSessionModel(pluginManager), ReferenceManagementSessionMixin(pluginManager))
|
|
7
15
|
.views(self => ({
|
|
16
|
+
/**
|
|
17
|
+
* #getter
|
|
18
|
+
*/
|
|
8
19
|
get tracks() {
|
|
9
20
|
return self.jbrowse.tracks;
|
|
10
21
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
22
|
+
}))
|
|
23
|
+
.views(self => {
|
|
24
|
+
// One index trackId → config for all tracks, assembly sequences, and
|
|
25
|
+
// connection tracks. Frozen jbrowse.tracks entries stay plain objects
|
|
26
|
+
// here; hydration to MST nodes happens lazily in TrackConfigurationReference
|
|
27
|
+
// on first access. Cached, so the N per-id lookups below share one rebuild
|
|
28
|
+
// per change rather than each re-scanning.
|
|
29
|
+
//
|
|
30
|
+
// `keepAlive` is what makes that last sentence true, and it is load
|
|
31
|
+
// bearing: an unobserved computed re-derives on EVERY read, and this one
|
|
32
|
+
// walks every track, assembly and connection the session can resolve. A
|
|
33
|
+
// reader inside a reaction or an MST action was already fine — MobX
|
|
34
|
+
// caches within a batch, which is why the resolvers and `showTrackGeneric`
|
|
35
|
+
// never showed it. Ranking search hits is the caller that is neither, and
|
|
36
|
+
// it cost 33ms per search on a 2000-track config against 0.07ms held.
|
|
37
|
+
const tracksByIdRecord = computed(() => {
|
|
15
38
|
const temporaryAssemblies = 'temporaryAssemblies' in self
|
|
16
39
|
? self.temporaryAssemblies
|
|
17
40
|
: [];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
41
|
+
// both come from the connections mixin, which not every session
|
|
42
|
+
// composes. connectionInstances is annotated because its element type
|
|
43
|
+
// is a runtime-pluggable stateModel, so the guard can't infer it.
|
|
44
|
+
const connectionInstances = isSessionWithConnections(self) ? self.connectionInstances : [];
|
|
45
|
+
const connectionTrackConfigs = isSessionWithConnections(self)
|
|
46
|
+
? self.connectionTrackConfigs
|
|
47
|
+
: {};
|
|
21
48
|
return Object.fromEntries([
|
|
22
|
-
...
|
|
23
|
-
|
|
49
|
+
...self.tracks.map(t => [t.trackId, t]),
|
|
50
|
+
// assembly sequence tracks, so they resolve by trackId
|
|
51
|
+
...self.assemblies.map(a => [a.sequence.trackId, a.sequence]),
|
|
24
52
|
...temporaryAssemblies.map(a => [a.sequence.trackId, a.sequence]),
|
|
25
53
|
...connectionInstances.flatMap(c => c.tracks.map(t => [t.trackId, t])),
|
|
54
|
+
// Persisted configs of opened connection tracks. Placed last so they
|
|
55
|
+
// win over the live connection instance: identity-stable across
|
|
56
|
+
// reload, and resolves even when the connection isn't re-established.
|
|
57
|
+
...Object.entries(connectionTrackConfigs).map(([trackId, e]) => [
|
|
58
|
+
trackId,
|
|
59
|
+
e.config,
|
|
60
|
+
]),
|
|
26
61
|
]);
|
|
27
|
-
},
|
|
28
|
-
|
|
62
|
+
}, { keepAlive: true });
|
|
63
|
+
// Per-id computed cache backing getTrackById. Resolving one track's config
|
|
64
|
+
// subscribes only to that id's computed, so editing track A leaves track
|
|
65
|
+
// B's observers untouched: an unedited id's entry keeps its object identity
|
|
66
|
+
// across a sibling edit, so its computed re-evaluates equal and MobX
|
|
67
|
+
// short-circuits — B never wakes. Derived on read (no reconcile autorun),
|
|
68
|
+
// so it is never stale mid-action: session hydration and add-and-show
|
|
69
|
+
// resolve straight through it. Not evicted — bounded by the distinct ids
|
|
70
|
+
// resolved this session, and holds no authoritative state.
|
|
71
|
+
const trackByIdComputeds = new Map();
|
|
72
|
+
return {
|
|
73
|
+
/**
|
|
74
|
+
* #method
|
|
75
|
+
* Config for one trackId — a track, assembly sequence, or connection
|
|
76
|
+
* track — or undefined. Per-id reactive: every display resolves its
|
|
77
|
+
* config through this (via TrackConfigurationReference) and subscribes
|
|
78
|
+
* only to its own id, so one track's settings edit doesn't re-render the
|
|
79
|
+
* others.
|
|
80
|
+
*/
|
|
81
|
+
getTrackById(id) {
|
|
82
|
+
let c = trackByIdComputeds.get(id);
|
|
83
|
+
if (!c) {
|
|
84
|
+
c = computed(() => tracksByIdRecord.get()[id]);
|
|
85
|
+
trackByIdComputeds.set(id, c);
|
|
86
|
+
}
|
|
87
|
+
return c.get();
|
|
88
|
+
},
|
|
89
|
+
/**
|
|
90
|
+
* #method
|
|
91
|
+
* Every track config the session can resolve, keyed by trackId. Prefer
|
|
92
|
+
* the per-id reactive `getTrackById(id)`: reading this whole map
|
|
93
|
+
* subscribes the caller to *every* track, so an edit to any one of them
|
|
94
|
+
* wakes it — the reason internal display config resolution moved off
|
|
95
|
+
* it. Kept for plugins that look up ids in a non-reactive context.
|
|
96
|
+
*
|
|
97
|
+
* @deprecated
|
|
98
|
+
*/
|
|
99
|
+
getTracksById() {
|
|
100
|
+
return tracksByIdRecord.get();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
})
|
|
29
104
|
.actions(self => ({
|
|
105
|
+
/**
|
|
106
|
+
* #action
|
|
107
|
+
* Add a track config to *this session*.
|
|
108
|
+
*
|
|
109
|
+
* This mixin's session has no separate session-track store, so the
|
|
110
|
+
* destination is the jbrowse config — which in the products that compose
|
|
111
|
+
* it (desktop) is the single user's own file rather than something a
|
|
112
|
+
* server hands other visitors, so the two scopes are the same place.
|
|
113
|
+
* Defined here anyway so that every session has it: a feature standing a
|
|
114
|
+
* track up on the user's behalf can then call one action everywhere
|
|
115
|
+
* instead of asking which mixin it landed on.
|
|
116
|
+
* `SessionTracksManagerSessionMixin` overrides it with the real
|
|
117
|
+
* session-scoped store.
|
|
118
|
+
*/
|
|
119
|
+
addSessionTrackConf(trackConf) {
|
|
120
|
+
assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
|
|
121
|
+
// the config's own adder appends without looking, so a re-add under a
|
|
122
|
+
// known id used to leave two entries with the first one winning
|
|
123
|
+
const expanded = expandLooseTrackConfig(trackConf, pluginManager);
|
|
124
|
+
const { trackId, type } = expanded;
|
|
125
|
+
const existing = self.getTrackById(trackId);
|
|
126
|
+
if (existing) {
|
|
127
|
+
assertNotReaddedDifferently(pluginManager, existing, {
|
|
128
|
+
...expanded,
|
|
129
|
+
trackId,
|
|
130
|
+
type,
|
|
131
|
+
});
|
|
132
|
+
return existing;
|
|
133
|
+
}
|
|
134
|
+
return self.jbrowse.addTrackConf(trackConf);
|
|
135
|
+
},
|
|
136
|
+
/**
|
|
137
|
+
* #action
|
|
138
|
+
* Add a track config wherever *this user's* catalog edits belong — the
|
|
139
|
+
* "Add track" workflows, where an admin adding a track means to add it
|
|
140
|
+
* for the whole site. `SessionTracksManagerSessionMixin` overrides it to
|
|
141
|
+
* send a non-admin's to the session instead; here there is only the one
|
|
142
|
+
* destination.
|
|
143
|
+
*
|
|
144
|
+
* Anything that is not an Add-track workflow wants
|
|
145
|
+
* `addSessionTrackConf`: a track a feature stands up on the user's behalf
|
|
146
|
+
* — a search result, a computed consensus, a reconstruction's labels — is
|
|
147
|
+
* not a catalog entry, and publishing one writes it into the config.json
|
|
148
|
+
* every visitor is served, once per click.
|
|
149
|
+
*/
|
|
150
|
+
publishTrackConf(trackConf) {
|
|
151
|
+
assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
|
|
152
|
+
return self.jbrowse.addTrackConf(trackConf);
|
|
153
|
+
},
|
|
154
|
+
/**
|
|
155
|
+
* #action
|
|
156
|
+
* Deprecated alias of `addSessionTrackConf`. Call that, or
|
|
157
|
+
* `publishTrackConf`, which say which destination they mean.
|
|
158
|
+
*
|
|
159
|
+
* @deprecated
|
|
160
|
+
*
|
|
161
|
+
* Kept because a prebuilt plugin bundle reaches this by name at runtime
|
|
162
|
+
* and cannot be recompiled — `jbrowse-plugin-protein3d` calls it, and a
|
|
163
|
+
* member lookup that stops resolving throws nothing at all
|
|
164
|
+
* (`pluginFacingSessionApi.test.ts` is the guard). It now means the
|
|
165
|
+
* session, which is the destination every such caller wanted: a track a
|
|
166
|
+
* plugin stands up for one user is not a catalog entry. Nothing in tree
|
|
167
|
+
* may call it — `no-restricted-syntax` says so.
|
|
168
|
+
*
|
|
169
|
+
* Spelled out rather than delegating through `this`, for the reason
|
|
170
|
+
* `SessionTracks.addToSession` is a plain closure: an action whose return
|
|
171
|
+
* type is inferred from a sibling's makes the pair circular.
|
|
172
|
+
*/
|
|
30
173
|
addTrackConf(trackConf) {
|
|
174
|
+
assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
|
|
31
175
|
return self.jbrowse.addTrackConf(trackConf);
|
|
32
176
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
177
|
+
/**
|
|
178
|
+
* #action
|
|
179
|
+
* Persist edited track config back to the in-memory jbrowse config. The
|
|
180
|
+
* session-tracks mixin overrides this so a non-admin's edits become a
|
|
181
|
+
* shareable session-track override instead.
|
|
182
|
+
*/
|
|
183
|
+
updateTrackConfiguration(trackConf) {
|
|
184
|
+
// an opened connection track lives in connectionTrackConfigs, not
|
|
185
|
+
// jbrowse.tracks; persist its edit there (jbrowse.updateTrackConf would
|
|
186
|
+
// no-op, since the track isn't in the config). Desktop uses this base
|
|
187
|
+
// mixin, so without this branch a connection-track edit is lost on
|
|
188
|
+
// reload.
|
|
189
|
+
if (isSessionWithConnections(self) &&
|
|
190
|
+
trackConf.trackId in self.connectionTrackConfigs) {
|
|
191
|
+
self.updateConnectionTrackConfig(trackConf);
|
|
40
192
|
}
|
|
193
|
+
else {
|
|
194
|
+
self.jbrowse.updateTrackConf(trackConf);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
/**
|
|
198
|
+
* #action
|
|
199
|
+
*/
|
|
200
|
+
deleteTrackConf(trackConf) {
|
|
201
|
+
const { trackId } = trackConf;
|
|
202
|
+
self.dereferenceTrack(trackId, self.getReferring(trackId));
|
|
41
203
|
if (self.adminMode) {
|
|
42
204
|
return self.jbrowse.deleteTrackConf(trackConf);
|
|
43
205
|
}
|
|
44
206
|
},
|
|
45
207
|
}));
|
|
46
208
|
}
|
|
209
|
+
/** Type guard for SessionWithTracks */
|
|
47
210
|
export function isSessionWithTracks(thing) {
|
|
48
211
|
return isBaseSession(thing) && 'tracks' in thing;
|
|
49
212
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AssertExtends } from '../assertExtends.ts';
|
|
2
|
+
import type { AbstractSessionModel } from '@jbrowse/core/util/types';
|
|
3
|
+
/**
|
|
4
|
+
* Compile-time assertion that a concrete session-model instance type satisfies
|
|
5
|
+
* `AbstractSessionModel`. Reference it with a product's session instance type
|
|
6
|
+
* to force the check; there is no runtime component, so a missing member
|
|
7
|
+
* surfaces as a type error at the reference site:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* type _Check = AssertSessionModel<Instance<WebSessionModelType>>
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Every product's session model pairs this with one `AssertExtends` per
|
|
14
|
+
* capability contract it relies on, because this assertion alone is not enough:
|
|
15
|
+
* `AbstractSessionModel` marks those capabilities **optional**, so it cannot
|
|
16
|
+
* catch a member drifting out of sync with the `SessionWith*` interface plugins
|
|
17
|
+
* actually narrow to. Keep them as one assertion per contract rather than
|
|
18
|
+
* collapsing them into a list — the whole value is the compile error naming the
|
|
19
|
+
* contract that broke.
|
|
20
|
+
*/
|
|
21
|
+
export type AssertSessionModel<T extends AbstractSessionModel> = AssertExtends<T, AbstractSessionModel>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NotificationSink } from '@jbrowse/core/util/types';
|
|
2
|
+
import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
3
|
+
interface DeveloperModeHost extends IStateTreeNode, Pick<NotificationSink, 'notify'> {
|
|
4
|
+
getPreference: (key: string) => unknown;
|
|
5
|
+
}
|
|
6
|
+
export declare function applyDeveloperMode(self: DeveloperModeHost): void;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { addDisposer, isAlive } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
import { enableContractReports, setContractReportSink, } from '@jbrowse/render-core/contractReports';
|
|
3
|
+
/**
|
|
4
|
+
* What a plugin author sees when their display breaks an ordering contract in
|
|
5
|
+
* somebody else's production build.
|
|
6
|
+
*
|
|
7
|
+
* The checks themselves have always run and always reported; what they had no
|
|
8
|
+
* way to reach was this population, which is the one least able to diagnose the
|
|
9
|
+
* symptom and the one nobody can write a test for. A `console.error` surviving
|
|
10
|
+
* the build would not have fixed that — nobody has a console open on a page
|
|
11
|
+
* they did not write — so the report has to land in the app, in the session the
|
|
12
|
+
* broken display is in, and it has to explain itself: the reader did not turn
|
|
13
|
+
* this on deliberately in the common case (a plugin served from their own
|
|
14
|
+
* machine did), and a message they cannot place reads as the app being broken.
|
|
15
|
+
*
|
|
16
|
+
* `warning` rather than `error`: an error snackbar in this app means the user's
|
|
17
|
+
* data or their request failed, and every word here is about code they are
|
|
18
|
+
* writing. Warnings persist until dismissed, which is what a message with a fix
|
|
19
|
+
* in it wants, and identical ones collapse in the snackbar — so a violation
|
|
20
|
+
* repeating across ten tracks of one broken type is still one notice.
|
|
21
|
+
*/
|
|
22
|
+
function notification({ family, message, armedBy }) {
|
|
23
|
+
return (`JBrowse ${family} contract broken — a bug in code, not in your data or ` +
|
|
24
|
+
`configuration.\n\n${message}\n\n` +
|
|
25
|
+
`Shown because ${armedBy}. These notices can be turned on anywhere with ` +
|
|
26
|
+
`localStorage.jbrowseDeveloperMode = 1, or site-wide with ` +
|
|
27
|
+
`configuration.preferences.developerMode.`);
|
|
28
|
+
}
|
|
29
|
+
export function applyDeveloperMode(self) {
|
|
30
|
+
if (self.getPreference('developerMode') === true) {
|
|
31
|
+
enableContractReports('this site sets configuration.preferences.developerMode');
|
|
32
|
+
}
|
|
33
|
+
addDisposer(self, setContractReportSink(report => {
|
|
34
|
+
// out of whatever reaction, fetch handler or upload found the violation:
|
|
35
|
+
// `notify` writes observables, and a diagnostic that throws where it is
|
|
36
|
+
// read is worse than the bug it describes
|
|
37
|
+
queueMicrotask(() => {
|
|
38
|
+
if (isAlive(self)) {
|
|
39
|
+
self.notify(notification(report), 'warning');
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { IAnyModelType, SnapshotIn, SnapshotOut } from '@jbrowse/mobx-state-tree';
|
|
3
|
+
/**
|
|
4
|
+
* Apply the `Core-extendSession` extension point and drop the legacy
|
|
5
|
+
* object-shaped `connectionInstances` from incoming snapshots (the schema
|
|
6
|
+
* changed from an object to an array, so old snapshots must be filtered out,
|
|
7
|
+
* xref https://github.com/GMOD/jbrowse-components/issues/1903).
|
|
8
|
+
*
|
|
9
|
+
* Shared by the web session (`finalizeWebSession`) and the desktop session
|
|
10
|
+
* factory so the extension point + migration live in one place.
|
|
11
|
+
*/
|
|
12
|
+
export declare function finalizeSession<T extends IAnyModelType>(pluginManager: PluginManager, sessionModel: T): import("@jbrowse/mobx-state-tree").ISnapshotProcessor<T, Omit<SnapshotIn<T> & {
|
|
13
|
+
connectionInstances?: unknown;
|
|
14
|
+
}, "connectionInstances">, Omit<SnapshotOut<T> & {
|
|
15
|
+
connectionInstances?: unknown;
|
|
16
|
+
}, "connectionInstances">>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { types } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
/**
|
|
3
|
+
* Apply the `Core-extendSession` extension point and drop the legacy
|
|
4
|
+
* object-shaped `connectionInstances` from incoming snapshots (the schema
|
|
5
|
+
* changed from an object to an array, so old snapshots must be filtered out,
|
|
6
|
+
* xref https://github.com/GMOD/jbrowse-components/issues/1903).
|
|
7
|
+
*
|
|
8
|
+
* Shared by the web session (`finalizeWebSession`) and the desktop session
|
|
9
|
+
* factory so the extension point + migration live in one place.
|
|
10
|
+
*/
|
|
11
|
+
export function finalizeSession(pluginManager, sessionModel) {
|
|
12
|
+
const extendedSessionModel = pluginManager.evaluateExtensionPoint(
|
|
13
|
+
/** #extensionPoint Core-extendSession | sync | Extend the session model with extra state or actions */
|
|
14
|
+
'Core-extendSession', sessionModel);
|
|
15
|
+
return types.snapshotProcessor(extendedSessionModel, {
|
|
16
|
+
preProcessor(snapshot) {
|
|
17
|
+
const { connectionInstances, ...rest } = snapshot;
|
|
18
|
+
return !Array.isArray(connectionInstances) ? rest : snapshot;
|
|
19
|
+
},
|
|
20
|
+
postProcessor(snapshot) {
|
|
21
|
+
// connectionInstances are runtime-only: they hold the whole fetched hub
|
|
22
|
+
// (thousands of tracks), so persisting them bloats the session. Open
|
|
23
|
+
// connection tracks survive reload via `connectionTrackConfigs` instead,
|
|
24
|
+
// which resolves synchronously without re-establishing the connection.
|
|
25
|
+
const { connectionInstances: _connectionInstances, ...rest } = snapshot;
|
|
26
|
+
return rest;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
package/esm/Session/index.d.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export
|
|
1
|
+
export { ReferenceManagementSessionMixin, isSessionWithReferenceManagement, } from './ReferenceManagement.ts';
|
|
2
|
+
export type { ReferringNode, SessionWithReferenceManagement, SessionWithReferenceManagementType, } from './ReferenceManagement.ts';
|
|
3
|
+
export { ConnectionManagementSessionMixin, isSessionWithConnections, } from './Connections.ts';
|
|
4
|
+
export type { ConnectionTrackConfigEntry, SessionWithConnections, SessionWithConnectionsType, } from './Connections.ts';
|
|
5
|
+
export { DrawerWidgetSessionMixin, isSessionWithDrawerWidgets, } from './DrawerWidgets.ts';
|
|
6
|
+
export type { SessionWithDrawerWidgets, SessionWithDrawerWidgetsType, } from './DrawerWidgets.ts';
|
|
7
|
+
export { ThemeManagerSessionMixin, isSessionWithThemes } from './Themes.ts';
|
|
8
|
+
export type { SessionWithThemes, SessionWithThemesType } from './Themes.ts';
|
|
9
|
+
export { TracksManagerSessionMixin, isSessionWithTracks } from './Tracks.ts';
|
|
10
|
+
export type { SessionWithTracks, SessionWithTracksType } from './Tracks.ts';
|
|
11
|
+
export { MultipleViewsSessionMixin, isSessionWithMultipleViews, } from './MultipleViews.ts';
|
|
12
|
+
export type { SessionWithMultipleViews, SessionWithMultipleViewsType, } from './MultipleViews.ts';
|
|
13
|
+
export { PreferencesSessionMixin } from './Preferences.ts';
|
|
14
|
+
export { BaseSessionModel, isBaseSession, isSession } from './BaseSession.ts';
|
|
15
|
+
export type { BaseSession, BaseSessionType } from './BaseSession.ts';
|
|
16
|
+
export { SessionTracksManagerSessionMixin } from './SessionTracks.ts';
|
|
17
|
+
export type { EditableTrackConfig, PlainTrackConfig, SessionWithSessionTracks, SessionWithSessionTracksType, } from './SessionTracks.ts';
|
|
18
|
+
export { bakePromotedDefaultsIntoSnapshot, bakeSessionCascades, getShareableSessionSnapshot, } from './shareableSnapshot.ts';
|
|
19
|
+
export { TrackMenuItemsSessionMixin, aboutTrackMenuItem, copyTrackSnapshot, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './TrackMenu.ts';
|
|
20
|
+
export { TrackMenuSessionMixin } from './TrackMenuSessionMixin.ts';
|
|
21
|
+
export { finalizeSession } from './finalizeSession.ts';
|
|
22
|
+
export type { AssertSessionModel } from './assertSessionModel.ts';
|
package/esm/Session/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export
|
|
1
|
+
export { ReferenceManagementSessionMixin, isSessionWithReferenceManagement, } from './ReferenceManagement.js';
|
|
2
|
+
export { ConnectionManagementSessionMixin, isSessionWithConnections, } from './Connections.js';
|
|
3
|
+
export { DrawerWidgetSessionMixin, isSessionWithDrawerWidgets, } from './DrawerWidgets.js';
|
|
4
|
+
export { ThemeManagerSessionMixin, isSessionWithThemes } from './Themes.js';
|
|
5
|
+
export { TracksManagerSessionMixin, isSessionWithTracks } from './Tracks.js';
|
|
6
|
+
export { MultipleViewsSessionMixin, isSessionWithMultipleViews, } from './MultipleViews.js';
|
|
7
|
+
export { PreferencesSessionMixin } from './Preferences.js';
|
|
8
|
+
export { BaseSessionModel, isBaseSession, isSession } from './BaseSession.js';
|
|
9
|
+
export { SessionTracksManagerSessionMixin } from './SessionTracks.js';
|
|
10
|
+
export { bakePromotedDefaultsIntoSnapshot, bakeSessionCascades, getShareableSessionSnapshot, } from './shareableSnapshot.js';
|
|
11
|
+
export { TrackMenuItemsSessionMixin, aboutTrackMenuItem, copyTrackSnapshot, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './TrackMenu.js';
|
|
12
|
+
export { TrackMenuSessionMixin } from './TrackMenuSessionMixin.js';
|
|
13
|
+
export { finalizeSession } from './finalizeSession.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
3
|
+
type TrackConfInput = {
|
|
4
|
+
trackId: string;
|
|
5
|
+
type: string;
|
|
6
|
+
} & Record<string, unknown>;
|
|
7
|
+
export declare function assertNotReaddedDifferently(pluginManager: PluginManager, existing: AnyConfigurationModel | TrackConfInput, trackConf: TrackConfInput): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getSnapshot, isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
import { compareStructural } from 'mobx';
|
|
3
|
+
// What a config means once its defaults are resolved: a live config node
|
|
4
|
+
// answers with its snapshot, and a plain object (desktop's `jbrowse.tracks` is
|
|
5
|
+
// frozen, so its entries are the input as written) is hydrated through the
|
|
6
|
+
// same schema first. Two inputs that would build the same node compare equal.
|
|
7
|
+
function resolvedTrackConf(pluginManager, conf) {
|
|
8
|
+
return isStateTreeNode(conf)
|
|
9
|
+
? getSnapshot(conf)
|
|
10
|
+
: getSnapshot(pluginManager
|
|
11
|
+
.pluggableConfigSchemaType('track')
|
|
12
|
+
.create(conf, { pluginManager }));
|
|
13
|
+
}
|
|
14
|
+
// Re-adding a trackId the session already holds returns the existing config,
|
|
15
|
+
// which is what keeps a content-hashed trackId idempotent. It is also the trap
|
|
16
|
+
// a recomputed track walks into: the new features arrive under the old id, the
|
|
17
|
+
// old config is handed back, nothing is said, and the track keeps showing the
|
|
18
|
+
// first values it saw. Same id with different content is therefore refused,
|
|
19
|
+
// naming the two-step that works.
|
|
20
|
+
export function assertNotReaddedDifferently(pluginManager, existing, trackConf) {
|
|
21
|
+
const same = compareStructural(resolvedTrackConf(pluginManager, existing), resolvedTrackConf(pluginManager, trackConf));
|
|
22
|
+
if (!same) {
|
|
23
|
+
throw new Error(`Track "${trackConf.trackId}" is already in this session with a different configuration, and adding it again would keep the old one. Delete it first (session.deleteTrackConf(session.getTrackById("${trackConf.trackId}"))), then add — and give a recomputed FromConfigAdapter a new adapterId, since the adapter cache is keyed on it.`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { AbstractSessionModel } from '@jbrowse/core/util';
|
|
2
|
+
/**
|
|
3
|
+
* The session snapshot to hand to anyone else — a share link, an exported
|
|
4
|
+
* `session.json`, a desktop→web export. Snapshotting and baking are one call so
|
|
5
|
+
* the pair can't be split: a bare `getSnapshot(session)` is never a correct
|
|
6
|
+
* outgoing snapshot (see `bakeSessionCascades`), and three of the four
|
|
7
|
+
* boundaries were spelling the two steps out identically.
|
|
8
|
+
*
|
|
9
|
+
* The fourth (desktop `ExportToWebDialog`) bakes a *transformed* snapshot from
|
|
10
|
+
* `planWebExport`, so it calls `bakeSessionCascades` directly.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getShareableSessionSnapshot(session: AbstractSessionModel): Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* Everything an outgoing snapshot has to resolve because the live session
|
|
15
|
+
* resolves it at read time against state that is staying behind.
|
|
16
|
+
*
|
|
17
|
+
* Two such cascades, and they have nothing to do with each other — promoted
|
|
18
|
+
* display-type defaults are per-track rendering, workspaces intent is the
|
|
19
|
+
* session's layout mode. What they share is only this: both live partly in the
|
|
20
|
+
* sender's own browser, so a raw `getSnapshot` records neither, and the
|
|
21
|
+
* recipient silently resolves their own.
|
|
22
|
+
*
|
|
23
|
+
* They get one entry point rather than a call each because the failure of a
|
|
24
|
+
* boundary that performs half of them is invisible at that boundary — the
|
|
25
|
+
* export succeeds, and what is missing only shows up on someone else's screen.
|
|
26
|
+
* Adding a third cascade should mean editing this function, not auditing four
|
|
27
|
+
* call sites.
|
|
28
|
+
*/
|
|
29
|
+
export declare function bakeSessionCascades(session: AbstractSessionModel, snapshot: Record<string, unknown>): Record<string, unknown>;
|
|
30
|
+
/**
|
|
31
|
+
* `getComputedStyle` for the promotable-default cascade.
|
|
32
|
+
*
|
|
33
|
+
* A track that "follows" a session-wide promoted display-type default holds no
|
|
34
|
+
* value of its own — its rendered value is resolved at read time from the
|
|
35
|
+
* promoted display-type defaults in `preferencesOverrides`, which are personal,
|
|
36
|
+
* localStorage-backed, and deliberately never serialized into a shared session.
|
|
37
|
+
* So a raw `getSnapshot(session)` records a track as at-default even when the
|
|
38
|
+
* sender is looking at a promoted value, and the recipient (who lacks the sender's
|
|
39
|
+
* preferences) sees something different. This flattens the live cascade into a
|
|
40
|
+
* self-contained snapshot:
|
|
41
|
+
*
|
|
42
|
+
* Every slot an open display *inherits* from a promoted default (i.e.
|
|
43
|
+
* `getDisplayTypeDefaultChanges` — non-customized, differs from base) is baked
|
|
44
|
+
* into that track's config layer: its own config for a user-added or connection
|
|
45
|
+
* track, else a `trackConfigDeltas` entry against the admin base (see
|
|
46
|
+
* `ownTrackConfig`). So the concrete value travels with the document, and
|
|
47
|
+
* because it lands in the track's config the recipient reads it as *customized*
|
|
48
|
+
* — a customized value is the top of the cascade, so their own promoted defaults
|
|
49
|
+
* never get consulted for it.
|
|
50
|
+
*
|
|
51
|
+
* **What this deliberately does not cover**, and the reason there is no flag
|
|
52
|
+
* here: a slot the sender was viewing at its *base* value. Nothing is baked for
|
|
53
|
+
* it (the value equals base, and `stripDefault` drops it from the snapshot
|
|
54
|
+
* regardless), so a recipient who has promoted something else resolves it from
|
|
55
|
+
* their own cascade and sees their value. There is no value that can express "I
|
|
56
|
+
* deliberately saw the default" — at-base and unset are byte-identical once
|
|
57
|
+
* stripped. That case used to be covered by stamping a per-display
|
|
58
|
+
* `ignorePromotedDefaults` on every open display, which cost a second
|
|
59
|
+
* shape-aware walk that had to stay in step with `openPromotableDisplays` by
|
|
60
|
+
* hand, and permanently detached received tracks from the recipient's own pins
|
|
61
|
+
* until they clicked "use this default" on each one. A promoted default is
|
|
62
|
+
* personal and local — the same as the theme a session is viewed in, which this
|
|
63
|
+
* function has never baked either.
|
|
64
|
+
*
|
|
65
|
+
* The live session is untouched; a modified deep copy of `snapshot` is returned.
|
|
66
|
+
* Tracks the sender never opened carry no display state to resolve, so they're
|
|
67
|
+
* left to pick up the recipient's defaults when opened — matching "export the
|
|
68
|
+
* actual state of the (open) tracks".
|
|
69
|
+
*
|
|
70
|
+
* Reach is `openPromotableDisplays` — literally the same walk the cascade's own
|
|
71
|
+
* "apply to open tracks" uses, so the two can't drift. It recurses into a
|
|
72
|
+
* composite view's `views` array (breakpoint-split, the linear-comparative /
|
|
73
|
+
* synteny family), which holds child views rather than tracks of its own —
|
|
74
|
+
* `LGVSyntenyDisplay` is only reachable that way, and before the recursion a
|
|
75
|
+
* shared session containing one rendered differently for the recipient.
|
|
76
|
+
*/
|
|
77
|
+
export declare function bakePromotedDefaultsIntoSnapshot(session: AbstractSessionModel, snapshot: Record<string, unknown>): Record<string, unknown>;
|