@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.
Files changed (137) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +629 -304
  2. package/esm/RootModel/BaseRootModel.js +139 -14
  3. package/esm/RootModel/HierarchicalConfig.d.ts +57 -21
  4. package/esm/RootModel/HierarchicalConfig.js +36 -0
  5. package/esm/RootModel/InternetAccounts.d.ts +18 -1
  6. package/esm/RootModel/InternetAccounts.js +120 -22
  7. package/esm/RootModel/PreferencesConfig.d.ts +68 -0
  8. package/esm/RootModel/PreferencesConfig.js +76 -0
  9. package/esm/RootModel/createConfigModel.d.ts +240 -0
  10. package/esm/RootModel/createConfigModel.js +56 -0
  11. package/esm/RootModel/index.d.ts +10 -5
  12. package/esm/RootModel/index.js +10 -5
  13. package/esm/RootModel/menuItems.d.ts +24 -0
  14. package/esm/RootModel/menuItems.js +143 -0
  15. package/esm/Session/BaseSession.d.ts +312 -11
  16. package/esm/Session/BaseSession.js +458 -14
  17. package/esm/Session/Connections.d.ts +114 -8
  18. package/esm/Session/Connections.js +195 -74
  19. package/esm/Session/DrawerWidgets.d.ts +115 -12
  20. package/esm/Session/DrawerWidgets.js +161 -42
  21. package/esm/Session/MultipleViews.d.ts +1281 -1128
  22. package/esm/Session/MultipleViews.js +396 -64
  23. package/esm/Session/Preferences.d.ts +1667 -0
  24. package/esm/Session/Preferences.js +59 -0
  25. package/esm/Session/ReferenceManagement.d.ts +28 -3
  26. package/esm/Session/ReferenceManagement.js +56 -41
  27. package/esm/Session/SessionTracks.d.ts +1277 -1138
  28. package/esm/Session/SessionTracks.js +540 -35
  29. package/esm/Session/Themes.d.ts +87 -2
  30. package/esm/Session/Themes.js +156 -23
  31. package/esm/Session/TrackMenu.d.ts +112 -0
  32. package/esm/Session/TrackMenu.js +200 -0
  33. package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
  34. package/esm/Session/TrackMenuSessionMixin.js +41 -0
  35. package/esm/Session/Tracks.d.ts +1171 -1117
  36. package/esm/Session/Tracks.js +183 -20
  37. package/esm/Session/assertSessionModel.d.ts +21 -0
  38. package/esm/Session/assertSessionModel.js +1 -0
  39. package/esm/Session/developerMode.d.ts +7 -0
  40. package/esm/Session/developerMode.js +43 -0
  41. package/esm/Session/finalizeSession.d.ts +16 -0
  42. package/esm/Session/finalizeSession.js +29 -0
  43. package/esm/Session/index.d.ts +22 -10
  44. package/esm/Session/index.js +13 -10
  45. package/esm/Session/readdedTrackConf.d.ts +8 -0
  46. package/esm/Session/readdedTrackConf.js +25 -0
  47. package/esm/Session/shareableSnapshot.d.ts +77 -0
  48. package/esm/Session/shareableSnapshot.js +196 -0
  49. package/esm/Session/temporaryAssemblyTracks.d.ts +24 -0
  50. package/esm/Session/temporaryAssemblyTracks.js +37 -0
  51. package/esm/assertExtends.d.ts +55 -0
  52. package/esm/assertExtends.js +1 -0
  53. package/esm/controllerTracks.d.ts +56 -0
  54. package/esm/controllerTracks.js +90 -0
  55. package/esm/destroyViewState.d.ts +21 -0
  56. package/esm/destroyViewState.js +29 -0
  57. package/esm/index.d.ts +31 -4
  58. package/esm/index.js +31 -4
  59. package/esm/loadPlugins.d.ts +33 -0
  60. package/esm/loadPlugins.js +41 -0
  61. package/esm/localFiles.d.ts +74 -0
  62. package/esm/localFiles.js +156 -0
  63. package/esm/observeSession.d.ts +56 -0
  64. package/esm/observeSession.js +91 -0
  65. package/esm/pluginInput.d.ts +32 -0
  66. package/esm/pluginInput.js +19 -0
  67. package/esm/pruneUnbuildableNodes.d.ts +109 -0
  68. package/esm/pruneUnbuildableNodes.js +568 -0
  69. package/esm/resolveAssemblies.d.ts +44 -0
  70. package/esm/resolveAssemblies.js +55 -0
  71. package/esm/rpcWorker.d.ts +26 -0
  72. package/esm/rpcWorker.js +98 -16
  73. package/esm/scheduleDetachedDestroy.d.ts +18 -0
  74. package/esm/scheduleDetachedDestroy.js +24 -0
  75. package/esm/sessionMigrations/index.d.ts +53 -0
  76. package/esm/sessionMigrations/index.js +432 -0
  77. package/esm/sessionUrl.d.ts +41 -0
  78. package/esm/sessionUrl.js +53 -0
  79. package/esm/sessionUtils.d.ts +69 -0
  80. package/esm/sessionUtils.js +613 -0
  81. package/esm/siblingCast.d.ts +14 -0
  82. package/esm/siblingCast.js +16 -0
  83. package/esm/snapshotUtils.d.ts +8 -0
  84. package/esm/snapshotUtils.js +12 -0
  85. package/esm/ui/AboutDialog.d.ts +3 -6
  86. package/esm/ui/AboutDialog.js +4 -4
  87. package/esm/ui/AboutDialogContents.d.ts +2 -6
  88. package/esm/ui/AboutDialogContents.js +34 -13
  89. package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
  90. package/esm/ui/AssemblyInfoPanel.js +69 -0
  91. package/esm/ui/DisplayDefaultsSection.d.ts +36 -0
  92. package/esm/ui/DisplayDefaultsSection.js +65 -0
  93. package/esm/ui/Drawer.d.ts +21 -0
  94. package/esm/ui/Drawer.js +60 -0
  95. package/esm/ui/DrawerControls.d.ts +5 -0
  96. package/esm/ui/DrawerControls.js +28 -0
  97. package/esm/ui/DrawerHeader.d.ts +6 -0
  98. package/esm/ui/DrawerHeader.js +36 -0
  99. package/esm/ui/DrawerWidget.d.ts +5 -0
  100. package/esm/ui/DrawerWidget.js +12 -0
  101. package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
  102. package/esm/ui/DrawerWidgetSelector.js +40 -0
  103. package/esm/ui/FileInfoPanel.d.ts +3 -6
  104. package/esm/ui/FileInfoPanel.js +35 -26
  105. package/esm/ui/HeaderButtons.d.ts +7 -1
  106. package/esm/ui/HeaderButtons.js +23 -18
  107. package/esm/ui/ModalWidget.d.ts +12 -0
  108. package/esm/ui/ModalWidget.js +33 -0
  109. package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
  110. package/esm/ui/ModalWidgetAppBar.js +9 -0
  111. package/esm/ui/PreferencesDialog.d.ts +56 -0
  112. package/esm/ui/PreferencesDialog.js +145 -0
  113. package/esm/ui/PreferencesResetDialog.d.ts +8 -0
  114. package/esm/ui/PreferencesResetDialog.js +19 -0
  115. package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
  116. package/esm/ui/RefNameAliasesDialog.js +65 -0
  117. package/esm/ui/RefNameInfoDialog.d.ts +3 -6
  118. package/esm/ui/RefNameInfoDialog.js +57 -63
  119. package/esm/ui/WidgetBody.d.ts +18 -0
  120. package/esm/ui/WidgetBody.js +22 -0
  121. package/esm/ui/WidgetHeading.d.ts +7 -0
  122. package/esm/ui/WidgetHeading.js +7 -0
  123. package/esm/ui/drawerLayout.d.ts +18 -0
  124. package/esm/ui/drawerLayout.js +22 -0
  125. package/esm/ui/index.d.ts +13 -1
  126. package/esm/ui/index.js +12 -1
  127. package/esm/ui/util.d.ts +37 -4
  128. package/esm/ui/util.js +35 -38
  129. package/esm/useEngineLifecycle.d.ts +19 -0
  130. package/esm/useEngineLifecycle.js +44 -0
  131. package/package.json +12 -15
  132. package/esm/RootModel/FormatAbout.d.ts +0 -12
  133. package/esm/RootModel/FormatAbout.js +0 -15
  134. package/esm/RootModel/FormatDetails.d.ts +0 -24
  135. package/esm/RootModel/FormatDetails.js +0 -27
  136. package/esm/Session/DialogQueue.d.ts +0 -17
  137. package/esm/Session/DialogQueue.js +0 -31
