@jbrowse/product-core 4.3.0 → 5.0.0-beta.10
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 +514 -323
- package/esm/RootModel/BaseRootModel.js +141 -14
- package/esm/RootModel/HierarchicalConfig.d.ts +30 -20
- 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 +37 -0
- package/esm/RootModel/PreferencesConfig.js +75 -0
- package/esm/RootModel/createConfigModel.d.ts +257 -0
- package/esm/RootModel/createConfigModel.js +77 -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 +141 -0
- package/esm/Session/BaseSession.d.ts +285 -11
- package/esm/Session/BaseSession.js +383 -15
- package/esm/Session/Connections.d.ts +115 -8
- package/esm/Session/Connections.js +196 -74
- package/esm/Session/DrawerWidgets.d.ts +145 -13
- package/esm/Session/DrawerWidgets.js +219 -49
- package/esm/Session/MultipleViews.d.ts +1067 -1177
- package/esm/Session/MultipleViews.js +395 -64
- package/esm/Session/Preferences.d.ts +1389 -0
- package/esm/Session/Preferences.js +58 -0
- package/esm/Session/ReferenceManagement.d.ts +28 -3
- package/esm/Session/ReferenceManagement.js +56 -41
- package/esm/Session/SessionTracks.d.ts +1101 -1180
- package/esm/Session/SessionTracks.js +584 -34
- package/esm/Session/Themes.d.ts +136 -2
- package/esm/Session/Themes.js +267 -21
- package/esm/Session/TrackMenu.d.ts +88 -0
- package/esm/Session/TrackMenu.js +175 -0
- package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
- package/esm/Session/TrackMenuSessionMixin.js +42 -0
- package/esm/Session/Tracks.d.ts +945 -1159
- package/esm/Session/Tracks.js +229 -30
- package/esm/Session/assertSessionModel.d.ts +21 -0
- package/esm/Session/assertSessionModel.js +1 -0
- package/esm/Session/copyTrackSnapshot.d.ts +21 -0
- package/esm/Session/copyTrackSnapshot.js +20 -0
- package/esm/Session/developerMode.d.ts +7 -0
- package/esm/Session/developerMode.js +43 -0
- package/esm/Session/finalizeSession.d.ts +15 -0
- package/esm/Session/finalizeSession.js +28 -0
- package/esm/Session/hydratedForms.d.ts +16 -0
- package/esm/Session/hydratedForms.js +13 -0
- package/esm/Session/index.d.ts +27 -10
- package/esm/Session/index.js +16 -10
- package/esm/Session/pluginExtraTrackItems.d.ts +11 -0
- package/esm/Session/pluginExtraTrackItems.js +13 -0
- package/esm/Session/readdedTrackConf.d.ts +8 -0
- package/esm/Session/readdedTrackConf.js +24 -0
- package/esm/Session/shareableSnapshot.d.ts +23 -0
- package/esm/Session/shareableSnapshot.js +66 -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 +76 -0
- package/esm/controllerTracks.js +125 -0
- package/esm/destroyViewState.d.ts +21 -0
- package/esm/destroyViewState.js +29 -0
- package/esm/embeddedController.d.ts +57 -0
- package/esm/embeddedController.js +100 -0
- package/esm/index.d.ts +32 -4
- package/esm/index.js +31 -4
- package/esm/loadPlugins.d.ts +38 -0
- package/esm/loadPlugins.js +45 -0
- package/esm/localFiles.d.ts +74 -0
- package/esm/localFiles.js +156 -0
- package/esm/observeSession.d.ts +55 -0
- package/esm/observeSession.js +90 -0
- package/esm/parallelChunkLoading.d.ts +30 -0
- package/esm/parallelChunkLoading.js +71 -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 +562 -0
- package/esm/resolveAssemblies.d.ts +68 -0
- package/esm/resolveAssemblies.js +77 -0
- package/esm/rpcWorker.d.ts +36 -4
- package/esm/rpcWorker.js +126 -20
- package/esm/sessionMigrations/index.d.ts +15 -0
- package/esm/sessionMigrations/index.js +178 -0
- package/esm/sessionUrl.d.ts +40 -0
- package/esm/sessionUrl.js +66 -0
- package/esm/sessionUtils.d.ts +74 -0
- package/esm/sessionUtils.js +599 -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 +8 -3
- package/esm/ui/AboutDialog.js +14 -5
- package/esm/ui/AboutDialogContents.d.ts +2 -6
- package/esm/ui/AboutDialogContents.js +13 -17
- package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
- package/esm/ui/AssemblyInfoPanel.js +63 -0
- package/esm/ui/DescriptionPanel.d.ts +3 -0
- package/esm/ui/DescriptionPanel.js +25 -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 +36 -26
- package/esm/ui/GeneralPreferencesTab.d.ts +17 -0
- package/esm/ui/GeneralPreferencesTab.js +34 -0
- package/esm/ui/HeaderButtons.d.ts +2 -1
- package/esm/ui/HeaderButtons.js +15 -17
- package/esm/ui/ModalWidget.d.ts +13 -0
- package/esm/ui/ModalWidget.js +48 -0
- package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
- package/esm/ui/ModalWidgetAppBar.js +9 -0
- package/esm/ui/PreferenceCheckbox.d.ts +6 -0
- package/esm/ui/PreferenceCheckbox.js +16 -0
- package/esm/ui/PreferencesDialog.d.ts +34 -0
- package/esm/ui/PreferencesDialog.js +55 -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 +45 -63
- package/esm/ui/ViewPreferencesTab.d.ts +12 -0
- package/esm/ui/ViewPreferencesTab.js +15 -0
- 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/aboutTestUtils.d.ts +140 -0
- package/esm/ui/aboutTestUtils.js +25 -0
- package/esm/ui/descriptionHtml.d.ts +25 -0
- package/esm/ui/descriptionHtml.js +64 -0
- package/esm/ui/drawerLayout.d.ts +18 -0
- package/esm/ui/drawerLayout.js +22 -0
- package/esm/ui/index.d.ts +15 -1
- package/esm/ui/index.js +13 -1
- package/esm/ui/preferencesReset.d.ts +18 -0
- package/esm/ui/preferencesReset.js +69 -0
- package/esm/ui/util.d.ts +22 -7
- package/esm/ui/util.js +19 -41
- package/esm/useEngineLifecycle.d.ts +19 -0
- package/esm/useEngineLifecycle.js +44 -0
- package/package.json +15 -18
- 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,248 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { expandLooseTrackConfig } from '@jbrowse/core/util/tracks';
|
|
2
|
+
import { addDisposer, types } from '@jbrowse/mobx-state-tree';
|
|
3
|
+
import { autorun, 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
|
+
* Each track's base by trackId: the entry its edits diff against. Here
|
|
19
|
+
* the config.json entry, which an edit writes directly.
|
|
20
|
+
*/
|
|
21
|
+
get trackBasesById() {
|
|
22
|
+
const tracks = self.jbrowse.tracks;
|
|
23
|
+
return new Map(tracks.map(t => [t.trackId, t]));
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* #method
|
|
27
|
+
* A track's base as the session resolves it, its edits applied. Here the
|
|
28
|
+
* base itself.
|
|
29
|
+
*/
|
|
30
|
+
withTrackEdits(base) {
|
|
31
|
+
return base;
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* #getter
|
|
35
|
+
*/
|
|
8
36
|
get tracks() {
|
|
9
37
|
return self.jbrowse.tracks;
|
|
10
38
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
39
|
+
}))
|
|
40
|
+
.extend(self => {
|
|
41
|
+
// Assembly sequences and connection tracks by trackId, which win over a
|
|
42
|
+
// track base of the same id.
|
|
43
|
+
const otherConfigsById = computed(() => {
|
|
15
44
|
const temporaryAssemblies = 'temporaryAssemblies' in self
|
|
16
45
|
? self.temporaryAssemblies
|
|
17
46
|
: [];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
47
|
+
// both come from the connections mixin, which not every session
|
|
48
|
+
// composes. connectionInstances is annotated because its element type
|
|
49
|
+
// is a runtime-pluggable stateModel, so the guard can't infer it.
|
|
50
|
+
const connectionInstances = isSessionWithConnections(self) ? self.connectionInstances : [];
|
|
51
|
+
const connectionTrackConfigs = isSessionWithConnections(self)
|
|
52
|
+
? self.connectionTrackConfigs
|
|
53
|
+
: {};
|
|
21
54
|
return Object.fromEntries([
|
|
22
|
-
...
|
|
23
|
-
...this.assemblies.map(a => [a.sequence.trackId, a.sequence]),
|
|
55
|
+
...self.assemblies.map(a => [a.sequence.trackId, a.sequence]),
|
|
24
56
|
...temporaryAssemblies.map(a => [a.sequence.trackId, a.sequence]),
|
|
25
57
|
...connectionInstances.flatMap(c => c.tracks.map(t => [t.trackId, t])),
|
|
58
|
+
// Persisted configs of opened connection tracks. Placed last so they
|
|
59
|
+
// win over the live connection instance: identity-stable across
|
|
60
|
+
// reload, and resolves even when the connection isn't re-established.
|
|
61
|
+
...Object.entries(connectionTrackConfigs).map(([trackId, e]) => [
|
|
62
|
+
trackId,
|
|
63
|
+
e.config,
|
|
64
|
+
]),
|
|
26
65
|
]);
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
66
|
+
}, { name: 'otherConfigsById' });
|
|
67
|
+
// Kept against the two lists it was built from rather than held, since
|
|
68
|
+
// holding it would rebuild it on every edit
|
|
69
|
+
let tracksById;
|
|
70
|
+
// Per-id computeds backing getTrackById, so an id's readers wake only
|
|
71
|
+
// when it resolves to another config. Not evicted: bounded by the
|
|
72
|
+
// distinct ids resolved this session.
|
|
73
|
+
const trackByIdComputeds = new Map();
|
|
74
|
+
return {
|
|
75
|
+
views: {
|
|
76
|
+
/**
|
|
77
|
+
* #method
|
|
78
|
+
* Config for one trackId — a track, assembly sequence, or connection
|
|
79
|
+
* track — or undefined. Per-id reactive: every display resolves its
|
|
80
|
+
* config through this (via TrackConfigurationReference) and
|
|
81
|
+
* subscribes only to its own id, so one track's settings edit doesn't
|
|
82
|
+
* re-render the others.
|
|
83
|
+
*/
|
|
84
|
+
getTrackById(id) {
|
|
85
|
+
let c = trackByIdComputeds.get(id);
|
|
86
|
+
if (!c) {
|
|
87
|
+
c = computed(() => {
|
|
88
|
+
const other = otherConfigsById.get()[id];
|
|
89
|
+
if (other) {
|
|
90
|
+
return other;
|
|
91
|
+
}
|
|
92
|
+
const base = self.trackBasesById.get(id);
|
|
93
|
+
return base && self.withTrackEdits(base);
|
|
94
|
+
});
|
|
95
|
+
trackByIdComputeds.set(id, c);
|
|
96
|
+
}
|
|
97
|
+
return c.get();
|
|
98
|
+
},
|
|
99
|
+
/**
|
|
100
|
+
* #method
|
|
101
|
+
* Every track config the session can resolve, keyed by trackId.
|
|
102
|
+
* Prefer the per-id reactive `getTrackById(id)`: this map is rebuilt
|
|
103
|
+
* over every track on the first read after any edit, and reading it
|
|
104
|
+
* subscribes the caller to all of them. Kept for plugins that look
|
|
105
|
+
* up ids in a non-reactive context.
|
|
106
|
+
*
|
|
107
|
+
* @deprecated
|
|
108
|
+
*/
|
|
109
|
+
getTracksById() {
|
|
110
|
+
const { tracks } = self;
|
|
111
|
+
const others = otherConfigsById.get();
|
|
112
|
+
if (tracksById?.tracks !== tracks || tracksById.others !== others) {
|
|
113
|
+
tracksById = {
|
|
114
|
+
tracks,
|
|
115
|
+
others,
|
|
116
|
+
record: Object.fromEntries([
|
|
117
|
+
...tracks.map(t => [t.trackId, t]),
|
|
118
|
+
...Object.entries(others),
|
|
119
|
+
]),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return tracksById.record;
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
actions: {
|
|
126
|
+
// Holds the indexes getTrackById reads and the track list, so a
|
|
127
|
+
// reader outside any reaction (ranking search hits: 33ms per search
|
|
128
|
+
// unheld on a 2000-track config, 0.07ms held; the search box's
|
|
129
|
+
// adapter list) gets the cache too. Not `keepAlive`: that
|
|
130
|
+
// subscription never ends, and it reaches jbrowse.tracks on the
|
|
131
|
+
// root, so it pinned every superseded session for the tab's life.
|
|
132
|
+
afterAttach() {
|
|
133
|
+
addDisposer(self, autorun(() => {
|
|
134
|
+
void self.tracks;
|
|
135
|
+
void self.trackBasesById;
|
|
136
|
+
otherConfigsById.get();
|
|
137
|
+
}, { name: 'trackIndex' }));
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
})
|
|
142
|
+
.actions(self => {
|
|
143
|
+
function addToSession(trackConf) {
|
|
144
|
+
assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
|
|
145
|
+
const expanded = expandLooseTrackConfig(trackConf, pluginManager);
|
|
146
|
+
const { trackId, type } = expanded;
|
|
147
|
+
const existing = self.getTrackById(trackId);
|
|
148
|
+
if (existing) {
|
|
149
|
+
assertNotReaddedDifferently(pluginManager, existing, {
|
|
150
|
+
...expanded,
|
|
151
|
+
trackId,
|
|
152
|
+
type,
|
|
153
|
+
});
|
|
154
|
+
return existing;
|
|
40
155
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
156
|
+
return self.jbrowse.addTrackConf(trackConf);
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
/**
|
|
160
|
+
* #action
|
|
161
|
+
* Add a track config to *this session*.
|
|
162
|
+
*
|
|
163
|
+
* This mixin's session has no separate session-track store, so the
|
|
164
|
+
* destination is the jbrowse config — which in the products that compose
|
|
165
|
+
* it (desktop) is the single user's own file rather than something a
|
|
166
|
+
* server hands other visitors, so the two scopes are the same place.
|
|
167
|
+
* Defined here anyway so that every session has it: a feature standing a
|
|
168
|
+
* track up on the user's behalf can then call one action everywhere
|
|
169
|
+
* instead of asking which mixin it landed on.
|
|
170
|
+
* `SessionTracksManagerSessionMixin` overrides it with the real
|
|
171
|
+
* session-scoped store.
|
|
172
|
+
*/
|
|
173
|
+
addSessionTrackConf(trackConf) {
|
|
174
|
+
return addToSession(trackConf);
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* #action
|
|
178
|
+
* Add a track config wherever *this user's* catalog edits belong — the
|
|
179
|
+
* "Add track" workflows, where an admin adding a track means to add it
|
|
180
|
+
* for the whole site. `SessionTracksManagerSessionMixin` overrides it to
|
|
181
|
+
* send a non-admin's to the session instead; here there is only the one
|
|
182
|
+
* destination.
|
|
183
|
+
*
|
|
184
|
+
* Anything that is not an Add-track workflow wants
|
|
185
|
+
* `addSessionTrackConf`: a track a feature stands up on the user's behalf
|
|
186
|
+
* — a search result, a computed consensus, a reconstruction's labels — is
|
|
187
|
+
* not a catalog entry, and publishing one writes it into the config.json
|
|
188
|
+
* every visitor is served, once per click.
|
|
189
|
+
*/
|
|
190
|
+
publishTrackConf(trackConf) {
|
|
191
|
+
assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
|
|
192
|
+
return self.jbrowse.addTrackConf(trackConf);
|
|
193
|
+
},
|
|
194
|
+
/**
|
|
195
|
+
* #action
|
|
196
|
+
* Deprecated alias of `addSessionTrackConf`. Call that, or
|
|
197
|
+
* `publishTrackConf`, which say which destination they mean.
|
|
198
|
+
*
|
|
199
|
+
* @deprecated
|
|
200
|
+
*
|
|
201
|
+
* Kept because a prebuilt plugin bundle reaches this by name at runtime
|
|
202
|
+
* and cannot be recompiled — `jbrowse-plugin-protein3d` calls it, and a
|
|
203
|
+
* member lookup that stops resolving throws nothing at all
|
|
204
|
+
* (`pluginFacingSessionApi.test.ts` is the guard). It now means the
|
|
205
|
+
* session, which is the destination every such caller wanted: a track a
|
|
206
|
+
* plugin stands up for one user is not a catalog entry. Nothing in tree
|
|
207
|
+
* may call it — `no-restricted-syntax` says so.
|
|
208
|
+
*/
|
|
209
|
+
addTrackConf(trackConf) {
|
|
210
|
+
return addToSession(trackConf);
|
|
211
|
+
},
|
|
212
|
+
/**
|
|
213
|
+
* #action
|
|
214
|
+
* Persist edited track config back to the in-memory jbrowse config. The
|
|
215
|
+
* session-tracks mixin overrides this so a non-admin's edits become a
|
|
216
|
+
* shareable session-track override instead.
|
|
217
|
+
*/
|
|
218
|
+
updateTrackConfiguration(trackConf) {
|
|
219
|
+
// an opened connection track lives in connectionTrackConfigs, not
|
|
220
|
+
// jbrowse.tracks; persist its edit there (jbrowse.updateTrackConf would
|
|
221
|
+
// no-op, since the track isn't in the config). Desktop uses this base
|
|
222
|
+
// mixin, so without this branch a connection-track edit is lost on
|
|
223
|
+
// reload.
|
|
224
|
+
if (isSessionWithConnections(self) &&
|
|
225
|
+
trackConf.trackId in self.connectionTrackConfigs) {
|
|
226
|
+
self.updateConnectionTrackConfig(trackConf);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
self.jbrowse.updateTrackConf(trackConf);
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
/**
|
|
233
|
+
* #action
|
|
234
|
+
*/
|
|
235
|
+
deleteTrackConf(trackConf) {
|
|
236
|
+
const { trackId } = trackConf;
|
|
237
|
+
self.dereferenceTrack(trackId, self.getReferring(trackId));
|
|
238
|
+
if (self.adminMode) {
|
|
239
|
+
return self.jbrowse.deleteTrackConf(trackConf);
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
});
|
|
46
244
|
}
|
|
245
|
+
/** Type guard for SessionWithTracks */
|
|
47
246
|
export function isSessionWithTracks(thing) {
|
|
48
247
|
return isBaseSession(thing) && 'tracks' in thing;
|
|
49
248
|
}
|
|
@@ -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,21 @@
|
|
|
1
|
+
import type { BaseTrackConfig } from '@jbrowse/core/pluggableElementTypes';
|
|
2
|
+
type TrackCopySnapshot = {
|
|
3
|
+
trackId: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
category?: unknown;
|
|
6
|
+
displays?: {
|
|
7
|
+
type: string;
|
|
8
|
+
displayId: string;
|
|
9
|
+
}[];
|
|
10
|
+
} & Record<string, unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* Clone a track config for "Copy track": snapshots the config, appends a unique
|
|
13
|
+
* timestamp suffix to trackId (so it doesn't collide with the original), tags
|
|
14
|
+
* " (copy)" on the name, and regenerates each displayId to the canonical
|
|
15
|
+
* `${trackId}-${type}` form baseTrackConfig auto-injects — keeping them unique
|
|
16
|
+
* (displayId is a types.identifier, so a collision would crash MST).
|
|
17
|
+
*/
|
|
18
|
+
export declare function copyTrackSnapshot(config: BaseTrackConfig, opts: {
|
|
19
|
+
clearCategory: boolean;
|
|
20
|
+
}): TrackCopySnapshot;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getSnapshot, isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
/**
|
|
3
|
+
* Clone a track config for "Copy track": snapshots the config, appends a unique
|
|
4
|
+
* timestamp suffix to trackId (so it doesn't collide with the original), tags
|
|
5
|
+
* " (copy)" on the name, and regenerates each displayId to the canonical
|
|
6
|
+
* `${trackId}-${type}` form baseTrackConfig auto-injects — keeping them unique
|
|
7
|
+
* (displayId is a types.identifier, so a collision would crash MST).
|
|
8
|
+
*/
|
|
9
|
+
export function copyTrackSnapshot(config, opts) {
|
|
10
|
+
const snap = structuredClone(isStateTreeNode(config) ? getSnapshot(config) : config);
|
|
11
|
+
snap.name = `${snap.name || snap.trackId} (copy)`;
|
|
12
|
+
snap.trackId += `-${Date.now()}`;
|
|
13
|
+
if (opts.clearCategory) {
|
|
14
|
+
snap.category = undefined;
|
|
15
|
+
}
|
|
16
|
+
for (const d of snap.displays ?? []) {
|
|
17
|
+
d.displayId = `${snap.trackId}-${d.type}`;
|
|
18
|
+
}
|
|
19
|
+
return snap;
|
|
20
|
+
}
|
|
@@ -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,15 @@
|
|
|
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 and desktop session factories so the extension point + migration live in one place.
|
|
10
|
+
*/
|
|
11
|
+
export declare function finalizeSession<T extends IAnyModelType>(pluginManager: PluginManager, sessionModel: T): import("@jbrowse/mobx-state-tree").ISnapshotProcessor<T, Omit<SnapshotIn<T> & {
|
|
12
|
+
connectionInstances?: unknown;
|
|
13
|
+
}, "connectionInstances">, Omit<SnapshotOut<T> & {
|
|
14
|
+
connectionInstances?: unknown;
|
|
15
|
+
}, "connectionInstances">>;
|
|
@@ -0,0 +1,28 @@
|
|
|
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 and desktop session factories so the extension point + migration live in one place.
|
|
9
|
+
*/
|
|
10
|
+
export function finalizeSession(pluginManager, sessionModel) {
|
|
11
|
+
const extendedSessionModel = pluginManager.evaluateExtensionPoint(
|
|
12
|
+
/** #extensionPoint Core-extendSession | sync | Extend the session model with extra state or actions */
|
|
13
|
+
'Core-extendSession', sessionModel);
|
|
14
|
+
return types.snapshotProcessor(extendedSessionModel, {
|
|
15
|
+
preProcessor(snapshot) {
|
|
16
|
+
const { connectionInstances, ...rest } = snapshot;
|
|
17
|
+
return !Array.isArray(connectionInstances) ? rest : snapshot;
|
|
18
|
+
},
|
|
19
|
+
postProcessor(snapshot) {
|
|
20
|
+
// connectionInstances are runtime-only: they hold the whole fetched hub
|
|
21
|
+
// (thousands of tracks), so persisting them bloats the session. Open
|
|
22
|
+
// connection tracks survive reload via `connectionTrackConfigs` instead,
|
|
23
|
+
// which resolves synchronously without re-establishing the connection.
|
|
24
|
+
const { connectionInstances: _connectionInstances, ...rest } = snapshot;
|
|
25
|
+
return rest;
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AssemblySnapshot, TrackSnapshot } from '../sessionUtils.ts';
|
|
2
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
3
|
+
import type { IAnyType } from '@jbrowse/mobx-state-tree';
|
|
4
|
+
/**
|
|
5
|
+
* A config as its own schema writes it back: shorthand expanded, a stub per
|
|
6
|
+
* compatible display injected, every slot at its default left out. A
|
|
7
|
+
* track-config delta is only exact between two configs in this form, because a
|
|
8
|
+
* member one has and the other lacks then means a reset rather than two
|
|
9
|
+
* spellings of one config.
|
|
10
|
+
*/
|
|
11
|
+
export interface HydratedForms {
|
|
12
|
+
track: (conf: TrackSnapshot) => TrackSnapshot;
|
|
13
|
+
assembly: (conf: AssemblySnapshot) => AssemblySnapshot;
|
|
14
|
+
}
|
|
15
|
+
export declare function hydratedTrackForm(pluginManager: PluginManager): (conf: TrackSnapshot) => TrackSnapshot;
|
|
16
|
+
export declare function hydratedForms(pluginManager: PluginManager, assemblyConfigSchema: IAnyType): HydratedForms;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getSnapshot } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
function snapshotThrough(schema, pluginManager) {
|
|
3
|
+
return (conf) => getSnapshot(schema.create(conf, { pluginManager }));
|
|
4
|
+
}
|
|
5
|
+
export function hydratedTrackForm(pluginManager) {
|
|
6
|
+
return snapshotThrough(pluginManager.pluggableConfigSchemaType('track'), pluginManager);
|
|
7
|
+
}
|
|
8
|
+
export function hydratedForms(pluginManager, assemblyConfigSchema) {
|
|
9
|
+
return {
|
|
10
|
+
track: hydratedTrackForm(pluginManager),
|
|
11
|
+
assembly: snapshotThrough(assemblyConfigSchema, pluginManager),
|
|
12
|
+
};
|
|
13
|
+
}
|
package/esm/Session/index.d.ts
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
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, storedThemeArgs, } from './Themes.ts';
|
|
8
|
+
export type { ThemeModeSelection } from './Themes.ts';
|
|
9
|
+
export type { SessionWithThemes, SessionWithThemesType } from './Themes.ts';
|
|
10
|
+
export { TracksManagerSessionMixin, isSessionWithTracks } from './Tracks.ts';
|
|
11
|
+
export type { SessionWithTracks, SessionWithTracksType } from './Tracks.ts';
|
|
12
|
+
export { MultipleViewsSessionMixin, isSessionWithMultipleViews, } from './MultipleViews.ts';
|
|
13
|
+
export type { SessionWithMultipleViews, SessionWithMultipleViewsType, } from './MultipleViews.ts';
|
|
14
|
+
export { PreferencesSessionMixin } from './Preferences.ts';
|
|
15
|
+
export { BaseSessionModel, isBaseSession, isSession } from './BaseSession.ts';
|
|
16
|
+
export type { BaseSession, BaseSessionType } from './BaseSession.ts';
|
|
17
|
+
export { SessionTracksManagerSessionMixin } from './SessionTracks.ts';
|
|
18
|
+
export type { EditableTrackConfig, PlainTrackConfig, SessionWithSessionTracks, SessionWithSessionTracksType, } from './SessionTracks.ts';
|
|
19
|
+
export { hydratedForms } from './hydratedForms.ts';
|
|
20
|
+
export type { HydratedForms } from './hydratedForms.ts';
|
|
21
|
+
export { bakeSessionCascades, getShareableSessionSnapshot, } from './shareableSnapshot.ts';
|
|
22
|
+
export { TrackMenuItemsSessionMixin, aboutTrackMenuItem, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './TrackMenu.ts';
|
|
23
|
+
export { copyTrackSnapshot } from './copyTrackSnapshot.ts';
|
|
24
|
+
export { pluginExtraTrackItems } from './pluginExtraTrackItems.ts';
|
|
25
|
+
export { TrackMenuSessionMixin } from './TrackMenuSessionMixin.ts';
|
|
26
|
+
export { finalizeSession } from './finalizeSession.ts';
|
|
27
|
+
export type { AssertSessionModel } from './assertSessionModel.ts';
|
package/esm/Session/index.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
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, storedThemeArgs, } 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 { hydratedForms } from './hydratedForms.js';
|
|
11
|
+
export { bakeSessionCascades, getShareableSessionSnapshot, } from './shareableSnapshot.js';
|
|
12
|
+
export { TrackMenuItemsSessionMixin, aboutTrackMenuItem, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './TrackMenu.js';
|
|
13
|
+
export { copyTrackSnapshot } from './copyTrackSnapshot.js';
|
|
14
|
+
export { pluginExtraTrackItems } from './pluginExtraTrackItems.js';
|
|
15
|
+
export { TrackMenuSessionMixin } from './TrackMenuSessionMixin.js';
|
|
16
|
+
export { finalizeSession } from './finalizeSession.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SessionWithDialog } from './TrackMenu.ts';
|
|
2
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
3
|
+
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
4
|
+
import type { MenuItem } from '@jbrowse/core/ui';
|
|
5
|
+
import type { TrackActionView } from '@jbrowse/core/util/types';
|
|
6
|
+
/**
|
|
7
|
+
* plugin-contributed per-track items (`Core-extraTrackMenuItems`), surfaced in
|
|
8
|
+
* both the hierarchical selector and the in-view label menu so plugins reach
|
|
9
|
+
* every track menu consistently
|
|
10
|
+
*/
|
|
11
|
+
export declare function pluginExtraTrackItems(pluginManager: PluginManager, session: SessionWithDialog, config: AnyConfigurationModel, view?: TrackActionView): MenuItem[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { buildExtraTrackMenuItems } from '@jbrowse/core/ui/buildExtraTrackMenuItems';
|
|
2
|
+
/**
|
|
3
|
+
* plugin-contributed per-track items (`Core-extraTrackMenuItems`), surfaced in
|
|
4
|
+
* both the hierarchical selector and the in-view label menu so plugins reach
|
|
5
|
+
* every track menu consistently
|
|
6
|
+
*/
|
|
7
|
+
export function pluginExtraTrackItems(pluginManager, session, config, view) {
|
|
8
|
+
return buildExtraTrackMenuItems(pluginManager, {
|
|
9
|
+
session: session,
|
|
10
|
+
config,
|
|
11
|
+
view,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -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,24 @@
|
|
|
1
|
+
import { getSnapshot, isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
import { compareStructural } from 'mobx';
|
|
3
|
+
import { hydratedTrackForm } from './hydratedForms.js';
|
|
4
|
+
// What a config means once its defaults are resolved: a live config node
|
|
5
|
+
// answers with its snapshot, and a plain object (desktop's `jbrowse.tracks` is
|
|
6
|
+
// frozen, so its entries are the input as written) is hydrated through the
|
|
7
|
+
// same schema first. Two inputs that would build the same node compare equal.
|
|
8
|
+
function resolvedTrackConf(pluginManager, conf) {
|
|
9
|
+
return isStateTreeNode(conf)
|
|
10
|
+
? getSnapshot(conf)
|
|
11
|
+
: hydratedTrackForm(pluginManager)(conf);
|
|
12
|
+
}
|
|
13
|
+
// Re-adding a trackId the session already holds returns the existing config,
|
|
14
|
+
// which is what keeps a content-hashed trackId idempotent. It is also the trap
|
|
15
|
+
// a recomputed track walks into: the new features arrive under the old id, the
|
|
16
|
+
// old config is handed back, nothing is said, and the track keeps showing the
|
|
17
|
+
// first values it saw. Same id with different content is therefore refused,
|
|
18
|
+
// naming the two-step that works.
|
|
19
|
+
export function assertNotReaddedDifferently(pluginManager, existing, trackConf) {
|
|
20
|
+
const same = compareStructural(resolvedTrackConf(pluginManager, existing), resolvedTrackConf(pluginManager, trackConf));
|
|
21
|
+
if (!same) {
|
|
22
|
+
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.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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 — currently the
|
|
16
|
+
* workspaces intent alone.
|
|
17
|
+
*
|
|
18
|
+
* It keeps its own entry point rather than being inlined into the one caller
|
|
19
|
+
* because the failure of a boundary that performs half of them is invisible at
|
|
20
|
+
* that boundary: the export succeeds, and what is missing only shows up on
|
|
21
|
+
* someone else's screen.
|
|
22
|
+
*/
|
|
23
|
+
export declare function bakeSessionCascades(session: AbstractSessionModel, snapshot: Record<string, unknown>): Record<string, unknown>;
|