@jbrowse/product-core 4.3.0 → 5.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/RootModel/BaseRootModel.d.ts +514 -323
- package/esm/RootModel/BaseRootModel.js +141 -14
- package/esm/RootModel/HierarchicalConfig.d.ts +30 -20
- package/esm/RootModel/HierarchicalConfig.js +36 -0
- package/esm/RootModel/InternetAccounts.d.ts +18 -1
- package/esm/RootModel/InternetAccounts.js +120 -22
- package/esm/RootModel/PreferencesConfig.d.ts +37 -0
- package/esm/RootModel/PreferencesConfig.js +75 -0
- package/esm/RootModel/createConfigModel.d.ts +257 -0
- package/esm/RootModel/createConfigModel.js +77 -0
- package/esm/RootModel/index.d.ts +10 -5
- package/esm/RootModel/index.js +10 -5
- package/esm/RootModel/menuItems.d.ts +24 -0
- package/esm/RootModel/menuItems.js +141 -0
- package/esm/Session/BaseSession.d.ts +285 -11
- package/esm/Session/BaseSession.js +383 -15
- package/esm/Session/Connections.d.ts +115 -8
- package/esm/Session/Connections.js +196 -74
- package/esm/Session/DrawerWidgets.d.ts +145 -13
- package/esm/Session/DrawerWidgets.js +219 -49
- package/esm/Session/MultipleViews.d.ts +1067 -1177
- package/esm/Session/MultipleViews.js +395 -64
- package/esm/Session/Preferences.d.ts +1389 -0
- package/esm/Session/Preferences.js +58 -0
- package/esm/Session/ReferenceManagement.d.ts +28 -3
- package/esm/Session/ReferenceManagement.js +56 -41
- package/esm/Session/SessionTracks.d.ts +1101 -1180
- package/esm/Session/SessionTracks.js +584 -34
- package/esm/Session/Themes.d.ts +136 -2
- package/esm/Session/Themes.js +267 -21
- package/esm/Session/TrackMenu.d.ts +88 -0
- package/esm/Session/TrackMenu.js +175 -0
- package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
- package/esm/Session/TrackMenuSessionMixin.js +42 -0
- package/esm/Session/Tracks.d.ts +945 -1159
- package/esm/Session/Tracks.js +229 -30
- package/esm/Session/assertSessionModel.d.ts +21 -0
- package/esm/Session/assertSessionModel.js +1 -0
- package/esm/Session/copyTrackSnapshot.d.ts +21 -0
- package/esm/Session/copyTrackSnapshot.js +20 -0
- package/esm/Session/developerMode.d.ts +7 -0
- package/esm/Session/developerMode.js +43 -0
- package/esm/Session/finalizeSession.d.ts +15 -0
- package/esm/Session/finalizeSession.js +28 -0
- package/esm/Session/hydratedForms.d.ts +16 -0
- package/esm/Session/hydratedForms.js +13 -0
- package/esm/Session/index.d.ts +27 -10
- package/esm/Session/index.js +16 -10
- package/esm/Session/pluginExtraTrackItems.d.ts +11 -0
- package/esm/Session/pluginExtraTrackItems.js +13 -0
- package/esm/Session/readdedTrackConf.d.ts +8 -0
- package/esm/Session/readdedTrackConf.js +24 -0
- package/esm/Session/shareableSnapshot.d.ts +23 -0
- package/esm/Session/shareableSnapshot.js +66 -0
- package/esm/Session/temporaryAssemblyTracks.d.ts +24 -0
- package/esm/Session/temporaryAssemblyTracks.js +37 -0
- package/esm/assertExtends.d.ts +55 -0
- package/esm/assertExtends.js +1 -0
- package/esm/controllerTracks.d.ts +76 -0
- package/esm/controllerTracks.js +125 -0
- package/esm/destroyViewState.d.ts +21 -0
- package/esm/destroyViewState.js +29 -0
- package/esm/embeddedController.d.ts +57 -0
- package/esm/embeddedController.js +100 -0
- package/esm/index.d.ts +32 -4
- package/esm/index.js +31 -4
- package/esm/loadPlugins.d.ts +38 -0
- package/esm/loadPlugins.js +45 -0
- package/esm/localFiles.d.ts +74 -0
- package/esm/localFiles.js +156 -0
- package/esm/observeSession.d.ts +55 -0
- package/esm/observeSession.js +90 -0
- package/esm/parallelChunkLoading.d.ts +30 -0
- package/esm/parallelChunkLoading.js +71 -0
- package/esm/pluginInput.d.ts +32 -0
- package/esm/pluginInput.js +19 -0
- package/esm/pruneUnbuildableNodes.d.ts +109 -0
- package/esm/pruneUnbuildableNodes.js +562 -0
- package/esm/resolveAssemblies.d.ts +68 -0
- package/esm/resolveAssemblies.js +77 -0
- package/esm/rpcWorker.d.ts +36 -4
- package/esm/rpcWorker.js +126 -20
- package/esm/sessionMigrations/index.d.ts +15 -0
- package/esm/sessionMigrations/index.js +178 -0
- package/esm/sessionUrl.d.ts +40 -0
- package/esm/sessionUrl.js +66 -0
- package/esm/sessionUtils.d.ts +74 -0
- package/esm/sessionUtils.js +599 -0
- package/esm/siblingCast.d.ts +14 -0
- package/esm/siblingCast.js +16 -0
- package/esm/snapshotUtils.d.ts +8 -0
- package/esm/snapshotUtils.js +12 -0
- package/esm/ui/AboutDialog.d.ts +8 -3
- package/esm/ui/AboutDialog.js +14 -5
- package/esm/ui/AboutDialogContents.d.ts +2 -6
- package/esm/ui/AboutDialogContents.js +13 -17
- package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
- package/esm/ui/AssemblyInfoPanel.js +63 -0
- package/esm/ui/DescriptionPanel.d.ts +3 -0
- package/esm/ui/DescriptionPanel.js +25 -0
- package/esm/ui/Drawer.d.ts +21 -0
- package/esm/ui/Drawer.js +60 -0
- package/esm/ui/DrawerControls.d.ts +5 -0
- package/esm/ui/DrawerControls.js +28 -0
- package/esm/ui/DrawerHeader.d.ts +6 -0
- package/esm/ui/DrawerHeader.js +36 -0
- package/esm/ui/DrawerWidget.d.ts +5 -0
- package/esm/ui/DrawerWidget.js +12 -0
- package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
- package/esm/ui/DrawerWidgetSelector.js +40 -0
- package/esm/ui/FileInfoPanel.d.ts +3 -6
- package/esm/ui/FileInfoPanel.js +36 -26
- package/esm/ui/GeneralPreferencesTab.d.ts +17 -0
- package/esm/ui/GeneralPreferencesTab.js +34 -0
- package/esm/ui/HeaderButtons.d.ts +2 -1
- package/esm/ui/HeaderButtons.js +15 -17
- package/esm/ui/ModalWidget.d.ts +13 -0
- package/esm/ui/ModalWidget.js +48 -0
- package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
- package/esm/ui/ModalWidgetAppBar.js +9 -0
- package/esm/ui/PreferenceCheckbox.d.ts +6 -0
- package/esm/ui/PreferenceCheckbox.js +16 -0
- package/esm/ui/PreferencesDialog.d.ts +34 -0
- package/esm/ui/PreferencesDialog.js +55 -0
- package/esm/ui/PreferencesResetDialog.d.ts +8 -0
- package/esm/ui/PreferencesResetDialog.js +19 -0
- package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
- package/esm/ui/RefNameAliasesDialog.js +65 -0
- package/esm/ui/RefNameInfoDialog.d.ts +3 -6
- package/esm/ui/RefNameInfoDialog.js +45 -63
- package/esm/ui/ViewPreferencesTab.d.ts +12 -0
- package/esm/ui/ViewPreferencesTab.js +15 -0
- package/esm/ui/WidgetBody.d.ts +18 -0
- package/esm/ui/WidgetBody.js +22 -0
- package/esm/ui/WidgetHeading.d.ts +7 -0
- package/esm/ui/WidgetHeading.js +7 -0
- package/esm/ui/aboutTestUtils.d.ts +140 -0
- package/esm/ui/aboutTestUtils.js +25 -0
- package/esm/ui/descriptionHtml.d.ts +25 -0
- package/esm/ui/descriptionHtml.js +64 -0
- package/esm/ui/drawerLayout.d.ts +18 -0
- package/esm/ui/drawerLayout.js +22 -0
- package/esm/ui/index.d.ts +15 -1
- package/esm/ui/index.js +13 -1
- package/esm/ui/preferencesReset.d.ts +18 -0
- package/esm/ui/preferencesReset.js +69 -0
- package/esm/ui/util.d.ts +22 -7
- package/esm/ui/util.js +19 -41
- package/esm/useEngineLifecycle.d.ts +19 -0
- package/esm/useEngineLifecycle.js +44 -0
- package/package.json +15 -18
- package/esm/RootModel/FormatAbout.d.ts +0 -12
- package/esm/RootModel/FormatAbout.js +0 -15
- package/esm/RootModel/FormatDetails.d.ts +0 -24
- package/esm/RootModel/FormatDetails.js +0 -27
- package/esm/Session/DialogQueue.d.ts +0 -17
- package/esm/Session/DialogQueue.js +0 -31
package/esm/index.js
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export { asRoot, asSession } from './siblingCast.js';
|
|
2
|
+
export { scheduleDetachedDestroy } from '@jbrowse/core/util';
|
|
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, hydratedForms, isBaseSession, isSession, isSessionWithConnections, isSessionWithDrawerWidgets, isSessionWithMultipleViews, isSessionWithReferenceManagement, isSessionWithThemes, isSessionWithTracks, pluginExtraTrackItems, storedThemeArgs, 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
|
+
export { initializeWorker } from './rpcWorker.js';
|
|
7
|
+
export { toPluginLoadRecord } from './pluginInput.js';
|
|
8
|
+
export { loadRuntimePlugins } from './loadPlugins.js';
|
|
9
|
+
export { decodeSessionFromUrl, encodeSessionToUrl } from './sessionUrl.js';
|
|
10
|
+
// in-memory files as tracks, for hosts whose data lives in a process rather
|
|
11
|
+
// than at a URL (a notebook kernel, an R session) — read by byte range, so an
|
|
12
|
+
// indexed file stays indexed
|
|
13
|
+
export { normalizeAdapterSnapshots, registerLocalFiles, resolveLocalFileUris, } from './localFiles.js';
|
|
14
|
+
export { searchIndexKey, withHostOverrides } from './controllerTracks.js';
|
|
15
|
+
// the lifecycle behind each single-view product's imperative controller
|
|
16
|
+
export { createEmbeddedController } from './embeddedController.js';
|
|
17
|
+
// hub names, sequence URIs and hub configs -> assembly configs, so every
|
|
18
|
+
// product takes the same assembly vocabulary
|
|
19
|
+
export { resolveAssemblies, resolveAssembly } from './resolveAssemblies.js';
|
|
20
|
+
// engine teardown, for a host that builds and discards engines — React unmount
|
|
21
|
+
// alone leaves the RPC workers and autoruns running
|
|
22
|
+
export { destroyViewState } from './destroyViewState.js';
|
|
23
|
+
// the two halves of "React owns this engine", both of which have a StrictMode
|
|
24
|
+
// trap in them that the obvious spelling walks straight into
|
|
25
|
+
export { useAsyncEngineLifecycle, useCreateOnce, useCreateOnceAsync, useDestroyOnUnmount, } from './useEngineLifecycle.js';
|
|
26
|
+
// the JS -> host direction of an embedded controller: where each view is
|
|
27
|
+
// looking, what got selected, and the layout as plain JSON
|
|
28
|
+
export { getSessionSnapshot, observeSession } from './observeSession.js';
|
|
29
|
+
export { migrateSessionSnapshot, migratedDisplayInstanceKeys, } from './sessionMigrations/index.js';
|
|
30
|
+
export { DEFAULT_WEB_BASE_URL, analyzeWebPortability, buildWebExportUrl, filterSessionInPlace, planWebExport, } from './sessionUtils.js';
|
|
31
|
+
export { describeUnbuildableNodes, pruneUnbuildableNodes, } from './pruneUnbuildableNodes.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LoadedPlugin, ReExportRegistryLoader } from '@jbrowse/core/PluginLoader';
|
|
2
|
+
import type { PluginDefinition } from '@jbrowse/core/pluginDefinitions';
|
|
3
|
+
export interface LoadPluginsArgs {
|
|
4
|
+
fetchESM?: (url: string) => Promise<LoadedPlugin>;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve relative plugin urls against this instead of the page. A config
|
|
7
|
+
* fetched from somewhere else names its plugins relative to itself, so pass
|
|
8
|
+
* that config's url — otherwise a `"url": "umd_plugin.js"` entry is looked
|
|
9
|
+
* for next to your own app and 404s.
|
|
10
|
+
*/
|
|
11
|
+
baseUri?: string;
|
|
12
|
+
/** @deprecated the pre-4.4 spelling of `baseUri` */
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The body of every embedded product's `loadPlugins`. Each product keeps its own
|
|
17
|
+
* one-line wrapper, because the doc comment on it is the published API surface
|
|
18
|
+
* and names that product's own entry points — but the behavior is here, so the
|
|
19
|
+
* two things that differed between the copies can't drift again: react-app's
|
|
20
|
+
* accepted no `baseUrl`, and only two of the three defaulted the base url at
|
|
21
|
+
* all.
|
|
22
|
+
*
|
|
23
|
+
* `dropVendored` is the one difference that is real, so it is a required
|
|
24
|
+
* argument rather than a default. A product that bundles the vendored plugins
|
|
25
|
+
* (see `vendoredPluginNames`) must drop a config's entry for them or it installs
|
|
26
|
+
* a second copy beside its own; a product that does not bundle them — the
|
|
27
|
+
* circular-genome-view build has neither MafViewer nor GWAS in its corePlugins —
|
|
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.
|
|
33
|
+
*/
|
|
34
|
+
export declare function loadRuntimePlugins(pluginDefinitions: PluginDefinition[], { dropVendored, jbrowseVersion, reExports, ...args }: LoadPluginsArgs & {
|
|
35
|
+
dropVendored: boolean;
|
|
36
|
+
jbrowseVersion: string;
|
|
37
|
+
reExports: ReExportRegistryLoader;
|
|
38
|
+
}): Promise<never[] | import("@jbrowse/core/PluginLoader").PluginRecord[]>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import PluginLoader from '@jbrowse/core/PluginLoader';
|
|
2
|
+
import { dropVendoredPlugins } from '@jbrowse/core/pluginDefinitions';
|
|
3
|
+
import { resolveStorePluginRefs } from '@jbrowse/core/util';
|
|
4
|
+
/**
|
|
5
|
+
* The body of every embedded product's `loadPlugins`. Each product keeps its own
|
|
6
|
+
* one-line wrapper, because the doc comment on it is the published API surface
|
|
7
|
+
* and names that product's own entry points — but the behavior is here, so the
|
|
8
|
+
* two things that differed between the copies can't drift again: react-app's
|
|
9
|
+
* accepted no `baseUrl`, and only two of the three defaulted the base url at
|
|
10
|
+
* all.
|
|
11
|
+
*
|
|
12
|
+
* `dropVendored` is the one difference that is real, so it is a required
|
|
13
|
+
* argument rather than a default. A product that bundles the vendored plugins
|
|
14
|
+
* (see `vendoredPluginNames`) must drop a config's entry for them or it installs
|
|
15
|
+
* a second copy beside its own; a product that does not bundle them — the
|
|
16
|
+
* circular-genome-view build has neither MafViewer nor GWAS in its corePlugins —
|
|
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.
|
|
22
|
+
*/
|
|
23
|
+
export async function loadRuntimePlugins(pluginDefinitions, { dropVendored, jbrowseVersion, reExports, ...args }) {
|
|
24
|
+
// Before dropVendoredPlugins, which matches on the UMD name a ref does not
|
|
25
|
+
// carry until the store supplies it. `jbrowseVersion` is required rather than
|
|
26
|
+
// defaulted so a product that forgets it fails to compile instead of quietly
|
|
27
|
+
// resolving every range against the wrong host.
|
|
28
|
+
const { definitions, failures } = await resolveStorePluginRefs(pluginDefinitions, jbrowseVersion);
|
|
29
|
+
// This path ends in `load`, which is all-or-nothing (see below) — so a ref
|
|
30
|
+
// that resolved to nothing is a failure now rather than a plugin the embedder
|
|
31
|
+
// silently never receives.
|
|
32
|
+
if (failures[0]) {
|
|
33
|
+
throw failures[0].error;
|
|
34
|
+
}
|
|
35
|
+
const toLoad = dropVendored ? dropVendoredPlugins(definitions) : definitions;
|
|
36
|
+
// the default matters: `new URL('umd_plugin.js', undefined)` throws, so a
|
|
37
|
+
// relative plugin url used to fail outright instead of resolving against the
|
|
38
|
+
// page the way every other relative url does
|
|
39
|
+
const base = args.baseUri ?? args.baseUrl;
|
|
40
|
+
return toLoad.length
|
|
41
|
+
? new PluginLoader(toLoad, args)
|
|
42
|
+
.installGlobalReExports(window, reExports)
|
|
43
|
+
.load(base ?? window.location.href)
|
|
44
|
+
: [];
|
|
45
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { BlobLocation } from '@jbrowse/core/util';
|
|
3
|
+
/**
|
|
4
|
+
* Bytes for one browser-local file, keyed by the name a track config refers to
|
|
5
|
+
* it by. `Blob`/`File` pass through; anything else (a `Uint8Array` from a
|
|
6
|
+
* notebook kernel's binary channel, an `ArrayBuffer` from a fetch) is wrapped.
|
|
7
|
+
*/
|
|
8
|
+
export type LocalFileInput = Record<string, Blob | BufferSource>;
|
|
9
|
+
/**
|
|
10
|
+
* Register in-memory bytes as browser-local files JBrowse can read, returning
|
|
11
|
+
* the `name -> BlobLocation` map {@link resolveLocalFileUris} substitutes with.
|
|
12
|
+
*
|
|
13
|
+
* This is the escape from "the data has to be on a web server first". A
|
|
14
|
+
* `BlobLocation` opens as a `BlobFile`, which serves byte ranges out of
|
|
15
|
+
* `Blob.slice()` — so a bgzipped+tabixed file, a BAM with its `.bai`, or a
|
|
16
|
+
* bigWig is *seeked into* exactly as if it were remote, and only the bytes the
|
|
17
|
+
* current view needs are ever touched. That is the whole point of doing this
|
|
18
|
+
* rather than inlining features into a config: a 200k-feature table is ~20MB of
|
|
19
|
+
* JSON to serialize, ship, and hold in memory, while the same data as a tabix
|
|
20
|
+
* file is a few MB the view reads a few KB of at a time.
|
|
21
|
+
*
|
|
22
|
+
* For hosts that hold their data in a process rather than at a URL — a notebook
|
|
23
|
+
* kernel, an R session, a desktop app — this is the way in, and it needs no web
|
|
24
|
+
* server, no CORS, and no temporary public bucket.
|
|
25
|
+
*/
|
|
26
|
+
export declare function registerLocalFiles(files: LocalFileInput): {
|
|
27
|
+
[k: string]: BlobLocation;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Fold newly-offered files into a controller's registered map, registering only
|
|
31
|
+
* the names it has not seen. Registering is what mints the blobIds, so a host
|
|
32
|
+
* re-stating its whole file set on every update — which is what a declarative
|
|
33
|
+
* host does — pays for each file once rather than once per update.
|
|
34
|
+
*
|
|
35
|
+
* Only ever adds: a name already registered keeps its existing blob, because a
|
|
36
|
+
* live track config points at that blobId and swapping it underneath would
|
|
37
|
+
* leave the track reading bytes nothing else references.
|
|
38
|
+
*/
|
|
39
|
+
export declare function mergeLocalFiles(current: Record<string, BlobLocation>, files: LocalFileInput): {
|
|
40
|
+
[x: string]: BlobLocation;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Expand every shorthand adapter snapshot (`{ type, uri }`) in a config to the
|
|
44
|
+
* canonical location keys its config schema declares, using that adapter type's
|
|
45
|
+
* own `normalizeSnapshot` — the hook that exists so "downstream code can read
|
|
46
|
+
* location keys without knowing each shorthand".
|
|
47
|
+
*
|
|
48
|
+
* {@link resolveLocalFileUris} has to run on the expanded form, and this is the
|
|
49
|
+
* difference between `localFiles` working and silently doing nothing: the
|
|
50
|
+
* shorthand puts `uri` on the *adapter*, so substituting there would replace
|
|
51
|
+
* `{ type: 'BamAdapter', uri: 'x.bam' }` with a bare BlobLocation and take the
|
|
52
|
+
* adapter's type with it. Expanding first moves the name onto `bamLocation`,
|
|
53
|
+
* where it is a location node and substitution is what it looks like. The
|
|
54
|
+
* expansion also derives the conventional index sibling (`.bai`/`.tbi`/`.crai`)
|
|
55
|
+
* from the same string, so registering `x.bam` and `x.bam.bai` is enough and no
|
|
56
|
+
* caller has to know which adapter wanted which file.
|
|
57
|
+
*
|
|
58
|
+
* Idempotent, and a no-op for an already-canonical config or an adapter type
|
|
59
|
+
* that declares no shorthand.
|
|
60
|
+
*/
|
|
61
|
+
export declare function normalizeAdapterSnapshots<T>(config: T, pluginManager: PluginManager): T;
|
|
62
|
+
/**
|
|
63
|
+
* Rewrite every `{ uri: <a registered name> }` in a track config to that file's
|
|
64
|
+
* `BlobLocation`, in place of nothing else — unregistered URIs are left alone,
|
|
65
|
+
* so a config can mix local and remote files freely.
|
|
66
|
+
*
|
|
67
|
+
* Runs on a config whose locations are already canonical, which is what
|
|
68
|
+
* `guessTrackConf` (for a loose track spec) and {@link normalizeAdapterSnapshots}
|
|
69
|
+
* (for a written-out one) each produce. So a host registers `peaks.bed.gz` and
|
|
70
|
+
* `peaks.bed.gz.tbi` under their plain names, the config names both under
|
|
71
|
+
* `bedGzLocation` and `index.location`, and this swaps both for blobs — index
|
|
72
|
+
* and all, with no host-side knowledge of which adapter wanted which sibling.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveLocalFileUris<T>(config: T, locations: Record<string, BlobLocation>): T;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { storeBlobLocation } from '@jbrowse/core/util';
|
|
2
|
+
/**
|
|
3
|
+
* Register in-memory bytes as browser-local files JBrowse can read, returning
|
|
4
|
+
* the `name -> BlobLocation` map {@link resolveLocalFileUris} substitutes with.
|
|
5
|
+
*
|
|
6
|
+
* This is the escape from "the data has to be on a web server first". A
|
|
7
|
+
* `BlobLocation` opens as a `BlobFile`, which serves byte ranges out of
|
|
8
|
+
* `Blob.slice()` — so a bgzipped+tabixed file, a BAM with its `.bai`, or a
|
|
9
|
+
* bigWig is *seeked into* exactly as if it were remote, and only the bytes the
|
|
10
|
+
* current view needs are ever touched. That is the whole point of doing this
|
|
11
|
+
* rather than inlining features into a config: a 200k-feature table is ~20MB of
|
|
12
|
+
* JSON to serialize, ship, and hold in memory, while the same data as a tabix
|
|
13
|
+
* file is a few MB the view reads a few KB of at a time.
|
|
14
|
+
*
|
|
15
|
+
* For hosts that hold their data in a process rather than at a URL — a notebook
|
|
16
|
+
* kernel, an R session, a desktop app — this is the way in, and it needs no web
|
|
17
|
+
* server, no CORS, and no temporary public bucket.
|
|
18
|
+
*/
|
|
19
|
+
export function registerLocalFiles(files) {
|
|
20
|
+
return Object.fromEntries(Object.entries(files).map(([name, data]) => [name, register(name, data)]));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Fold newly-offered files into a controller's registered map, registering only
|
|
24
|
+
* the names it has not seen. Registering is what mints the blobIds, so a host
|
|
25
|
+
* re-stating its whole file set on every update — which is what a declarative
|
|
26
|
+
* host does — pays for each file once rather than once per update.
|
|
27
|
+
*
|
|
28
|
+
* Only ever adds: a name already registered keeps its existing blob, because a
|
|
29
|
+
* live track config points at that blobId and swapping it underneath would
|
|
30
|
+
* leave the track reading bytes nothing else references.
|
|
31
|
+
*/
|
|
32
|
+
export function mergeLocalFiles(current, files) {
|
|
33
|
+
const fresh = Object.fromEntries(Object.entries(files).filter(([name]) => !current[name]));
|
|
34
|
+
return { ...current, ...registerLocalFiles(fresh) };
|
|
35
|
+
}
|
|
36
|
+
// Registering the same bytes twice used to mint a second blobId and leave the
|
|
37
|
+
// first in core's process-global blobMap forever. Nothing collects that map, so
|
|
38
|
+
// a host that hands the same `localFiles` to a new controller each time it
|
|
39
|
+
// rebuilds — which is how a host swaps genome or track set, now that the
|
|
40
|
+
// controller has no setters that rebuild internally — grew it without bound.
|
|
41
|
+
//
|
|
42
|
+
// Keyed on the caller's own object, which is the only identity available: bytes
|
|
43
|
+
// are not comparable cheaply, and two Uint8Arrays over the same buffer are
|
|
44
|
+
// legitimately different files. A WeakMap so a host dropping its reference to
|
|
45
|
+
// the data lets the entry go, and per name inside it because the same bytes can
|
|
46
|
+
// legitimately be registered under two names and a BlobLocation carries one.
|
|
47
|
+
const registered = new WeakMap();
|
|
48
|
+
function register(name, data) {
|
|
49
|
+
const byName = registered.get(data) ?? new Map();
|
|
50
|
+
const seen = byName.get(name);
|
|
51
|
+
if (seen) {
|
|
52
|
+
return seen;
|
|
53
|
+
}
|
|
54
|
+
const location = storeBlobLocation({
|
|
55
|
+
blob: data instanceof File ? data : new File([data], name),
|
|
56
|
+
});
|
|
57
|
+
byName.set(name, location);
|
|
58
|
+
registered.set(data, byName);
|
|
59
|
+
return location;
|
|
60
|
+
}
|
|
61
|
+
// A location node is `{ uri }` and at most the two keys that travel with it.
|
|
62
|
+
// Anything else carrying a `uri` is a shorthand adapter (`{ type, uri }`), and
|
|
63
|
+
// telling the two apart is what stops resolveLocalFileUris replacing a whole
|
|
64
|
+
// adapter with a bare location — see its comment.
|
|
65
|
+
const LOCATION_KEYS = new Set(['uri', 'baseUri', 'locationType']);
|
|
66
|
+
function isLocationNode(record) {
|
|
67
|
+
return Object.keys(record).every(key => LOCATION_KEYS.has(key));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Expand every shorthand adapter snapshot (`{ type, uri }`) in a config to the
|
|
71
|
+
* canonical location keys its config schema declares, using that adapter type's
|
|
72
|
+
* own `normalizeSnapshot` — the hook that exists so "downstream code can read
|
|
73
|
+
* location keys without knowing each shorthand".
|
|
74
|
+
*
|
|
75
|
+
* {@link resolveLocalFileUris} has to run on the expanded form, and this is the
|
|
76
|
+
* difference between `localFiles` working and silently doing nothing: the
|
|
77
|
+
* shorthand puts `uri` on the *adapter*, so substituting there would replace
|
|
78
|
+
* `{ type: 'BamAdapter', uri: 'x.bam' }` with a bare BlobLocation and take the
|
|
79
|
+
* adapter's type with it. Expanding first moves the name onto `bamLocation`,
|
|
80
|
+
* where it is a location node and substitution is what it looks like. The
|
|
81
|
+
* expansion also derives the conventional index sibling (`.bai`/`.tbi`/`.crai`)
|
|
82
|
+
* from the same string, so registering `x.bam` and `x.bam.bai` is enough and no
|
|
83
|
+
* caller has to know which adapter wanted which file.
|
|
84
|
+
*
|
|
85
|
+
* Idempotent, and a no-op for an already-canonical config or an adapter type
|
|
86
|
+
* that declares no shorthand.
|
|
87
|
+
*/
|
|
88
|
+
export function normalizeAdapterSnapshots(config, pluginManager) {
|
|
89
|
+
if (Array.isArray(config)) {
|
|
90
|
+
return config.map(item => normalizeAdapterSnapshots(item, pluginManager));
|
|
91
|
+
}
|
|
92
|
+
if (typeof config === 'object' && config !== null) {
|
|
93
|
+
const record = config;
|
|
94
|
+
// `type` names a track, a display and an adapter alike, so this asks
|
|
95
|
+
// whether the name is an adapter rather than demanding that it is —
|
|
96
|
+
// getAdapterType throws on a miss, and 'AlignmentsTrack' is a miss
|
|
97
|
+
const normalize = typeof record.type === 'string' &&
|
|
98
|
+
pluginManager.hasAdapterType(record.type)
|
|
99
|
+
? pluginManager.getAdapterType(record.type).normalizeSnapshot
|
|
100
|
+
: undefined;
|
|
101
|
+
const normalized = normalize?.(record) ?? record;
|
|
102
|
+
// Every normalizeSnapshot returns its argument unchanged when there was no
|
|
103
|
+
// shorthand to expand, so identity is the signal that `uri` was consumed —
|
|
104
|
+
// and it then has to GO. The same function runs again as the config
|
|
105
|
+
// schema's preProcessSnapshot when MST builds the tree, and a surviving
|
|
106
|
+
// `uri` makes that second pass rebuild the location from the string,
|
|
107
|
+
// overwriting the BlobLocation just substituted into it. That failure is
|
|
108
|
+
// invisible: the track ends up pointing at a relative URL named
|
|
109
|
+
// `volvox-sorted.bam`, which 404s against the host page's own origin.
|
|
110
|
+
const expanded = normalized === record
|
|
111
|
+
? record
|
|
112
|
+
: Object.fromEntries(Object.entries(normalized).filter(([key]) => key !== 'uri'));
|
|
113
|
+
return Object.fromEntries(Object.entries(expanded).map(([key, value]) => [
|
|
114
|
+
key,
|
|
115
|
+
normalizeAdapterSnapshots(value, pluginManager),
|
|
116
|
+
]));
|
|
117
|
+
}
|
|
118
|
+
return config;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Rewrite every `{ uri: <a registered name> }` in a track config to that file's
|
|
122
|
+
* `BlobLocation`, in place of nothing else — unregistered URIs are left alone,
|
|
123
|
+
* so a config can mix local and remote files freely.
|
|
124
|
+
*
|
|
125
|
+
* Runs on a config whose locations are already canonical, which is what
|
|
126
|
+
* `guessTrackConf` (for a loose track spec) and {@link normalizeAdapterSnapshots}
|
|
127
|
+
* (for a written-out one) each produce. So a host registers `peaks.bed.gz` and
|
|
128
|
+
* `peaks.bed.gz.tbi` under their plain names, the config names both under
|
|
129
|
+
* `bedGzLocation` and `index.location`, and this swaps both for blobs — index
|
|
130
|
+
* and all, with no host-side knowledge of which adapter wanted which sibling.
|
|
131
|
+
*/
|
|
132
|
+
export function resolveLocalFileUris(config, locations) {
|
|
133
|
+
if (Array.isArray(config)) {
|
|
134
|
+
return config.map(item => resolveLocalFileUris(item, locations));
|
|
135
|
+
}
|
|
136
|
+
if (typeof config === 'object' && config !== null) {
|
|
137
|
+
const record = config;
|
|
138
|
+
const uri = record.uri;
|
|
139
|
+
// the whole location node is replaced: a BlobLocation carries blobId and
|
|
140
|
+
// its own locationType, and leaving the old uri alongside would make it
|
|
141
|
+
// fail isBlobLocation's discrimination. Only a *location* node, though — a
|
|
142
|
+
// shorthand adapter also has a `uri`, and replacing that node discards the
|
|
143
|
+
// adapter's type and every sibling key, which the config schema then papers
|
|
144
|
+
// over with its `/path/to/my.bam` default: a track that loads nothing, with
|
|
145
|
+
// nothing logged. normalizeAdapterSnapshots is what turns the shorthand
|
|
146
|
+
// into a location node so this substitutes it.
|
|
147
|
+
if (typeof uri === 'string' && locations[uri] && isLocationNode(record)) {
|
|
148
|
+
return locations[uri];
|
|
149
|
+
}
|
|
150
|
+
return Object.fromEntries(Object.entries(record).map(([key, value]) => [
|
|
151
|
+
key,
|
|
152
|
+
resolveLocalFileUris(value, locations),
|
|
153
|
+
]));
|
|
154
|
+
}
|
|
155
|
+
return config;
|
|
156
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { SessionSnapshot } from './sessionUrl.ts';
|
|
2
|
+
type OpenTracks = {
|
|
3
|
+
configuration: {
|
|
4
|
+
trackId: string;
|
|
5
|
+
};
|
|
6
|
+
}[];
|
|
7
|
+
interface ObservedView {
|
|
8
|
+
id?: string;
|
|
9
|
+
coarseVisibleLocStrings?: string;
|
|
10
|
+
views?: {
|
|
11
|
+
coarseVisibleLocStrings?: string;
|
|
12
|
+
}[];
|
|
13
|
+
tracks?: OpenTracks;
|
|
14
|
+
levels?: {
|
|
15
|
+
tracks: OpenTracks;
|
|
16
|
+
}[];
|
|
17
|
+
}
|
|
18
|
+
export interface ObservedSession {
|
|
19
|
+
name: string;
|
|
20
|
+
views: ObservedView[];
|
|
21
|
+
selection?: unknown;
|
|
22
|
+
}
|
|
23
|
+
interface ObservedRoot {
|
|
24
|
+
session?: ObservedSession;
|
|
25
|
+
}
|
|
26
|
+
/** where one view is looking: a locstring, or one per panel for a comparative view */
|
|
27
|
+
export type ViewLocation = string | string[] | undefined;
|
|
28
|
+
export interface SessionObservers {
|
|
29
|
+
/** fires with every view's visible region when the layout settles */
|
|
30
|
+
onLocationChange?: (locations: ViewLocation[]) => void;
|
|
31
|
+
/** fires with the serialized feature when one is selected in any view */
|
|
32
|
+
onFeatureSelect?: (feature: unknown) => void;
|
|
33
|
+
/**
|
|
34
|
+
* fires with a plain-JSON snapshot of the layout the user built, in the shape
|
|
35
|
+
* the `session` option takes — so "save this arrangement" is storing this
|
|
36
|
+
* value and reopening it is passing it back
|
|
37
|
+
*/
|
|
38
|
+
onSessionChange?: (session: SessionSnapshot) => void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The live session as a plain JSON snapshot, ready to hand straight back as a
|
|
42
|
+
* product's `session` option (or its controller's `setSession`). The
|
|
43
|
+
* uncompressed twin of `encodeSession`, for hosts that move JSON rather than
|
|
44
|
+
* URLs — a notebook kernel, an R session, a "save this layout" button.
|
|
45
|
+
*
|
|
46
|
+
* Not a plain `getSnapshot`, and for the same reason `encodeSession` isn't: a
|
|
47
|
+
* workspace arrangement turned on by the sender's preference is not in the
|
|
48
|
+
* snapshot, so a raw one replays as a classic stack for whoever opens it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getSessionSnapshot(session: ObservedSession): SessionSnapshot;
|
|
51
|
+
/**
|
|
52
|
+
* Wire the read-backs a host asked for, returning one disposer for all of them.
|
|
53
|
+
*/
|
|
54
|
+
export declare function observeSession(root: ObservedRoot, { onLocationChange, onFeatureSelect, onSessionChange }: SessionObservers): () => void;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isFeature } from '@jbrowse/core/util';
|
|
2
|
+
import { autorun, untracked } from 'mobx';
|
|
3
|
+
import { getShareableSessionSnapshot } from './Session/shareableSnapshot.js';
|
|
4
|
+
/**
|
|
5
|
+
* The live session as a plain JSON snapshot, ready to hand straight back as a
|
|
6
|
+
* product's `session` option (or its controller's `setSession`). The
|
|
7
|
+
* uncompressed twin of `encodeSession`, for hosts that move JSON rather than
|
|
8
|
+
* URLs — a notebook kernel, an R session, a "save this layout" button.
|
|
9
|
+
*
|
|
10
|
+
* Not a plain `getSnapshot`, and for the same reason `encodeSession` isn't: a
|
|
11
|
+
* workspace arrangement turned on by the sender's preference is not in the
|
|
12
|
+
* snapshot, so a raw one replays as a classic stack for whoever opens it.
|
|
13
|
+
*/
|
|
14
|
+
export function getSessionSnapshot(session) {
|
|
15
|
+
// `name` is restated rather than the whole thing cast: the bake returns an
|
|
16
|
+
// open record, but the model guarantees the session has one
|
|
17
|
+
return {
|
|
18
|
+
...getShareableSessionSnapshot(session),
|
|
19
|
+
name: session.name,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// A linear view reports its visible region as one coarseVisibleLocStrings
|
|
23
|
+
// string; a comparative view (synteny/dotplot) has no single string, so report
|
|
24
|
+
// one entry per panel instead.
|
|
25
|
+
function viewLocation(view) {
|
|
26
|
+
return typeof view.coarseVisibleLocStrings === 'string'
|
|
27
|
+
? view.coarseVisibleLocStrings || undefined
|
|
28
|
+
: view.views
|
|
29
|
+
?.map(v => v.coarseVisibleLocStrings)
|
|
30
|
+
.filter(loc => loc !== undefined);
|
|
31
|
+
}
|
|
32
|
+
// Every trackId a view has open. A synteny view keeps its tracks per level
|
|
33
|
+
// rather than in one flat list, so reading `tracks` alone reports none of them —
|
|
34
|
+
// and the read-back then never fired when a synteny track was opened.
|
|
35
|
+
function viewTracks(view) {
|
|
36
|
+
return (view.tracks ?? view.levels?.flatMap(l => l.tracks) ?? [])
|
|
37
|
+
.map(t => t.configuration.trackId)
|
|
38
|
+
.join(',');
|
|
39
|
+
}
|
|
40
|
+
// The coarse signal the read-backs ride on: which views exist, what each has
|
|
41
|
+
// open, and where each is looking. Deliberately NOT the whole snapshot —
|
|
42
|
+
// depending on that would pull in offsetPx, and a snapshot is kilobytes, so a
|
|
43
|
+
// drag would push one per pointer event across the host's wire.
|
|
44
|
+
// coarseVisibleLocStrings is JBrowse's own debounced location, so this settles
|
|
45
|
+
// after a pan rather than firing during it.
|
|
46
|
+
//
|
|
47
|
+
// A plain function, not an action: actions run untracked, and computing this is
|
|
48
|
+
// precisely what subscribes the autorun below.
|
|
49
|
+
function layoutSignal(views) {
|
|
50
|
+
return views
|
|
51
|
+
.map(v => `${v.id}:${JSON.stringify(viewLocation(v))}:${viewTracks(v)}`)
|
|
52
|
+
.join('|');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Wire the read-backs a host asked for, returning one disposer for all of them.
|
|
56
|
+
*/
|
|
57
|
+
export function observeSession(root, { onLocationChange, onFeatureSelect, onSessionChange }) {
|
|
58
|
+
const disposers = [];
|
|
59
|
+
// `session` is read inside each autorun and never captured: setSession
|
|
60
|
+
// replaces the whole node (`self.session = cast(...)` in BaseRootModel), so a
|
|
61
|
+
// captured one is a dead MST node the moment a session is restored, and the
|
|
62
|
+
// read-back would go silent for the rest of the product's life.
|
|
63
|
+
if (onLocationChange ?? onSessionChange) {
|
|
64
|
+
disposers.push(autorun(() => {
|
|
65
|
+
const { session } = root;
|
|
66
|
+
if (!session) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// the only dependency this autorun should have
|
|
70
|
+
layoutSignal(session.views);
|
|
71
|
+
onLocationChange?.(session.views.map(viewLocation));
|
|
72
|
+
// untracked, so reading the snapshot adds no dependencies of its own
|
|
73
|
+
// eslint-disable-next-line no-restricted-syntax -- effect input: the callback consumes the snapshot, the views are the trigger
|
|
74
|
+
onSessionChange?.(untracked(() => getSessionSnapshot(session)));
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
if (onFeatureSelect) {
|
|
78
|
+
disposers.push(autorun(() => {
|
|
79
|
+
const selection = root.session?.selection;
|
|
80
|
+
if (isFeature(selection)) {
|
|
81
|
+
onFeatureSelect(selection.toJSON());
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
return () => {
|
|
86
|
+
for (const dispose of disposers) {
|
|
87
|
+
dispose();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type ChunkId = string | number;
|
|
2
|
+
type ChunkHandler = (chunkId: ChunkId, promises: Promise<unknown>[]) => void;
|
|
3
|
+
export interface WebpackChunkRuntime {
|
|
4
|
+
f?: Record<string, ChunkHandler | undefined>;
|
|
5
|
+
}
|
|
6
|
+
export interface WorkerScope {
|
|
7
|
+
importScripts: (url: string) => void;
|
|
8
|
+
location: {
|
|
9
|
+
href: string;
|
|
10
|
+
};
|
|
11
|
+
fetch: (url: string) => Promise<{
|
|
12
|
+
arrayBuffer: () => Promise<unknown>;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Download the chunks of a dynamic import together in a classic web worker.
|
|
17
|
+
*
|
|
18
|
+
* Webpack's worker chunk handler (`f.i`) runs `importScripts` for one chunk at
|
|
19
|
+
* a time, and `importScripts` blocks until its file has arrived, so an import
|
|
20
|
+
* spanning 26 chunks waits on 26 round trips in a row. This handler asks the
|
|
21
|
+
* original which url it would load (none, if the chunk is installed), fetches
|
|
22
|
+
* it, and runs `importScripts` once the fetch lands, by then a cache read. A
|
|
23
|
+
* chunk file only registers module factories, so install order is free.
|
|
24
|
+
*
|
|
25
|
+
* Only same-origin http(s) chunks are prefetched: another origin would need
|
|
26
|
+
* CORS for `fetch`, and a `file:` worker (Desktop) has no round trip to save.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parallelizeChunkLoading(runtime: WebpackChunkRuntime | undefined, scope: WorkerScope): void;
|
|
29
|
+
export declare function parallelizeWorkerChunkLoading(): void;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Download the chunks of a dynamic import together in a classic web worker.
|
|
3
|
+
*
|
|
4
|
+
* Webpack's worker chunk handler (`f.i`) runs `importScripts` for one chunk at
|
|
5
|
+
* a time, and `importScripts` blocks until its file has arrived, so an import
|
|
6
|
+
* spanning 26 chunks waits on 26 round trips in a row. This handler asks the
|
|
7
|
+
* original which url it would load (none, if the chunk is installed), fetches
|
|
8
|
+
* it, and runs `importScripts` once the fetch lands, by then a cache read. A
|
|
9
|
+
* chunk file only registers module factories, so install order is free.
|
|
10
|
+
*
|
|
11
|
+
* Only same-origin http(s) chunks are prefetched: another origin would need
|
|
12
|
+
* CORS for `fetch`, and a `file:` worker (Desktop) has no round trip to save.
|
|
13
|
+
*/
|
|
14
|
+
export function parallelizeChunkLoading(runtime, scope) {
|
|
15
|
+
const handlers = runtime?.f;
|
|
16
|
+
const original = handlers?.i;
|
|
17
|
+
if (!handlers || !original) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const { origin, protocol } = new URL(scope.location.href);
|
|
21
|
+
if (protocol !== 'http:' && protocol !== 'https:') {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const importScripts = scope.importScripts.bind(scope);
|
|
25
|
+
const loading = new Map();
|
|
26
|
+
const urlOriginalWouldLoad = (chunkId, promises) => {
|
|
27
|
+
let url;
|
|
28
|
+
scope.importScripts = (u) => {
|
|
29
|
+
url = u;
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
original(chunkId, promises);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
scope.importScripts = importScripts;
|
|
36
|
+
}
|
|
37
|
+
return url;
|
|
38
|
+
};
|
|
39
|
+
handlers.i = (chunkId, promises) => {
|
|
40
|
+
const url = urlOriginalWouldLoad(chunkId, promises);
|
|
41
|
+
if (url === undefined) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (new URL(url, scope.location.href).origin !== origin) {
|
|
45
|
+
importScripts(url);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let pending = loading.get(url);
|
|
49
|
+
if (!pending) {
|
|
50
|
+
pending = scope
|
|
51
|
+
.fetch(url)
|
|
52
|
+
.then(r => r.arrayBuffer())
|
|
53
|
+
.then(() => { }, () => { })
|
|
54
|
+
.then(() => {
|
|
55
|
+
importScripts(url);
|
|
56
|
+
});
|
|
57
|
+
pending.catch(() => {
|
|
58
|
+
loading.delete(url);
|
|
59
|
+
});
|
|
60
|
+
loading.set(url, pending);
|
|
61
|
+
}
|
|
62
|
+
promises.push(pending);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function parallelizeWorkerChunkLoading() {
|
|
66
|
+
parallelizeChunkLoading(
|
|
67
|
+
// eslint-disable-next-line unicorn/no-typeof-undefined -- undeclared outside webpack, so a direct read throws
|
|
68
|
+
typeof __webpack_require__ === 'undefined'
|
|
69
|
+
? undefined
|
|
70
|
+
: __webpack_require__, globalThis);
|
|
71
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PluginConstructor } from '@jbrowse/core/Plugin';
|
|
2
|
+
import type { PluginRecord } from '@jbrowse/core/PluginLoader';
|
|
3
|
+
/**
|
|
4
|
+
* A plugin an embedded product's caller can register: either the class itself,
|
|
5
|
+
* or the `{ plugin, definition }` record `loadPlugins` returns.
|
|
6
|
+
*
|
|
7
|
+
* Prefer passing the record straight through. The `definition` is what lets the
|
|
8
|
+
* RPC worker load the same plugin on its side — hand it only the class and the
|
|
9
|
+
* plugin exists on the main thread but not in the worker, so anything it
|
|
10
|
+
* contributes that runs there (an adapter, most commonly) fails to resolve.
|
|
11
|
+
*/
|
|
12
|
+
export type PluginInput = PluginConstructor | PluginRecord;
|
|
13
|
+
/**
|
|
14
|
+
* Turn caller-supplied plugins into `PluginManager` load records, keeping each
|
|
15
|
+
* runtime plugin's `definition` when it has one.
|
|
16
|
+
*
|
|
17
|
+
* That definition is the whole point: `PluginManager.addPlugin` only records
|
|
18
|
+
* `runtimePluginDefinitions` for records that carry one, and `RpcManager` ships
|
|
19
|
+
* that list to the RPC worker as its boot config so the worker can fetch the
|
|
20
|
+
* same plugin. Dropping it leaves the plugin main-thread-only, and the failure
|
|
21
|
+
* surfaces far away — as an unknown adapter/track type inside the worker.
|
|
22
|
+
*
|
|
23
|
+
* Shared by every embedded product rather than restated, because getting it
|
|
24
|
+
* wrong is invisible until someone combines a runtime plugin with a worker.
|
|
25
|
+
*/
|
|
26
|
+
export declare function toPluginLoadRecord(p: PluginInput): {
|
|
27
|
+
plugin: import("@jbrowse/core/Plugin").default;
|
|
28
|
+
definition?: undefined;
|
|
29
|
+
} | {
|
|
30
|
+
plugin: import("@jbrowse/core/Plugin").default;
|
|
31
|
+
definition: import("@jbrowse/core/pluginDefinitions").PluginDefinition;
|
|
32
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn caller-supplied plugins into `PluginManager` load records, keeping each
|
|
3
|
+
* runtime plugin's `definition` when it has one.
|
|
4
|
+
*
|
|
5
|
+
* That definition is the whole point: `PluginManager.addPlugin` only records
|
|
6
|
+
* `runtimePluginDefinitions` for records that carry one, and `RpcManager` ships
|
|
7
|
+
* that list to the RPC worker as its boot config so the worker can fetch the
|
|
8
|
+
* same plugin. Dropping it leaves the plugin main-thread-only, and the failure
|
|
9
|
+
* surfaces far away — as an unknown adapter/track type inside the worker.
|
|
10
|
+
*
|
|
11
|
+
* Shared by every embedded product rather than restated, because getting it
|
|
12
|
+
* wrong is invisible until someone combines a runtime plugin with a worker.
|
|
13
|
+
*/
|
|
14
|
+
export function toPluginLoadRecord(p) {
|
|
15
|
+
// a plugin class is a function; a loadPlugins record is a plain object
|
|
16
|
+
return typeof p === 'function'
|
|
17
|
+
? { plugin: new p() }
|
|
18
|
+
: { plugin: new p.plugin(), definition: p.definition };
|
|
19
|
+
}
|