@jbrowse/product-core 5.0.0-beta.8 → 5.0.0-beta.9

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 (64) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +1 -4
  2. package/esm/RootModel/HierarchicalConfig.d.ts +21 -47
  3. package/esm/RootModel/PreferencesConfig.d.ts +18 -49
  4. package/esm/RootModel/PreferencesConfig.js +2 -3
  5. package/esm/RootModel/createConfigModel.d.ts +141 -124
  6. package/esm/RootModel/createConfigModel.js +26 -14
  7. package/esm/RootModel/menuItems.js +4 -6
  8. package/esm/Session/BaseSession.d.ts +30 -13
  9. package/esm/Session/BaseSession.js +50 -16
  10. package/esm/Session/Connections.d.ts +4 -3
  11. package/esm/Session/Connections.js +4 -3
  12. package/esm/Session/DrawerWidgets.d.ts +8 -1
  13. package/esm/Session/DrawerWidgets.js +19 -7
  14. package/esm/Session/MultipleViews.d.ts +13 -14
  15. package/esm/Session/Preferences.d.ts +8 -13
  16. package/esm/Session/SessionTracks.d.ts +32 -38
  17. package/esm/Session/SessionTracks.js +52 -115
  18. package/esm/Session/Themes.d.ts +9 -10
  19. package/esm/Session/Themes.js +9 -10
  20. package/esm/Session/TrackMenu.d.ts +1 -26
  21. package/esm/Session/TrackMenu.js +2 -33
  22. package/esm/Session/TrackMenuSessionMixin.js +2 -1
  23. package/esm/Session/Tracks.d.ts +8 -13
  24. package/esm/Session/copyTrackSnapshot.d.ts +21 -0
  25. package/esm/Session/copyTrackSnapshot.js +20 -0
  26. package/esm/Session/hydratedForms.d.ts +16 -0
  27. package/esm/Session/hydratedForms.js +13 -0
  28. package/esm/Session/index.d.ts +5 -1
  29. package/esm/Session/index.js +4 -1
  30. package/esm/Session/pluginExtraTrackItems.d.ts +11 -0
  31. package/esm/Session/pluginExtraTrackItems.js +13 -0
  32. package/esm/controllerTracks.d.ts +13 -8
  33. package/esm/controllerTracks.js +18 -9
  34. package/esm/index.d.ts +6 -6
  35. package/esm/index.js +3 -3
  36. package/esm/loadPlugins.d.ts +7 -2
  37. package/esm/loadPlugins.js +6 -2
  38. package/esm/resolveAssemblies.d.ts +34 -11
  39. package/esm/resolveAssemblies.js +29 -11
  40. package/esm/rpcWorker.d.ts +12 -5
  41. package/esm/rpcWorker.js +1 -1
  42. package/esm/sessionMigrations/index.d.ts +5 -5
  43. package/esm/sessionMigrations/index.js +82 -19
  44. package/esm/sessionUrl.js +1 -1
  45. package/esm/sessionUtils.d.ts +6 -1
  46. package/esm/sessionUtils.js +42 -35
  47. package/esm/ui/AboutDialog.d.ts +10 -2
  48. package/esm/ui/AboutDialog.js +11 -2
  49. package/esm/ui/AboutDialogContents.js +3 -21
  50. package/esm/ui/AppReadyMarker.d.ts +45 -0
  51. package/esm/ui/AppReadyMarker.js +89 -0
  52. package/esm/ui/AssemblyInfoPanel.js +4 -10
  53. package/esm/ui/DescriptionPanel.js +2 -1
  54. package/esm/ui/FileInfoPanel.js +5 -10
  55. package/esm/ui/RefNameInfoDialog.js +10 -23
  56. package/esm/ui/aboutTestUtils.d.ts +140 -0
  57. package/esm/ui/aboutTestUtils.js +25 -0
  58. package/esm/ui/descriptionHtml.d.ts +5 -6
  59. package/esm/ui/descriptionHtml.js +5 -7
  60. package/esm/ui/index.d.ts +2 -0
  61. package/esm/ui/index.js +1 -0
  62. package/esm/ui/util.d.ts +9 -27
  63. package/esm/ui/util.js +12 -31
  64. package/package.json +4 -4
@@ -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
+ }
@@ -3,9 +3,10 @@ import type { LooseTrackInput } from '@jbrowse/core/util/tracks';
3
3
  import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
4
4
  export type TrackConf = Record<string, unknown>;
5
5
  /**
6
- * What a controller accepts per track: a full config, a bare data-file URL, or
7
- * `{ uri, index?, ...extra }`. The loose forms expand through core's
8
- * `guessTrackConf`, the same inference the "Add track" flow runs.
6
+ * What a controller accepts per track: a full config, the trackId of one the
7
+ * catalog already holds (a hub's, when the assembly came from one), a bare
8
+ * data-file URL, or `{ uri, index?, ...extra }`. The loose forms expand through
9
+ * core's `guessTrackConf`, the same inference the "Add track" flow runs.
9
10
  */
10
11
  export type TrackInput = string | TrackConf;