@@ -1,49 +1,212 @@
1
+ import { expandLooseTrackConfig } from '@jbrowse/core/util/tracks';
1
2
  import { types } from '@jbrowse/mobx-state-tree';
2
- import { BaseSessionModel, isBaseSession } from "./BaseSession.js";
3
- import { ReferenceManagementSessionMixin } from "./ReferenceManagement.js";
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
- get assemblies() {
12
- return self.jbrowse.assemblies;
13
- },
14
- getTracksById() {
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
- const connectionInstances = 'connectionInstances' in self
19
- ? self.connectionInstances
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
- ...this.tracks.map(t => [t.trackId, t]),
23
- ...this.assemblies.map(a => [a.sequence.trackId, a.sequence]),
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
- deleteTrackConf(trackConf) {
34
- const callbacksToDereferenceTrack = [];
35
- const dereferenceTypeCount = {};
36
- const referring = self.getReferring(trackConf);
37
- self.removeReferring(referring, trackConf, callbacksToDereferenceTrack, dereferenceTypeCount);
38
- for (const cb of callbacksToDereferenceTrack) {
39
- cb();
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
+ }
@@ -1,10 +1,22 @@
1
- export * from './ReferenceManagement.ts';
2
- export * from './Connections.ts';
3
- export * from './DrawerWidgets.ts';
4
- export * from './DialogQueue.ts';
5
- export * from './Themes.ts';
6
- export * from './Tracks.ts';
7
- export * from './MultipleViews.ts';
8
- export * from './BaseSession.ts';
9
- export * from './SessionTracks.ts';
10
- export * from './BaseSession.ts';
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';
@@ -1,10 +1,13 @@
1
- export * from "./ReferenceManagement.js";
2
- export * from "./Connections.js";
3
- export * from "./DrawerWidgets.js";
4
- export * from "./DialogQueue.js";
5
- export * from "./Themes.js";
6
- export * from "./Tracks.js";
7
- export * from "./MultipleViews.js";
8
- export * from "./BaseSession.js";
9
- export * from "./SessionTracks.js";
10
- export * from "./BaseSession.js";
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>;