@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.
Files changed (157) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +514 -323
  2. package/esm/RootModel/BaseRootModel.js +141 -14
  3. package/esm/RootModel/HierarchicalConfig.d.ts +30 -20
  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 +37 -0
  8. package/esm/RootModel/PreferencesConfig.js +75 -0
  9. package/esm/RootModel/createConfigModel.d.ts +257 -0
  10. package/esm/RootModel/createConfigModel.js +77 -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 +141 -0
  15. package/esm/Session/BaseSession.d.ts +285 -11
  16. package/esm/Session/BaseSession.js +383 -15
  17. package/esm/Session/Connections.d.ts +115 -8
  18. package/esm/Session/Connections.js +196 -74
  19. package/esm/Session/DrawerWidgets.d.ts +145 -13
  20. package/esm/Session/DrawerWidgets.js +219 -49
  21. package/esm/Session/MultipleViews.d.ts +1067 -1177
  22. package/esm/Session/MultipleViews.js +395 -64
  23. package/esm/Session/Preferences.d.ts +1389 -0
  24. package/esm/Session/Preferences.js +58 -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 +1101 -1180
  28. package/esm/Session/SessionTracks.js +584 -34
  29. package/esm/Session/Themes.d.ts +136 -2
  30. package/esm/Session/Themes.js +267 -21
  31. package/esm/Session/TrackMenu.d.ts +88 -0
  32. package/esm/Session/TrackMenu.js +175 -0
  33. package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
  34. package/esm/Session/TrackMenuSessionMixin.js +42 -0
  35. package/esm/Session/Tracks.d.ts +945 -1159
  36. package/esm/Session/Tracks.js +229 -30
  37. package/esm/Session/assertSessionModel.d.ts +21 -0
  38. package/esm/Session/assertSessionModel.js +1 -0
  39. package/esm/Session/copyTrackSnapshot.d.ts +21 -0
  40. package/esm/Session/copyTrackSnapshot.js +20 -0
  41. package/esm/Session/developerMode.d.ts +7 -0
  42. package/esm/Session/developerMode.js +43 -0
  43. package/esm/Session/finalizeSession.d.ts +15 -0
  44. package/esm/Session/finalizeSession.js +28 -0
  45. package/esm/Session/hydratedForms.d.ts +16 -0
  46. package/esm/Session/hydratedForms.js +13 -0
  47. package/esm/Session/index.d.ts +27 -10
  48. package/esm/Session/index.js +16 -10
  49. package/esm/Session/pluginExtraTrackItems.d.ts +11 -0
  50. package/esm/Session/pluginExtraTrackItems.js +13 -0
  51. package/esm/Session/readdedTrackConf.d.ts +8 -0
  52. package/esm/Session/readdedTrackConf.js +24 -0
  53. package/esm/Session/shareableSnapshot.d.ts +23 -0
  54. package/esm/Session/shareableSnapshot.js +66 -0
  55. package/esm/Session/temporaryAssemblyTracks.d.ts +24 -0
  56. package/esm/Session/temporaryAssemblyTracks.js +37 -0
  57. package/esm/assertExtends.d.ts +55 -0
  58. package/esm/assertExtends.js +1 -0
  59. package/esm/controllerTracks.d.ts +76 -0
  60. package/esm/controllerTracks.js +125 -0
  61. package/esm/destroyViewState.d.ts +21 -0
  62. package/esm/destroyViewState.js +29 -0
  63. package/esm/embeddedController.d.ts +57 -0
  64. package/esm/embeddedController.js +100 -0
  65. package/esm/index.d.ts +32 -4
  66. package/esm/index.js +31 -4
  67. package/esm/loadPlugins.d.ts +38 -0
  68. package/esm/loadPlugins.js +45 -0
  69. package/esm/localFiles.d.ts +74 -0
  70. package/esm/localFiles.js +156 -0
  71. package/esm/observeSession.d.ts +55 -0
  72. package/esm/observeSession.js +90 -0
  73. package/esm/parallelChunkLoading.d.ts +30 -0
  74. package/esm/parallelChunkLoading.js +71 -0
  75. package/esm/pluginInput.d.ts +32 -0
  76. package/esm/pluginInput.js +19 -0
  77. package/esm/pruneUnbuildableNodes.d.ts +109 -0
  78. package/esm/pruneUnbuildableNodes.js +562 -0
  79. package/esm/resolveAssemblies.d.ts +68 -0
  80. package/esm/resolveAssemblies.js +77 -0
  81. package/esm/rpcWorker.d.ts +36 -4
  82. package/esm/rpcWorker.js +126 -20
  83. package/esm/sessionMigrations/index.d.ts +15 -0
  84. package/esm/sessionMigrations/index.js +178 -0
  85. package/esm/sessionUrl.d.ts +40 -0
  86. package/esm/sessionUrl.js +66 -0
  87. package/esm/sessionUtils.d.ts +74 -0
  88. package/esm/sessionUtils.js +599 -0
  89. package/esm/siblingCast.d.ts +14 -0
  90. package/esm/siblingCast.js +16 -0
  91. package/esm/snapshotUtils.d.ts +8 -0
  92. package/esm/snapshotUtils.js +12 -0
  93. package/esm/ui/AboutDialog.d.ts +8 -3
  94. package/esm/ui/AboutDialog.js +14 -5
  95. package/esm/ui/AboutDialogContents.d.ts +2 -6
  96. package/esm/ui/AboutDialogContents.js +13 -17
  97. package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
  98. package/esm/ui/AssemblyInfoPanel.js +63 -0
  99. package/esm/ui/DescriptionPanel.d.ts +3 -0
  100. package/esm/ui/DescriptionPanel.js +25 -0
  101. package/esm/ui/Drawer.d.ts +21 -0
  102. package/esm/ui/Drawer.js +60 -0
  103. package/esm/ui/DrawerControls.d.ts +5 -0
  104. package/esm/ui/DrawerControls.js +28 -0
  105. package/esm/ui/DrawerHeader.d.ts +6 -0
  106. package/esm/ui/DrawerHeader.js +36 -0
  107. package/esm/ui/DrawerWidget.d.ts +5 -0
  108. package/esm/ui/DrawerWidget.js +12 -0
  109. package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
  110. package/esm/ui/DrawerWidgetSelector.js +40 -0
  111. package/esm/ui/FileInfoPanel.d.ts +3 -6
  112. package/esm/ui/FileInfoPanel.js +36 -26
  113. package/esm/ui/GeneralPreferencesTab.d.ts +17 -0
  114. package/esm/ui/GeneralPreferencesTab.js +34 -0
  115. package/esm/ui/HeaderButtons.d.ts +2 -1
  116. package/esm/ui/HeaderButtons.js +15 -17
  117. package/esm/ui/ModalWidget.d.ts +13 -0
  118. package/esm/ui/ModalWidget.js +48 -0
  119. package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
  120. package/esm/ui/ModalWidgetAppBar.js +9 -0
  121. package/esm/ui/PreferenceCheckbox.d.ts +6 -0
  122. package/esm/ui/PreferenceCheckbox.js +16 -0
  123. package/esm/ui/PreferencesDialog.d.ts +34 -0
  124. package/esm/ui/PreferencesDialog.js +55 -0
  125. package/esm/ui/PreferencesResetDialog.d.ts +8 -0
  126. package/esm/ui/PreferencesResetDialog.js +19 -0
  127. package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
  128. package/esm/ui/RefNameAliasesDialog.js +65 -0
  129. package/esm/ui/RefNameInfoDialog.d.ts +3 -6
  130. package/esm/ui/RefNameInfoDialog.js +45 -63
  131. package/esm/ui/ViewPreferencesTab.d.ts +12 -0
  132. package/esm/ui/ViewPreferencesTab.js +15 -0
  133. package/esm/ui/WidgetBody.d.ts +18 -0
  134. package/esm/ui/WidgetBody.js +22 -0
  135. package/esm/ui/WidgetHeading.d.ts +7 -0
  136. package/esm/ui/WidgetHeading.js +7 -0
  137. package/esm/ui/aboutTestUtils.d.ts +140 -0
  138. package/esm/ui/aboutTestUtils.js +25 -0
  139. package/esm/ui/descriptionHtml.d.ts +25 -0
  140. package/esm/ui/descriptionHtml.js +64 -0
  141. package/esm/ui/drawerLayout.d.ts +18 -0
  142. package/esm/ui/drawerLayout.js +22 -0
  143. package/esm/ui/index.d.ts +15 -1
  144. package/esm/ui/index.js +13 -1
  145. package/esm/ui/preferencesReset.d.ts +18 -0
  146. package/esm/ui/preferencesReset.js +69 -0
  147. package/esm/ui/util.d.ts +22 -7
  148. package/esm/ui/util.js +19 -41
  149. package/esm/useEngineLifecycle.d.ts +19 -0
  150. package/esm/useEngineLifecycle.js +44 -0
  151. package/package.json +15 -18
  152. package/esm/RootModel/FormatAbout.d.ts +0 -12
  153. package/esm/RootModel/FormatAbout.js +0 -15
  154. package/esm/RootModel/FormatDetails.d.ts +0 -24
  155. package/esm/RootModel/FormatDetails.js +0 -27
  156. package/esm/Session/DialogQueue.d.ts +0 -17
  157. package/esm/Session/DialogQueue.js +0 -31
@@ -1,49 +1,248 @@
1
- import { types } from '@jbrowse/mobx-state-tree';
2
- import { BaseSessionModel, isBaseSession } from "./BaseSession.js";
3
- import { ReferenceManagementSessionMixin } from "./ReferenceManagement.js";
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
- get assemblies() {
12
- return self.jbrowse.assemblies;
13
- },
14
- getTracksById() {
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
- const connectionInstances = 'connectionInstances' in self
19
- ? self.connectionInstances
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
- ...this.tracks.map(t => [t.trackId, t]),
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
- .actions(self => ({
30
- addTrackConf(trackConf) {
31
- return self.jbrowse.addTrackConf(trackConf);
32
- },
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();
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
- if (self.adminMode) {
42
- return self.jbrowse.deleteTrackConf(trackConf);
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
+ }
@@ -1,10 +1,27 @@
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, 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';
@@ -1,10 +1,16 @@
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, 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>;