11
12
  interface ControllerView {
@@ -34,10 +35,11 @@ export declare function isLooseTrack(track: TrackInput): track is string | Loose
34
35
  export declare function withAssemblyName(track: TrackConf, assemblyName?: string): TrackConf;
35
36
  /**
36
37
  * Expand every loose entry into a full track config; stamp the assembly name
37
- * onto the ones already written out. `node` is any node of the live tree — the
38
- * pluginManager whose format plugins drive the guess comes off its env.
38
+ * onto the ones already written out. A string the catalog already resolves is
39
+ * that track, and is tried before the file guess: "not URL-shaped" cannot tell
40
+ * an id from a relative path like `data/reads.bam`.
39
41
  */
40
- export declare function resolveTracks(tracks: TrackInput[], node: IStateTreeNode, assemblyName?: string, localFiles?: Record<string, BlobLocation>): TrackConf[];
42
+ export declare function resolveTracks(tracks: TrackInput[], session: ControllerSession, assemblyName?: string, localFiles?: Record<string, BlobLocation>): TrackConf[];
41
43
  /**
42
44
  * Register each track config only if the session cannot already resolve it, then
43
45
  * show it. `getTrackById` resolves catalog + connection + session tracks, so a
@@ -51,6 +53,9 @@ export declare function resolveTracks(tracks: TrackInput[], node: IStateTreeNode
51
53
  export declare function openTracks(session: ControllerSession, tracks: TrackConf[]): Promise<void>;
52
54
  /** Open every wanted track and close any others the view currently shows. */
53
55
  export declare function reconcileTracks(session: ControllerSession, tracks: TrackConf[]): Promise<void>;
54
- /** Merge a hub's own search adapters with the ones the caller supplied. */
55
- export declare function mergeSearchAdapters<T>(a: readonly T[] | undefined, b: readonly T[] | undefined): T[] | undefined;
56
+ /**
57
+ * A hub's entries followed by the host's own, the host's winning where both
58
+ * carry the same `idKey`.
59
+ */
60
+ export declare function withHostOverrides<T extends object>(hub: readonly T[] | undefined, host: readonly T[] | undefined, idKey: string): T[];
56
61
  export {};
@@ -1,4 +1,5 @@
1
1
  import { getEnv } from '@jbrowse/core/util';
2
+ import { withPageBaseUri } from '@jbrowse/core/util/addRelativeUris';
2
3
  import { guessTrackConf } from '@jbrowse/core/util/tracks';
3
4
  import { isAlive } from '@jbrowse/mobx-state-tree';
4
5
  import { resolveLocalFileUris } from './localFiles.js';
@@ -20,12 +21,16 @@ export function withAssemblyName(track, assemblyName) {
20
21
  }
21
22
  /**
22
23
  * Expand every loose entry into a full track config; stamp the assembly name
23
- * onto the ones already written out. `node` is any node of the live tree — the
24
- * pluginManager whose format plugins drive the guess comes off its env.
24
+ * onto the ones already written out. A string the catalog already resolves is
25
+ * that track, and is tried before the file guess: "not URL-shaped" cannot tell
26
+ * an id from a relative path like `data/reads.bam`.
25
27
  */
26
- export function resolveTracks(tracks, node, assemblyName, localFiles) {
27
- const { pluginManager } = getEnv(node);
28
+ export function resolveTracks(tracks, session, assemblyName, localFiles) {
29
+ const { pluginManager } = getEnv(session);
28
30
  return tracks.map(track => {
31
+ if (typeof track === 'string' && session.getTrackById(track)) {
32
+ return { trackId: track };
33
+ }
29
34
  const conf = isLooseTrack(track)
30
35
  ? guessTrackConf(track, pluginManager, assemblyName)
31
36
  : // full configs are stamped here too, not just in the build() catalog
@@ -35,7 +40,7 @@ export function resolveTracks(tracks, node, assemblyName, localFiles) {
35
40
  withAssemblyName(track, assemblyName);
36
41
  // after the guess, so the adapter has already derived its index sibling
37
42
  // from the uri string and both get swapped for blobs together
38
- return localFiles ? resolveLocalFileUris(conf, localFiles) : conf;
43
+ return withPageBaseUri(localFiles ? resolveLocalFileUris(conf, localFiles) : conf);
39
44
  });
40
45
  }
41
46
  /**
@@ -83,8 +88,12 @@ export async function reconcileTracks(session, tracks) {
83
88
  view.hideTrack(trackId);
84
89
  }
85
90
  }
86
- /** Merge a hub's own search adapters with the ones the caller supplied. */
87
- export function mergeSearchAdapters(a, b) {
88
- const merged = [...(a ?? []), ...(b ?? [])];
89
- return merged.length ? merged : undefined;
91
+ /**
92
+ * A hub's entries followed by the host's own, the host's winning where both
93
+ * carry the same `idKey`.
94
+ */
95
+ export function withHostOverrides(hub = [], host = [], idKey) {
96
+ const idOf = (entry) => entry[idKey];
97
+ const hostIds = new Set(host.map(idOf).filter(id => id !== undefined));
98
+ return [...hub.filter(entry => !hostIds.has(idOf(entry))), ...host];
90
99
  }
package/esm/index.d.ts CHANGED
@@ -3,10 +3,10 @@ export { asRoot, asSession } from './siblingCast.ts';
3
3
  export { scheduleDetachedDestroy } from '@jbrowse/core/util';
4
4
  export { BaseRootModelFactory, FormatAboutConfigSchemaFactory, FormatDetailsConfigSchemaFactory, HierarchicalConfigSchemaFactory, InternetAccountsRootModelMixin, PreferencesConfigSchemaFactory, createConfigModel, exportSessionMenuItem, importSessionMenuItem, isRootModel, newSessionMenuItem, openConnectionMenuItem, openTrackMenuItem, pluginStoreMenuItem, preferencesMenuItem, redoMenuItem, undoMenuItem, workspacesMenuItem, } from './RootModel/index.ts';
5
5
  export type { BaseRootModel, BaseRootModelType, RootConfigurationSnapshot, RootModelWithInternetAccounts, RootModelWithInternetAccountsType, } from './RootModel/index.ts';
6
- export { BaseSessionModel, ConnectionManagementSessionMixin, DrawerWidgetSessionMixin, MultipleViewsSessionMixin, PreferencesSessionMixin, ReferenceManagementSessionMixin, SessionTracksManagerSessionMixin, ThemeManagerSessionMixin, TrackMenuItemsSessionMixin, TrackMenuSessionMixin, TracksManagerSessionMixin, aboutTrackMenuItem, bakeSessionCascades, getShareableSessionSnapshot, copyTrackSnapshot, finalizeSession, isBaseSession, isSession, isSessionWithConnections, isSessionWithDrawerWidgets, isSessionWithMultipleViews, isSessionWithReferenceManagement, isSessionWithThemes, isSessionWithTracks, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './Session/index.ts';
7
- export type { AssertSessionModel, BaseSession, BaseSessionType, ConnectionTrackConfigEntry, EditableTrackConfig, PlainTrackConfig, ReferringNode, SessionWithConnections, SessionWithConnectionsType, SessionWithDrawerWidgets, SessionWithDrawerWidgetsType, SessionWithMultipleViews, SessionWithMultipleViewsType, SessionWithReferenceManagement, SessionWithReferenceManagementType, SessionWithSessionTracks, SessionWithSessionTracksType, SessionWithThemes, SessionWithThemesType, SessionWithTracks, SessionWithTracksType, } from './Session/index.ts';
8
- export { AboutDialog, Drawer, DrawerControls, DrawerHeader, DrawerWidget, DrawerWidgetSelector, ModalWidget, ModalWidgetAppBar, PreferencesDialog, WidgetBody, WidgetHeading, drawerGridTemplateColumns, } from './ui/index.ts';
9
- export type { PreferencesDialogSession, PreferencesPanelDescriptor, } from './ui/index.ts';
6
+ export { BaseSessionModel, ConnectionManagementSessionMixin, DrawerWidgetSessionMixin, MultipleViewsSessionMixin, PreferencesSessionMixin, ReferenceManagementSessionMixin, SessionTracksManagerSessionMixin, ThemeManagerSessionMixin, TrackMenuItemsSessionMixin, TrackMenuSessionMixin, TracksManagerSessionMixin, aboutTrackMenuItem, bakeSessionCascades, getShareableSessionSnapshot, copyTrackSnapshot, finalizeSession, hydratedForms, isBaseSession, isSession, isSessionWithConnections, isSessionWithDrawerWidgets, isSessionWithMultipleViews, isSessionWithReferenceManagement, isSessionWithThemes, isSessionWithTracks, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './Session/index.ts';
7
+ export type { AssertSessionModel, BaseSession, BaseSessionType, ConnectionTrackConfigEntry, EditableTrackConfig, HydratedForms, PlainTrackConfig, ReferringNode, SessionWithConnections, SessionWithConnectionsType, SessionWithDrawerWidgets, SessionWithDrawerWidgetsType, SessionWithMultipleViews, SessionWithMultipleViewsType, SessionWithReferenceManagement, SessionWithReferenceManagementType, SessionWithSessionTracks, SessionWithSessionTracksType, SessionWithThemes, SessionWithThemesType, SessionWithTracks, SessionWithTracksType, } from './Session/index.ts';
8
+ export { AboutDialog, AppReadyMarker, Drawer, DrawerControls, DrawerHeader, DrawerWidget, DrawerWidgetSelector, ModalWidget, ModalWidgetAppBar, PreferencesDialog, WidgetBody, WidgetHeading, drawerGridTemplateColumns, } from './ui/index.ts';
9
+ export type { AboutPanelProps, PreferencesDialogSession, PreferencesPanelDescriptor, } from './ui/index.ts';
10
10
  export { initializeWorker } from './rpcWorker.ts';
11
11
  export { toPluginLoadRecord } from './pluginInput.ts';
12
12
  export type { PluginInput } from './pluginInput.ts';
@@ -16,7 +16,7 @@ export { decodeSessionFromUrl, encodeSessionToUrl } from './sessionUrl.ts';
16
16
  export type { SessionSnapshot } from './sessionUrl.ts';
17
17
  export { mergeLocalFiles, normalizeAdapterSnapshots, registerLocalFiles, resolveLocalFileUris, } from './localFiles.ts';
18
18
  export type { LocalFileInput } from './localFiles.ts';
19
- export { isLooseTrack, mergeSearchAdapters, openTracks, reconcileTracks, resolveTracks, withAssemblyName, } from './controllerTracks.ts';
19
+ export { isLooseTrack, openTracks, reconcileTracks, resolveTracks, withAssemblyName, withHostOverrides, } from './controllerTracks.ts';
20
20
  export type { ControllerSession, TrackConf, TrackInput, } from './controllerTracks.ts';
21
21
  export { resolveAssemblies, resolveAssembly } from './resolveAssemblies.ts';
22
22
  export type { AssemblyInput, ResolvedAssemblies } from './resolveAssemblies.ts';
@@ -26,6 +26,6 @@ export { getSessionSnapshot, observeSession } from './observeSession.ts';
26
26
  export type { SessionObservers, ViewLocation } from './observeSession.ts';
27
27
  export { MIGRATED_DISPLAY_INSTANCE_KEYS, migrateConfigSnapshot, migrateSessionSnapshot, } from './sessionMigrations/index.ts';
28
28
  export { DEFAULT_WEB_BASE_URL, analyzeWebPortability, buildWebExportUrl, filterSessionInPlace, planWebExport, } from './sessionUtils.ts';
29
- export type { AssemblySnapshot, DroppedSessionNode, HostedBaseConfig, NonPortableLocation, SelfContainedReason, TrackSnapshot, WebExportInput, WebExportPlan, WebPortabilityReport, } from './sessionUtils.ts';
29
+ export type { AssemblySnapshot, DroppedSessionNode, HostedBase, HostedBaseConfig, NonPortableLocation, SelfContainedReason, TrackSnapshot, WebExportInput, WebExportPlan, WebPortabilityReport, } from './sessionUtils.ts';
30
30
  export { describeUnbuildableNodes, pruneUnbuildableNodes, } from './pruneUnbuildableNodes.ts';
31
31
  export type { HeldNode, UnbuildableNode } from './pruneUnbuildableNodes.ts';
package/esm/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  export { asRoot, asSession } from './siblingCast.js';
2
2
  export { scheduleDetachedDestroy } from '@jbrowse/core/util';
3
3
  export { BaseRootModelFactory, FormatAboutConfigSchemaFactory, FormatDetailsConfigSchemaFactory, HierarchicalConfigSchemaFactory, InternetAccountsRootModelMixin, PreferencesConfigSchemaFactory, createConfigModel, exportSessionMenuItem, importSessionMenuItem, isRootModel, newSessionMenuItem, openConnectionMenuItem, openTrackMenuItem, pluginStoreMenuItem, preferencesMenuItem, redoMenuItem, undoMenuItem, workspacesMenuItem, } from './RootModel/index.js';
4
- export { BaseSessionModel, ConnectionManagementSessionMixin, DrawerWidgetSessionMixin, MultipleViewsSessionMixin, PreferencesSessionMixin, ReferenceManagementSessionMixin, SessionTracksManagerSessionMixin, ThemeManagerSessionMixin, TrackMenuItemsSessionMixin, TrackMenuSessionMixin, TracksManagerSessionMixin, aboutTrackMenuItem, bakeSessionCascades, getShareableSessionSnapshot, copyTrackSnapshot, finalizeSession, isBaseSession, isSession, isSessionWithConnections, isSessionWithDrawerWidgets, isSessionWithMultipleViews, isSessionWithReferenceManagement, isSessionWithThemes, isSessionWithTracks, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './Session/index.js';
5
- export { AboutDialog, Drawer, DrawerControls, DrawerHeader, DrawerWidget, DrawerWidgetSelector, ModalWidget, ModalWidgetAppBar, PreferencesDialog, WidgetBody, WidgetHeading, drawerGridTemplateColumns, } from './ui/index.js';
4
+ export { BaseSessionModel, ConnectionManagementSessionMixin, DrawerWidgetSessionMixin, MultipleViewsSessionMixin, PreferencesSessionMixin, ReferenceManagementSessionMixin, SessionTracksManagerSessionMixin, ThemeManagerSessionMixin, TrackMenuItemsSessionMixin, TrackMenuSessionMixin, TracksManagerSessionMixin, aboutTrackMenuItem, bakeSessionCascades, getShareableSessionSnapshot, copyTrackSnapshot, finalizeSession, hydratedForms, isBaseSession, isSession, isSessionWithConnections, isSessionWithDrawerWidgets, isSessionWithMultipleViews, isSessionWithReferenceManagement, isSessionWithThemes, isSessionWithTracks, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './Session/index.js';
5
+ export { AboutDialog, AppReadyMarker, Drawer, DrawerControls, DrawerHeader, DrawerWidget, DrawerWidgetSelector, ModalWidget, ModalWidgetAppBar, PreferencesDialog, WidgetBody, WidgetHeading, drawerGridTemplateColumns, } from './ui/index.js';
6
6
  export { initializeWorker } from './rpcWorker.js';
7
7
  export { toPluginLoadRecord } from './pluginInput.js';
8
8
  export { loadRuntimePlugins } from './loadPlugins.js';
@@ -13,7 +13,7 @@ export { decodeSessionFromUrl, encodeSessionToUrl } from './sessionUrl.js';
13
13
  export { mergeLocalFiles, normalizeAdapterSnapshots, registerLocalFiles, resolveLocalFileUris, } from './localFiles.js';
14
14
  // the track half of an imperative controller: loose track specs -> configs, and
15
15
  // opening exactly the wanted set
16
- export { isLooseTrack, mergeSearchAdapters, openTracks, reconcileTracks, resolveTracks, withAssemblyName, } from './controllerTracks.js';
16
+ export { isLooseTrack, openTracks, reconcileTracks, resolveTracks, withAssemblyName, withHostOverrides, } from './controllerTracks.js';
17
17
  // hub names, sequence URIs and hub configs -> assembly configs, so every
18
18
  // product takes the same assembly vocabulary
19
19
  export { resolveAssemblies, resolveAssembly } from './resolveAssemblies.js';
@@ -1,4 +1,4 @@
1
- import type { LoadedPlugin } from '@jbrowse/core/PluginLoader';
1
+ import type { LoadedPlugin, ReExportRegistryLoader } from '@jbrowse/core/PluginLoader';
2
2
  import type { PluginDefinition } from '@jbrowse/core/pluginDefinitions';
3
3
  export interface LoadPluginsArgs {
4
4
  fetchESM?: (url: string) => Promise<LoadedPlugin>;
@@ -26,8 +26,13 @@ export interface LoadPluginsArgs {
26
26
  * a second copy beside its own; a product that does not bundle them — the
27
27
  * circular-genome-view build has neither MafViewer nor GWAS in its corePlugins —
28
28
  * must still fetch one a config names, or the config silently loses the plugin.
29
+ *
30
+ * `reExports` is the product's `reExports.generated.ts`, which serves every
31
+ * `@jbrowse` package the product bundles. Required for the same reason: a
32
+ * plugin handed core's map alone cannot read the rest.
29
33
  */
30
- export declare function loadRuntimePlugins(pluginDefinitions: PluginDefinition[], { dropVendored, jbrowseVersion, ...args }: LoadPluginsArgs & {
34
+ export declare function loadRuntimePlugins(pluginDefinitions: PluginDefinition[], { dropVendored, jbrowseVersion, reExports, ...args }: LoadPluginsArgs & {
31
35
  dropVendored: boolean;
32
36
  jbrowseVersion: string;
37
+ reExports: ReExportRegistryLoader;
33
38
  }): Promise<never[] | import("@jbrowse/core/PluginLoader").PluginRecord[]>;
@@ -15,8 +15,12 @@ import { resolveStorePluginRefs } from '@jbrowse/core/util';
15
15
  * a second copy beside its own; a product that does not bundle them — the
16
16
  * circular-genome-view build has neither MafViewer nor GWAS in its corePlugins —
17
17
  * must still fetch one a config names, or the config silently loses the plugin.
18
+ *
19
+ * `reExports` is the product's `reExports.generated.ts`, which serves every
20
+ * `@jbrowse` package the product bundles. Required for the same reason: a
21
+ * plugin handed core's map alone cannot read the rest.
18
22
  */
19
- export async function loadRuntimePlugins(pluginDefinitions, { dropVendored, jbrowseVersion, ...args }) {
23
+ export async function loadRuntimePlugins(pluginDefinitions, { dropVendored, jbrowseVersion, reExports, ...args }) {
20
24
  // Before dropVendoredPlugins, which matches on the UMD name a ref does not
21
25
  // carry until the store supplies it. `jbrowseVersion` is required rather than
22
26
  // defaulted so a product that forgets it fails to compile instead of quietly
@@ -35,7 +39,7 @@ export async function loadRuntimePlugins(pluginDefinitions, { dropVendored, jbro
35
39
  const base = args.baseUri ?? args.baseUrl;
36
40
  return toLoad.length
37
41
  ? new PluginLoader(toLoad, args)
38
- .installGlobalReExports(window)
42
+ .installGlobalReExports(window, reExports)
39
43
  .load(base ?? window.location.href)
40
44
  : [];
41
45
  }
@@ -4,6 +4,10 @@ export interface TextSearchAdapterConfig {
4
4
  [key: string]: unknown;
5
5
  }
6
6
  type SearchAdapters = TextSearchAdapterConfig[] | undefined;
7
+ export interface HubTrackConfig {
8
+ trackId: string;
9
+ [key: string]: unknown;
10
+ }
7
11
  /**
8
12
  * The shapes an assembly can take, discriminated at resolve time: a sequence
9
13
  * file URL (`'.../hg38.fa.gz'`, `.2bit`, ...) built into an assembly via
@@ -16,11 +20,23 @@ export interface ResolvedAssembly {
16
20
  assembly: AssemblyConfig;
17
21
  /** whatever search adapters the hub this came from carried */
18
22
  aggregateTextSearchAdapters?: SearchAdapters;
23
+ /**
24
+ * the hub's track catalog, which its search index names its hits against, so
25
+ * a host keeping the index keeps these too
26
+ */
27
+ tracks?: HubTrackConfig[];
19
28
  }
20
- export interface ResolvedAssemblies {
29
+ export interface ResolvedAssemblies<Track extends object = HubTrackConfig, Index extends object = TextSearchAdapterConfig> {
21
30
  assemblies: AssemblyConfig[];
22
- /** whatever search adapters the resolved hubs carried, merged */
23
- aggregateTextSearchAdapters?: SearchAdapters;
31
+ /** the hubs' search adapters, then the host's own */
32
+ aggregateTextSearchAdapters?: (TextSearchAdapterConfig | Index)[];
33
+ /** the hubs' track catalogs, then the host's own tracks */
34
+ tracks?: (HubTrackConfig | Track)[];
35
+ }
36
+ /** What a host already has, to merge with what its hubs bring. */
37
+ export interface HostCatalog<Track extends object, Index extends object> {
38
+ tracks?: readonly Track[];
39
+ aggregateTextSearchAdapters?: readonly Index[];
24
40
  }
25
41
  /** One assembly in any of the four accepted shapes. */
26
42
  export declare function resolveAssembly(input: AssemblyInput): Promise<ResolvedAssembly>;
@@ -29,16 +45,23 @@ export declare function resolveAssembly(input: AssemblyInput): Promise<ResolvedA
29
45
  * the same vocabulary the single-view one does — `['hg38', 'mm39']` as readily
30
46
  * as two hand-written configs.
31
47
  *
48
+ * A hub brings a track catalog and a search index that names hits by those
49
+ * tracks' ids, so both come back beside the assemblies. Pass the host's own
50
+ * `tracks` and `aggregateTextSearchAdapters` as `host` and they come back merged
51
+ * in, the host's winning an id, which makes spreading the result into
52
+ * `createApp`'s options correct:
53
+ *
54
+ * ```ts
55
+ * createApp(el, { ...options, ...(await resolveAssemblies(names, options)) })
56
+ * ```
57
+ *
58
+ * A key neither the hubs nor the host supplied is left out rather than set to
59
+ * `undefined`, so a spread never clears one.
60
+ *
32
61
  * Exported because the resolution is async and `createApp` is not: an engine
33
62
  * that resolved its own genomes would have to hand back a controller whose
34
- * `viewState` is undefined until the fetches land, which is most of what makes
35
- * the single-view controller four times the size. A host builds its options
63
+ * `viewState` is undefined until the fetches land. A host builds its options
36
64
  * asynchronously already (runtime plugins), so this belongs in that step.
37
- *
38
- * Without it every host reimplements it — jbrowse-anywidget carried a
39
- * line-for-line Python translation of core's hubUrl/fetchHub/addRelativeUris,
40
- * GenArk regex and all, purely because the multi-view product could not take a
41
- * hub name.
42
65
  */
43
- export declare function resolveAssemblies(inputs: AssemblyInput[]): Promise<ResolvedAssemblies>;
66
+ export declare function resolveAssemblies<Track extends object = HubTrackConfig, Index extends object = TextSearchAdapterConfig>(inputs: AssemblyInput[], host?: HostCatalog<Track, Index>): Promise<ResolvedAssemblies<Track, Index>>;
44
67
  export {};
@@ -1,5 +1,6 @@
1
1
  import { fetchHub } from '@jbrowse/core/util/fetchHub';
2
2
  import { isSequenceUri, makeAssembly } from '@jbrowse/core/util/makeAssembly';
3
+ import { withHostOverrides } from './controllerTracks.js';
3
4
  // A hub's config arrives off the network, so its search adapters are untyped
4
5
  // there. Keep the ones that carry the id everything downstream references,
5
6
  // rather than casting the lot: an entry without one cannot be referred to by a
@@ -8,12 +9,20 @@ function searchAdaptersOf(hub) {
8
9
  const found = (hub.aggregateTextSearchAdapters ?? []).filter((a) => typeof a.textSearchAdapterId === 'string');
9
10
  return found.length ? found : undefined;
10
11
  }
12
+ function tracksOf(hub) {
13
+ const found = (hub.tracks ?? []).filter((t) => typeof t.trackId === 'string');
14
+ return found.length ? found : undefined;
15
+ }
11
16
  function fromHubConfig(hub) {
12
17
  const assembly = hub.assemblies?.[0];
13
18
  if (!assembly) {
14
19
  throw new Error('hub config has no assemblies');
15
20
  }
16
- return { assembly, aggregateTextSearchAdapters: searchAdaptersOf(hub) };
21
+ return {
22
+ assembly,
23
+ aggregateTextSearchAdapters: searchAdaptersOf(hub),
24
+ tracks: tracksOf(hub),
25
+ };
17
26
  }
18
27
  /** One assembly in any of the four accepted shapes. */
19
28
  export async function resolveAssembly(input) {
@@ -34,22 +43,31 @@ export async function resolveAssembly(input) {
34
43
  * the same vocabulary the single-view one does — `['hg38', 'mm39']` as readily
35
44
  * as two hand-written configs.
36
45
  *
46
+ * A hub brings a track catalog and a search index that names hits by those
47
+ * tracks' ids, so both come back beside the assemblies. Pass the host's own
48
+ * `tracks` and `aggregateTextSearchAdapters` as `host` and they come back merged
49
+ * in, the host's winning an id, which makes spreading the result into
50
+ * `createApp`'s options correct:
51
+ *
52
+ * ```ts
53
+ * createApp(el, { ...options, ...(await resolveAssemblies(names, options)) })
54
+ * ```
55
+ *
56
+ * A key neither the hubs nor the host supplied is left out rather than set to
57
+ * `undefined`, so a spread never clears one.
58
+ *
37
59
  * Exported because the resolution is async and `createApp` is not: an engine
38
60
  * that resolved its own genomes would have to hand back a controller whose
39
- * `viewState` is undefined until the fetches land, which is most of what makes
40
- * the single-view controller four times the size. A host builds its options
61
+ * `viewState` is undefined until the fetches land. A host builds its options
41
62
  * asynchronously already (runtime plugins), so this belongs in that step.
42
- *
43
- * Without it every host reimplements it — jbrowse-anywidget carried a
44
- * line-for-line Python translation of core's hubUrl/fetchHub/addRelativeUris,
45
- * GenArk regex and all, purely because the multi-view product could not take a
46
- * hub name.
47
63
  */
48
- export async function resolveAssemblies(inputs) {
64
+ export async function resolveAssemblies(inputs, host = {}) {
49
65
  const resolved = await Promise.all(inputs.map(resolveAssembly));
50
- const adapters = resolved.flatMap(r => r.aggregateTextSearchAdapters ?? []);
66
+ const tracks = withHostOverrides(resolved.flatMap(r => r.tracks ?? []), host.tracks, 'trackId');
67
+ const adapters = withHostOverrides(resolved.flatMap(r => r.aggregateTextSearchAdapters ?? []), host.aggregateTextSearchAdapters, 'textSearchAdapterId');
51
68
  return {
52
69
  assemblies: resolved.map(r => r.assembly),
53
- aggregateTextSearchAdapters: adapters.length ? adapters : undefined,
70
+ ...(tracks.length ? { tracks } : {}),
71
+ ...(adapters.length ? { aggregateTextSearchAdapters: adapters } : {}),
54
72
  };
55
73
  }
@@ -1,11 +1,21 @@
1
1
  import { RpcServer } from '@jbrowse/core/util/librpc';
2
2
  import type { PluginConstructor } from '@jbrowse/core/Plugin';
3
- import type { LoadedPlugin } from '@jbrowse/core/PluginLoader';
3
+ import type { LoadedPlugin, ReExportRegistryLoader } from '@jbrowse/core/PluginLoader';
4
4
  declare global {
5
5
  interface Window {
6
6
  rpcServer?: RpcServer;
7
7
  }
8
8
  }
9
+ export interface WorkerOptions {
10
+ fetchESM?: (url: string) => Promise<LoadedPlugin>;
11
+ fetchCJS?: (url: string) => Promise<LoadedPlugin>;
12
+ /**
13
+ * The product's `workerReExports.generated.ts`, so a runtime plugin loading
14
+ * here reads the host's copy of every `@jbrowse` package the product
15
+ * bundles.
16
+ */
17
+ reExports: ReExportRegistryLoader;
18
+ }
9
19
  type RpcFunc = (args: unknown) => Promise<unknown>;
10
20
  /**
11
21
  * Wire the method's `statusCallback` to the status channel the driver opened
@@ -31,8 +41,5 @@ type RpcFunc = (args: unknown) => Promise<unknown>;
31
41
  * about the driver on the other side of postMessage that nothing else asserts.
32
42
  */
33
43
  export declare function wrapForRpc(func: RpcFunc): (args: unknown) => Promise<unknown>;
34
- export declare function initializeWorker(corePlugins: PluginConstructor[], opts: {
35
- fetchESM?: (url: string) => Promise<LoadedPlugin>;
36
- fetchCJS?: (url: string) => Promise<LoadedPlugin>;
37
- }): Promise<void>;
44
+ export declare function initializeWorker(corePlugins: PluginConstructor[], opts: WorkerOptions): Promise<void>;
38
45
  export {};
package/esm/rpcWorker.js CHANGED
@@ -31,7 +31,7 @@ async function getPluginManager(corePlugins, opts) {
31
31
  // engine — needs its definition here, not just its instance.
32
32
  const runtimePlugins = config.plugins.length
33
33
  ? await new PluginLoader(config.plugins, opts)
34
- .installGlobalReExports(self)
34
+ .installGlobalReExports(self, opts.reExports)
35
35
  .load(config.windowHref)
36
36
  : [];
37
37
  return new PluginManager([
@@ -12,7 +12,7 @@
12
12
  * LinearFeatureDisplay → LinearBasicDisplay
13
13
  *
14
14
  * Also lifts the per-instance alignments settings a v4.3.0 session persisted
15
- * (`colorBySetting`, `filterBySetting`, `jexlFilters`, `trackMaxHeight`,
15
+ * (`colorBySetting`, `filterBySetting`, `trackMaxHeight`,
16
16
  * `hideMismatchesSetting`) into the config where they now live as slots — off
17
17
  * the nested `LinearAlignmentsDisplay` container's `PileupDisplay` /
18
18
  * `SNPCoverageDisplay` sub-nodes and off a flat old display alike, see
@@ -35,10 +35,10 @@
35
35
  * membership is whether the key still works, not which file makes it work.
36
36
  *
37
37
  * Keyed rather than flat because the same name means different things on
38
- * different displays: `jexlFilters` is lifted for the alignments display and,
39
- * differently, for the two variant ones, while on a LinearBasicDisplay — whose
40
- * own prop has always been `jexlFiltersSetting` — it is simply dead, and a flat
41
- * list would call that one supported.
38
+ * different displays: `jexlFilters` still works on the two multi-sample variant
39
+ * ones, while on a LinearBasicDisplay — whose own prop has always been
40
+ * `jexlFiltersSetting` — and on the alignments display, which reads no filter
41
+ * slot at all, it is simply dead, and a flat list would call those supported.
42
42
  */
43
43
  export declare const MIGRATED_DISPLAY_INSTANCE_KEYS: Record<string, string[]>;
44
44
  /**
@@ -12,7 +12,7 @@
12
12
  * LinearFeatureDisplay → LinearBasicDisplay
13
13
  *
14
14
  * Also lifts the per-instance alignments settings a v4.3.0 session persisted
15
- * (`colorBySetting`, `filterBySetting`, `jexlFilters`, `trackMaxHeight`,
15
+ * (`colorBySetting`, `filterBySetting`, `trackMaxHeight`,
16
16
  * `hideMismatchesSetting`) into the config where they now live as slots — off
17
17
  * the nested `LinearAlignmentsDisplay` container's `PileupDisplay` /
18
18
  * `SNPCoverageDisplay` sub-nodes and off a flat old display alike, see
@@ -62,6 +62,58 @@ const displayTypeMap = {
62
62
  // modifications/methylation session opens colored `normal`). We pull them off
63
63
  // the instance here and route them into the config.
64
64
  const NESTED_ALIGNMENTS_SUBNODES = ['PileupDisplay', 'SNPCoverageDisplay'];
65
+ // The alignments `color` field each v4 scheme name paints now. `methylation`,
66
+ // `stranded` and `insertSizeGradient` were retired before the colour object
67
+ // and land on the fields that replaced them.
68
+ const V4_COLOR_FIELDS = {
69
+ strand: 'strand',
70
+ mappingQuality: 'mapq',
71
+ insertSize: 'insertSize',
72
+ insertSizeGradient: 'insertSize',
73
+ firstOfPairStrand: 'firstOfPairStrand',
74
+ stranded: 'firstOfPairStrand',
75
+ pairOrientation: 'pairOrientation',
76
+ insertSizeAndOrientation: 'insertSizeAndOrientation',
77
+ mateRefName: 'mateRefName',
78
+ };
79
+ // The v4 schemes that drew a cell per base, which are the `baseColor` object's
80
+ // field now rather than the read fill's.
81
+ const V4_BASE_COLOR_FIELDS = {
82
+ perBaseQuality: 'baseQuality',
83
+ perBaseLetter: 'base',
84
+ perBaseLettering: 'base',
85
+ modifications: 'modifications',
86
+ methylation: 'modifications',
87
+ bisulfite: 'bisulfite',
88
+ };
89
+ function colorSlotsOf(value) {
90
+ if (!isObject(value)) {
91
+ return {};
92
+ }
93
+ const { type, tag, modifications } = value;
94
+ const field = type === 'tag' && typeof tag === 'string'
95
+ ? `tags.${tag}`
96
+ : typeof type === 'string'
97
+ ? V4_COLOR_FIELDS[type]
98
+ : undefined;
99
+ const baseField = typeof type === 'string' ? V4_BASE_COLOR_FIELDS[type] : undefined;
100
+ const given = isObject(modifications) ? modifications : undefined;
101
+ const { isolatedModification, ...rest } = given ?? {};
102
+ const settings = given || type === 'methylation'
103
+ ? {
104
+ ...rest,
105
+ ...(typeof isolatedModification === 'string'
106
+ ? { shownModifications: [isolatedModification] }
107
+ : {}),
108
+ ...(type === 'methylation' ? { fillUnmarked: true } : {}),
109
+ }
110
+ : undefined;
111
+ return {
112
+ ...(field ? { color: { field } } : {}),
113
+ ...(baseField ? { baseColor: { field: baseField } } : {}),
114
+ ...(settings ? { modifications: settings } : {}),
115
+ };
116
+ }
65
117
  // Persisted key -> the slot it now lives in, and how to carry its value across.
66
118
  //
67
119
  // The `*Setting` names are the ones a real saved session carries, and getting
@@ -74,13 +126,19 @@ const NESTED_ALIGNMENTS_SUBNODES = ['PileupDisplay', 'SNPCoverageDisplay'];
74
126
  //
75
127
  // `hideSmallIndelsSetting` and `hideLargeIndelsSetting` are deliberately absent:
76
128
  // those two toggles have no slot today, the feature having been removed rather
77
- // than moved, so there is nowhere to carry them.
129
+ // than moved, so there is nowhere to carry them. `jexlFilters` is absent for the
130
+ // same reason since the slot left `baseLinearDisplayConfigSchema`: the
131
+ // alignments display never read it, so the slot this used to carry the value to
132
+ // no longer exists.
133
+ //
134
+ // `spread` carries one old key across as several slots: a v4 `colorBy` named a
135
+ // scheme and held the modification settings, which are the `color` or
136
+ // `baseColor` object's field and the `modifications` slot now.
78
137
  const MIGRATED_INSTANCE_SLOTS = {
79
- colorBy: { slot: 'colorBy' },
80
- colorBySetting: { slot: 'colorBy' },
138
+ colorBy: { slot: 'color', spread: colorSlotsOf },
139
+ colorBySetting: { slot: 'color', spread: colorSlotsOf },
81
140
  filterBy: { slot: 'filterBy' },
82
141
  filterBySetting: { slot: 'filterBy' },
83
- jexlFilters: { slot: 'jexlFilters' },
84
142
  trackMaxHeight: { slot: 'maxHeight' },
85
143
  hideMismatchesSetting: {
86
144
  slot: 'showMismatches',
@@ -102,10 +160,10 @@ const MIGRATED_INSTANCE_SLOTS = {
102
160
  * membership is whether the key still works, not which file makes it work.
103
161
  *
104
162
  * Keyed rather than flat because the same name means different things on
105
- * different displays: `jexlFilters` is lifted for the alignments display and,
106
- * differently, for the two variant ones, while on a LinearBasicDisplay — whose
107
- * own prop has always been `jexlFiltersSetting` — it is simply dead, and a flat
108
- * list would call that one supported.
163
+ * different displays: `jexlFilters` still works on the two multi-sample variant
164
+ * ones, while on a LinearBasicDisplay — whose own prop has always been
165
+ * `jexlFiltersSetting` — and on the alignments display, which reads no filter
166
+ * slot at all, it is simply dead, and a flat list would call those supported.
109
167
  */
110
168
  export const MIGRATED_DISPLAY_INSTANCE_KEYS = {
111
169
  '*': ['heightPreConfig'],
@@ -123,9 +181,14 @@ export const MIGRATED_DISPLAY_INSTANCE_KEYS = {
123
181
  // the display was actually writing.
124
182
  function migratedSettingsOf(source) {
125
183
  const settings = {};
126
- for (const [key, { slot, convert }] of Object.entries(MIGRATED_INSTANCE_SLOTS)) {
184
+ for (const [key, { slot, convert, spread }] of Object.entries(MIGRATED_INSTANCE_SLOTS)) {
127
185
  if (source[key] !== undefined) {
128
- settings[slot] = convert ? convert(source[key]) : source[key];
186
+ if (spread) {
187
+ Object.assign(settings, spread(source[key]));
188
+ }
189
+ else {
190
+ settings[slot] = convert ? convert(source[key]) : source[key];
191
+ }
129
192
  }
130
193
  }
131
194
  return settings;
@@ -133,12 +196,6 @@ function migratedSettingsOf(source) {
133
196
  function isObject(v) {
134
197
  return !!v && typeof v === 'object' && !Array.isArray(v);
135
198
  }
136
- // The standalone `methylation` colorBy scheme was removed; it is now the
137
- // `modifications` scheme with `fillUnmarked` set. It is NOT rewritten here: the
138
- // alignments model normalizes it at read time (`normalizeColorBy`, applied in
139
- // its `colorBy` getter), which covers every persistence path — instance slot and
140
- // config-file display default alike — so session migration deliberately leaves a
141
- // `methylation` colorBy untouched.
142
199
  function migrateDisplayType(display, trackConfigId, collected) {
143
200
  const entry = displayTypeMap[display.type];
144
201
  if (!entry) {
@@ -184,8 +241,10 @@ function migrateDisplayType(display, trackConfigId, collected) {
184
241
  *
185
242
  * The display's own keys are read only when it carries a `configuration`
186
243
  * reference, i.e. only for a session instance. A *config* display node reaches
187
- * this same function through `migrateConfigSnapshot`, and there `colorBy` and
188
- * friends are the live slots — lifting them would strip a working config.
244
+ * this same function through `migrateConfigSnapshot`, and there `filterBy` and
245
+ * friends are the live slots — lifting them would strip a working config. Its
246
+ * `colorBy` is the one key rewritten in place, onto the `color`, `baseColor`
247
+ * and `modifications` slots that replaced it.
189
248
  */
190
249
  function extractAlignmentsInstanceSettings(display, trackConfigId, collected) {
191
250
  if (display.type !== 'LinearAlignmentsDisplay') {
@@ -202,6 +261,10 @@ function extractAlignmentsInstanceSettings(display, trackConfigId, collected) {
202
261
  ...(subNode ? migratedSettingsOf(subNode) : {}),
203
262
  ...ownSettings,
204
263
  };
264
+ if (!isInstance && !subNode && display.colorBy !== undefined) {
265
+ const { colorBy, ...config } = display;
266
+ return { ...config, ...colorSlotsOf(colorBy) };
267
+ }
205
268
  if (!subNode && Object.keys(settings).length === 0) {
206
269
  return display;
207
270
  }
package/esm/sessionUrl.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ENCODED_PREFIX, fromUrlSafeB64, toUrlSafeB64, } from '@jbrowse/core/util';
2
- import { getShareableSessionSnapshot } from './Session/index.js';
2
+ import { getShareableSessionSnapshot } from './Session/shareableSnapshot.js';
3
3
  /**
4
4
  * Serialize a live session into a compact, URL-safe string for a query param or
5
5
  * hash fragment. Deflated then base64url-encoded, so it survives a URL intact
@@ -1,3 +1,4 @@
1
+ import type { HydratedForms } from './Session/hydratedForms.ts';
1
2
  import type { PluginDefinition } from '@jbrowse/core/pluginDefinitions';
2
3
  import type { IAnyStateTreeNode, IAnyType } from '@jbrowse/mobx-state-tree';
3
4
  export interface DroppedSessionNode {
@@ -48,6 +49,10 @@ export interface HostedBaseConfig {
48
49
  };
49
50
  [key: string]: unknown;
50
51
  }
52
+ export interface HostedBase {
53
+ config: HostedBaseConfig;
54
+ forms: HydratedForms;
55
+ }
51
56
  export interface WebExportPlan {
52
57
  strategy: 'hostedConfigBase' | 'selfContained';
53
58
  configUrl?: string;
@@ -61,7 +66,7 @@ export interface WebExportPlan {
61
66
  unavailableAccounts: string[];
62
67
  }
63
68
  export type SelfContainedReason = 'noSourceConfig' | 'baseUnreachable' | 'assembliesNotInBase';
64
- export declare function planWebExport(snapshot: WebExportInput, baseConfig?: HostedBaseConfig): WebExportPlan;
69
+ export declare function planWebExport(snapshot: WebExportInput, base?: HostedBase): WebExportPlan;
65
70
  export declare const DEFAULT_WEB_BASE_URL = "https://jbrowse.org/code/jb2/latest/";
66
71
  export declare function buildWebExportUrl(plan: Pick<WebExportPlan, 'webBaseUrl' | 'configUrl'>, sessionParam: string, options?: {
67
72
  password?: string;