@jbrowse/core 5.0.0-beta.1 → 5.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +2 -2
- package/esm/PluginLoader.d.ts +1 -0
- package/esm/PluginLoader.js +52 -8
- package/esm/PluginManager.d.ts +70 -17
- package/esm/PluginManager.js +151 -20
- package/esm/ReExports/modules.js +3 -53
- package/esm/ReExports/publicUtil.d.ts +1 -1
- package/esm/ReExports/publicUtil.js +1 -1
- package/esm/ReExports/sharedModules.d.ts +96 -0
- package/esm/ReExports/sharedModules.js +59 -0
- package/esm/ReExports/uiStub.d.ts +1 -0
- package/esm/ReExports/uiStub.js +13 -0
- package/esm/ReExports/workerModules.d.ts +2 -0
- package/esm/ReExports/workerModules.js +10 -0
- package/esm/configuration/configurationSchema.d.ts +5 -38
- package/esm/configuration/configurationSchema.js +53 -40
- package/esm/configuration/promotableDefaults.js +12 -6
- package/esm/configuration/promotableResolve.d.ts +1 -1
- package/esm/configuration/promotableResolve.js +1 -1
- package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +12 -0
- package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +39 -0
- package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +29 -0
- package/esm/data_adapters/BaseAdapter/featureDensity.js +17 -0
- package/esm/data_adapters/BaseAdapter/index.d.ts +1 -0
- package/esm/data_adapters/BaseAdapter/index.js +1 -0
- package/esm/data_adapters/BaseAdapter/stats.d.ts +33 -1
- package/esm/data_adapters/BaseAdapter/stats.js +52 -13
- package/esm/data_adapters/BaseAdapter/types.d.ts +1 -0
- package/esm/pluggableElementTypes/DisplayType.d.ts +9 -10
- package/esm/pluggableElementTypes/DisplayType.js +43 -8
- package/esm/pluggableElementTypes/ViewType.d.ts +25 -2
- package/esm/pluggableElementTypes/ViewType.js +75 -2
- package/esm/pluggableElementTypes/extendElementType.js +8 -3
- package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +41 -0
- package/esm/pluggableElementTypes/models/BaseDisplayModel.js +74 -1
- package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +21 -0
- package/esm/pluggableElementTypes/models/BaseTrackModel.js +37 -2
- package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +49 -0
- package/esm/pluggableElementTypes/models/BaseViewModel.js +39 -0
- package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +10 -7
- package/esm/pluggableElementTypes/models/assertDisplayContract.js +23 -30
- package/esm/rpc/RpcRegistry.d.ts +10 -0
- package/esm/rpc/byteBudget.d.ts +9 -0
- package/esm/rpc/coreRpcMethods.d.ts +1 -0
- package/esm/rpc/coreRpcMethods.js +1 -0
- package/esm/rpc/methods/CoreGetFeatureDensity.d.ts +11 -0
- package/esm/rpc/methods/CoreGetFeatureDensity.js +22 -0
- package/esm/svg/svgReady.d.ts +1 -0
- package/esm/ui/CascadingMenu.js +30 -8
- package/esm/ui/LoadingOverlay.d.ts +14 -0
- package/esm/ui/LoadingOverlay.js +1 -1
- package/esm/ui/MenuTypes.d.ts +11 -2
- package/esm/ui/MenuTypes.js +3 -0
- package/esm/ui/PaletteContext.d.ts +11 -4
- package/esm/ui/PaletteContext.js +34 -5
- package/esm/ui/PinAdornment.js +12 -6
- package/esm/ui/index.d.ts +1 -1
- package/esm/ui/index.js +1 -1
- package/esm/ui/launchViewMenu.js +7 -1
- package/esm/ui/legendMenuItem.d.ts +5 -5
- package/esm/ui/legendMenuItem.js +5 -5
- package/esm/ui/makeSizeMenu.js +1 -1
- package/esm/ui/menuItems.d.ts +1 -1
- package/esm/ui/menuItems.js +1 -1
- package/esm/ui/promotableMenuItems.js +8 -6
- package/esm/ui/promotablePinCoverage.js +2 -1
- package/esm/ui/toggleMenuItems.d.ts +1 -3
- package/esm/util/addAndShowTrack.d.ts +1 -1
- package/esm/util/addAndShowTrack.js +5 -1
- package/esm/util/addTrackFromWidget.js +2 -2
- package/esm/util/cacheBudgets.d.ts +22 -19
- package/esm/util/cacheBudgets.js +22 -19
- package/esm/util/createStopTokenRotation.d.ts +1 -0
- package/esm/util/createStopTokenRotation.js +7 -27
- package/esm/util/fetchContext.d.ts +2 -2
- package/esm/util/fetchContext.js +2 -2
- package/esm/util/fetchPhases.d.ts +9 -9
- package/esm/util/hooks.d.ts +8 -0
- package/esm/util/hooks.js +27 -0
- package/esm/util/index.d.ts +2 -3
- package/esm/util/index.js +2 -3
- package/esm/util/installFetch.d.ts +49 -23
- package/esm/util/installFetch.js +49 -21
- package/esm/util/installInitAutorun.d.ts +8 -8
- package/esm/util/installInitAutorun.js +15 -15
- package/esm/util/lazyAfterAttach.d.ts +13 -0
- package/esm/util/lazyAfterAttach.js +31 -0
- package/esm/util/legendCandidates.d.ts +50 -0
- package/esm/util/legendCandidates.js +87 -0
- package/esm/util/mstUtils.d.ts +12 -1
- package/esm/util/mstUtils.js +20 -2
- package/esm/util/pluginStore.d.ts +1 -0
- package/esm/util/pluginStore.js +8 -1
- package/esm/util/range.d.ts +13 -9
- package/esm/util/range.js +17 -8
- package/esm/util/rxjs.d.ts +16 -0
- package/esm/util/rxjs.js +49 -0
- package/esm/util/tabix.d.ts +10 -4
- package/esm/util/tabix.js +11 -5
- package/esm/util/tracks.d.ts +48 -0
- package/esm/util/tracks.js +192 -66
- package/esm/util/types/index.d.ts +48 -5
- package/esm/util/types/index.js +3 -0
- package/esm/util/unknownSnapshotKeys.d.ts +36 -0
- package/esm/util/unknownSnapshotKeys.js +69 -0
- package/esm/util/viewStatus.d.ts +23 -0
- package/esm/util/viewStatus.js +17 -0
- package/esm/util/whenViewSettled.d.ts +17 -4
- package/esm/util/whenViewSettled.js +19 -6
- package/esm/util/withLaunchInput.d.ts +123 -0
- package/esm/util/withLaunchInput.js +228 -0
- package/package.json +12 -9
- package/esm/util/applyInitSettings.d.ts +0 -79
- package/esm/util/applyInitSettings.js +0 -91
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// How many distinct colors a derived key may name and still BE a key. Past this
|
|
2
|
+
// the data is not a categorical vocabulary (a track keyed by unique per-feature
|
|
3
|
+
// names), and a list of every feature is the thing a key exists instead of.
|
|
4
|
+
//
|
|
5
|
+
// A different question from `legendIsReadable`'s lower bar, which asks whether a
|
|
6
|
+
// key a display already has is worth the rows it costs.
|
|
7
|
+
export const MAX_LEGEND_ENTRIES = 30;
|
|
8
|
+
// Bound on the candidate list one region ships. Far above MAX_LEGEND_ENTRIES, so
|
|
9
|
+
// a vocabulary that is categorical at all crosses whole, and a track with a name
|
|
10
|
+
// per feature ships a prefix that reads the same way it would have.
|
|
11
|
+
export const MAX_LEGEND_CANDIDATES = 1024;
|
|
12
|
+
/**
|
|
13
|
+
* Accumulator for the distinct (row, name, color) combinations a packer walks
|
|
14
|
+
* past, in first-seen order and bounded — the small list `unionLegendCandidates`
|
|
15
|
+
* derives a key from, so the main thread never re-walks the features. Fed scalar
|
|
16
|
+
* by scalar, from the loop that already holds the three values.
|
|
17
|
+
*
|
|
18
|
+
* Deduped on the whole triple rather than on (row, color): the union takes the
|
|
19
|
+
* first NAME it sees for a color, so where that name is already spoken for, the
|
|
20
|
+
* second name on that color has to still be in the list to carry it.
|
|
21
|
+
*
|
|
22
|
+
* A nameless feature contributes nothing, which is the union's rule too — so a
|
|
23
|
+
* track whose features have no names ships an empty list rather than one entry
|
|
24
|
+
* per feature.
|
|
25
|
+
*/
|
|
26
|
+
export function createLegendCandidateCollector(maxCandidates = MAX_LEGEND_CANDIDATES) {
|
|
27
|
+
const candidates = [];
|
|
28
|
+
const colorsSeen = new Map();
|
|
29
|
+
return {
|
|
30
|
+
candidates,
|
|
31
|
+
add(rowIndex, label, color) {
|
|
32
|
+
if (label !== '' && candidates.length < maxCandidates) {
|
|
33
|
+
let byLabel = colorsSeen.get(rowIndex);
|
|
34
|
+
if (byLabel === undefined) {
|
|
35
|
+
byLabel = new Map();
|
|
36
|
+
colorsSeen.set(rowIndex, byLabel);
|
|
37
|
+
}
|
|
38
|
+
let colors = byLabel.get(label);
|
|
39
|
+
if (colors === undefined) {
|
|
40
|
+
colors = new Set();
|
|
41
|
+
byLabel.set(label, colors);
|
|
42
|
+
}
|
|
43
|
+
if (!colors.has(color)) {
|
|
44
|
+
colors.add(color);
|
|
45
|
+
candidates.push({ rowIndex, label, color });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The categorical color key over any number of packed regions: one entry per
|
|
53
|
+
* distinct color, labeled by the first name seen in that color, in first-seen
|
|
54
|
+
* order.
|
|
55
|
+
*
|
|
56
|
+
* Keyed by color rather than by name, so each key row is 1:1 with a color — a
|
|
57
|
+
* row is normally a toggle and hiding a category hides features BY color, so two
|
|
58
|
+
* names sharing one color must collapse to a single row. A name reused across
|
|
59
|
+
* two colors keeps its first-seen color.
|
|
60
|
+
*
|
|
61
|
+
* `[]` when the data carries no categorical signal: nothing named, or more than
|
|
62
|
+
* `maxEntries` distinct colors.
|
|
63
|
+
*
|
|
64
|
+
* `regions` is consumed lazily, so a caller able to answer without reading any
|
|
65
|
+
* of them can pass a generator and have none of them touched.
|
|
66
|
+
*/
|
|
67
|
+
export function unionLegendCandidates(regions, resolve, maxEntries = MAX_LEGEND_ENTRIES) {
|
|
68
|
+
const entries = [];
|
|
69
|
+
const seenColors = new Set();
|
|
70
|
+
const seenLabels = new Set();
|
|
71
|
+
for (const region of regions) {
|
|
72
|
+
const { candidates, rowPaintsCandidateColor } = resolve(region);
|
|
73
|
+
for (const { rowIndex, label, color } of candidates) {
|
|
74
|
+
if (rowPaintsCandidateColor(rowIndex) &&
|
|
75
|
+
!seenLabels.has(label) &&
|
|
76
|
+
!seenColors.has(color)) {
|
|
77
|
+
seenLabels.add(label);
|
|
78
|
+
seenColors.add(color);
|
|
79
|
+
entries.push({ label, color });
|
|
80
|
+
if (entries.length > maxEntries) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return entries;
|
|
87
|
+
}
|
package/esm/util/mstUtils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type PluginManager from '../PluginManager.ts';
|
|
2
|
-
import type { AbstractDisplayModel, AbstractSessionModel, AbstractTrackModel, AbstractViewModel } from './types/index.ts';
|
|
2
|
+
import type { AbstractDisplayModel, AbstractSessionModel, AbstractViewContainer, AbstractTrackModel, AbstractViewModel } from './types/index.ts';
|
|
3
3
|
import type { AssemblyHost, RenderingServices } from './types/renderingServices.ts';
|
|
4
4
|
import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
5
5
|
export { findParentThat, findParentThatIs, getRpcSessionId, } from './parentWalk.ts';
|
|
@@ -85,5 +85,16 @@ export declare function canonicalizeViewRefName(node: IAnyStateTreeNode, refName
|
|
|
85
85
|
export declare function getEnv(obj: IAnyStateTreeNode): {
|
|
86
86
|
pluginManager: PluginManager;
|
|
87
87
|
};
|
|
88
|
+
/**
|
|
89
|
+
* #api core/util
|
|
90
|
+
* `addOrReplaceView` for view types whose state model may be lazily loaded;
|
|
91
|
+
* the synchronous `addOrReplaceView` requires it loaded already.
|
|
92
|
+
*/
|
|
93
|
+
export declare function launchOrReplaceView(args: {
|
|
94
|
+
session: AbstractViewContainer;
|
|
95
|
+
typeName: string;
|
|
96
|
+
initialState?: Record<string, unknown>;
|
|
97
|
+
replacing?: AbstractViewModel;
|
|
98
|
+
}): Promise<AbstractViewModel>;
|
|
88
99
|
export declare function hashCode(str: string): number;
|
|
89
100
|
export declare function objectHash(obj: object): string;
|
package/esm/util/mstUtils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getEnv as getEnvMST } from '@jbrowse/mobx-state-tree';
|
|
1
|
+
import { getEnv as getEnvMST, isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
2
|
import { cachedParent, findParentThatIs } from './parentWalk.js';
|
|
3
|
-
import { isDisplayModel, isSessionModel, isTrackModel, isViewModel, } from './types/index.js';
|
|
3
|
+
import { addOrReplaceView, isDisplayModel, isSessionModel, isTrackModel, isViewModel, } from './types/index.js';
|
|
4
4
|
export { findParentThat, findParentThatIs, getRpcSessionId, } from './parentWalk.js';
|
|
5
5
|
const containingDisplayCache = new WeakMap();
|
|
6
6
|
const containingTrackCache = new WeakMap();
|
|
@@ -105,6 +105,24 @@ export function canonicalizeViewRefName(node, refName) {
|
|
|
105
105
|
export function getEnv(obj) {
|
|
106
106
|
return getEnvMST(obj);
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* #api core/util
|
|
110
|
+
* `addOrReplaceView` for view types whose state model may be lazily loaded;
|
|
111
|
+
* the synchronous `addOrReplaceView` requires it loaded already.
|
|
112
|
+
*/
|
|
113
|
+
export async function launchOrReplaceView(args) {
|
|
114
|
+
// a test's fake session is not an MST node; addView's own guard is the
|
|
115
|
+
// error path there
|
|
116
|
+
if (isStateTreeNode(args.session)) {
|
|
117
|
+
const { pluginManager } = getEnvMST(args.session);
|
|
118
|
+
await pluginManager?.getViewType(args.typeName).loadStateModel();
|
|
119
|
+
// the displays of the tracks a launch opens with are dynamic imports too
|
|
120
|
+
await pluginManager?.preloadSessionTypes({
|
|
121
|
+
views: [{ type: args.typeName, ...args.initialState }],
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return addOrReplaceView(args);
|
|
125
|
+
}
|
|
108
126
|
export function hashCode(str) {
|
|
109
127
|
let hash = 0;
|
|
110
128
|
if (str.length === 0) {
|
|
@@ -34,6 +34,7 @@ export interface PluginUpdate {
|
|
|
34
34
|
name: string;
|
|
35
35
|
definition: PluginDefinition;
|
|
36
36
|
}
|
|
37
|
+
export declare function releaseVersion(jbrowseVersion: string): string;
|
|
37
38
|
export declare function resolvePlugin(plugin: JBrowsePlugin, jbrowseVersion: string): ResolvedPlugin;
|
|
38
39
|
export declare function installedVersionFromUrl(url: string | undefined, packageName: string | undefined): string | undefined;
|
|
39
40
|
/**
|
package/esm/util/pluginStore.js
CHANGED
|
@@ -37,6 +37,13 @@ export function installablePlugins(plugins, isElectron) {
|
|
|
37
37
|
(isElectron ||
|
|
38
38
|
publishedBuilds(plugin).some(b => b.esmUrl ?? b.url ?? b.umdUrl)));
|
|
39
39
|
}
|
|
40
|
+
// A prerelease host is its release for compatibility purposes. Under semver
|
|
41
|
+
// 5.0.0-beta.1 sorts below 5.0.0, so a range written to keep a plugin off v5
|
|
42
|
+
// (`<5.0.0`) would serve it to every beta host and a v5-only range (`>=5.0.0`)
|
|
43
|
+
// would hide it from them. Store ranges are written against release versions.
|
|
44
|
+
export function releaseVersion(jbrowseVersion) {
|
|
45
|
+
return jbrowseVersion.split('-')[0];
|
|
46
|
+
}
|
|
40
47
|
// `*` (and empty) mean "any JBrowse version"; compare-versions throws on those,
|
|
41
48
|
// so handle them directly. A malformed range simply fails to match rather than
|
|
42
49
|
// breaking the store UI — producer-side validation is the place to reject those.
|
|
@@ -44,7 +51,7 @@ function rangeMatches(jbrowseVersion, range) {
|
|
|
44
51
|
let matched = range === '*' || range === '';
|
|
45
52
|
if (!matched) {
|
|
46
53
|
try {
|
|
47
|
-
matched = satisfies(jbrowseVersion, range);
|
|
54
|
+
matched = satisfies(releaseVersion(jbrowseVersion), range);
|
|
48
55
|
}
|
|
49
56
|
catch {
|
|
50
57
|
matched = false;
|
package/esm/util/range.d.ts
CHANGED
|
@@ -20,9 +20,15 @@ export declare function doesIntersect2(left1: number, right1: number, left2: num
|
|
|
20
20
|
/**
|
|
21
21
|
* Compute the expanded fetch range a tabix adapter must "redispatch" to when
|
|
22
22
|
* features found in a query extend past the requested window. Returns the union
|
|
23
|
-
* of the query with the bounds of every
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* of the query with the bounds of every line `expands` admits, or `undefined`
|
|
24
|
+
* when nothing extends past the query (no redispatch needed).
|
|
25
|
+
*
|
|
26
|
+
* **A predicate, not a set of types**, because the useful question is not what a
|
|
27
|
+
* record is called. The bound exists to reach a record's *children*, so what
|
|
28
|
+
* disqualifies a record is having none — and in GFF3 that is answerable exactly,
|
|
29
|
+
* from whether the record carries an `ID` for a `Parent` to reference. A type
|
|
30
|
+
* blocklist can only ever approximate it, and it is the caller's format that
|
|
31
|
+
* knows how: this file reads no column but the coordinates.
|
|
26
32
|
*
|
|
27
33
|
* Feature coordinates arrive interbase, matching the query: @gmod/tabix applies
|
|
28
34
|
* the index's coordinate offset before invoking the line callback, so a GFF/GTF
|
|
@@ -30,15 +36,13 @@ export declare function doesIntersect2(left1: number, right1: number, left2: num
|
|
|
30
36
|
*
|
|
31
37
|
* Seeding the accumulator with the query bounds is what makes the result a
|
|
32
38
|
* union rather than just the feature bounds. A redispatch narrower than the
|
|
33
|
-
* original window would drop
|
|
34
|
-
*
|
|
35
|
-
* those types are deliberately excluded from the bounds.
|
|
39
|
+
* original window would drop an excluded line that the first fetch found inside
|
|
40
|
+
* the query but that falls outside the expanded range.
|
|
36
41
|
*/
|
|
37
|
-
export declare function calculateRedispatchRange
|
|
42
|
+
export declare function calculateRedispatchRange<T extends {
|
|
38
43
|
start: number;
|
|
39
44
|
end: number;
|
|
40
|
-
|
|
41
|
-
}[], dontRedispatchSet: Set<string>, queryStart: number, queryEnd: number): {
|
|
45
|
+
}>(features: T[], expands: (feature: T) => boolean, queryStart: number, queryEnd: number): {
|
|
42
46
|
start: number;
|
|
43
47
|
end: number;
|
|
44
48
|
} | undefined;
|
package/esm/util/range.js
CHANGED
|
@@ -45,9 +45,15 @@ export function doesIntersect2(left1, right1, left2, right2) {
|
|
|
45
45
|
/**
|
|
46
46
|
* Compute the expanded fetch range a tabix adapter must "redispatch" to when
|
|
47
47
|
* features found in a query extend past the requested window. Returns the union
|
|
48
|
-
* of the query with the bounds of every
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* of the query with the bounds of every line `expands` admits, or `undefined`
|
|
49
|
+
* when nothing extends past the query (no redispatch needed).
|
|
50
|
+
*
|
|
51
|
+
* **A predicate, not a set of types**, because the useful question is not what a
|
|
52
|
+
* record is called. The bound exists to reach a record's *children*, so what
|
|
53
|
+
* disqualifies a record is having none — and in GFF3 that is answerable exactly,
|
|
54
|
+
* from whether the record carries an `ID` for a `Parent` to reference. A type
|
|
55
|
+
* blocklist can only ever approximate it, and it is the caller's format that
|
|
56
|
+
* knows how: this file reads no column but the coordinates.
|
|
51
57
|
*
|
|
52
58
|
* Feature coordinates arrive interbase, matching the query: @gmod/tabix applies
|
|
53
59
|
* the index's coordinate offset before invoking the line callback, so a GFF/GTF
|
|
@@ -55,15 +61,18 @@ export function doesIntersect2(left1, right1, left2, right2) {
|
|
|
55
61
|
*
|
|
56
62
|
* Seeding the accumulator with the query bounds is what makes the result a
|
|
57
63
|
* union rather than just the feature bounds. A redispatch narrower than the
|
|
58
|
-
* original window would drop
|
|
59
|
-
*
|
|
60
|
-
* those types are deliberately excluded from the bounds.
|
|
64
|
+
* original window would drop an excluded line that the first fetch found inside
|
|
65
|
+
* the query but that falls outside the expanded range.
|
|
61
66
|
*/
|
|
62
|
-
export function calculateRedispatchRange(features,
|
|
67
|
+
export function calculateRedispatchRange(features, expands, queryStart, queryEnd) {
|
|
63
68
|
let minStart = queryStart;
|
|
64
69
|
let maxEnd = queryEnd;
|
|
70
|
+
// bounds first: only a line reaching past the current bound can widen it, so
|
|
71
|
+
// the predicate, which may scan the line's text, runs on those few lines and
|
|
72
|
+
// not on every line in the window
|
|
65
73
|
for (const feature of features) {
|
|
66
|
-
if (
|
|
74
|
+
if ((feature.start < minStart || feature.end > maxEnd) &&
|
|
75
|
+
expands(feature)) {
|
|
67
76
|
if (feature.start < minStart) {
|
|
68
77
|
minStart = feature.start;
|
|
69
78
|
}
|
package/esm/util/rxjs.d.ts
CHANGED
|
@@ -15,3 +15,19 @@ import type { Observer } from 'rxjs';
|
|
|
15
15
|
* @param func - observer function, could be async
|
|
16
16
|
*/
|
|
17
17
|
export declare function ObservableCreate<T>(func: (arg: Observer<T>) => void | Promise<void>, stopToken?: StopToken): Observable<T>;
|
|
18
|
+
/**
|
|
19
|
+
* Subscribe to an observable and resolve when it completes, running `onNext` per
|
|
20
|
+
* item as it arrives — the streaming shape for parsing items out of a feature
|
|
21
|
+
* stream rather than collecting them first.
|
|
22
|
+
*
|
|
23
|
+
* **`onNext` throwing rejects the promise.** rxjs does not do that on its own:
|
|
24
|
+
* an exception out of a `next` handler goes to its global unhandled-error hook,
|
|
25
|
+
* so the subscriber keeps being fed and `complete` still fires — a bare promise
|
|
26
|
+
* around that resolves as *success* with the items silently missing. Beside
|
|
27
|
+
* `ObservableCreate` because it is the other half of the pattern: the adapter
|
|
28
|
+
* wraps its body in that, the RPC `await`s this, so a per-item parse failure
|
|
29
|
+
* surfaces as a track error instead of a blank-but-loaded track.
|
|
30
|
+
*
|
|
31
|
+
* The source is unsubscribed on the first throw.
|
|
32
|
+
*/
|
|
33
|
+
export declare function subscribeToObservable<T>(observable: Observable<T>, onNext: (item: T) => void): Promise<void>;
|
package/esm/util/rxjs.js
CHANGED
|
@@ -43,3 +43,52 @@ export function ObservableCreate(func, stopToken) {
|
|
|
43
43
|
};
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Subscribe to an observable and resolve when it completes, running `onNext` per
|
|
48
|
+
* item as it arrives — the streaming shape for parsing items out of a feature
|
|
49
|
+
* stream rather than collecting them first.
|
|
50
|
+
*
|
|
51
|
+
* **`onNext` throwing rejects the promise.** rxjs does not do that on its own:
|
|
52
|
+
* an exception out of a `next` handler goes to its global unhandled-error hook,
|
|
53
|
+
* so the subscriber keeps being fed and `complete` still fires — a bare promise
|
|
54
|
+
* around that resolves as *success* with the items silently missing. Beside
|
|
55
|
+
* `ObservableCreate` because it is the other half of the pattern: the adapter
|
|
56
|
+
* wraps its body in that, the RPC `await`s this, so a per-item parse failure
|
|
57
|
+
* surfaces as a track error instead of a blank-but-loaded track.
|
|
58
|
+
*
|
|
59
|
+
* The source is unsubscribed on the first throw.
|
|
60
|
+
*/
|
|
61
|
+
export function subscribeToObservable(observable, onNext) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
// A box rather than two bare `let`s: oxlint narrows a flag assigned only
|
|
64
|
+
// inside a closure to its initializer and reports the reads as always-false.
|
|
65
|
+
// `subscription` is genuinely still undefined during a synchronous source's
|
|
66
|
+
// first emissions, which is why it is read optionally below.
|
|
67
|
+
const state = {
|
|
68
|
+
failed: false,
|
|
69
|
+
};
|
|
70
|
+
state.subscription = observable.subscribe({
|
|
71
|
+
next: item => {
|
|
72
|
+
if (!state.failed) {
|
|
73
|
+
try {
|
|
74
|
+
onNext(item);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
state.failed = true;
|
|
78
|
+
// A parser throws an Error in every real case here; the wrap is for
|
|
79
|
+
// the rule that a rejection reason must be one, and keeps whatever
|
|
80
|
+
// was actually thrown as the `cause`.
|
|
81
|
+
reject(e instanceof Error ? e : new Error(String(e), { cause: e }));
|
|
82
|
+
state.subscription?.unsubscribe();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
error: reject,
|
|
87
|
+
complete: () => {
|
|
88
|
+
if (!state.failed) {
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
package/esm/util/tabix.d.ts
CHANGED
|
@@ -45,9 +45,15 @@ export declare function readTabixLines(gff: TabixLineSource, refName: string, st
|
|
|
45
45
|
* Read a region's tabix lines, then — if any feature found there extends past
|
|
46
46
|
* the query — read the overhang on either side, so a parent line's children (a
|
|
47
47
|
* gene's exons, a transcript's CDS) outside the original window are pulled in
|
|
48
|
-
* and the parent/child tree resolves fully.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* and the parent/child tree resolves fully. `expands` decides which lines widen
|
|
49
|
+
* the bound, so one chromosome-spanning record can't force a whole-chromosome
|
|
50
|
+
* refetch; {@link calculateRedispatchRange} says why that is a predicate.
|
|
51
|
+
*
|
|
52
|
+
* **There is one control, and skipping the expansion is a value of it**: an
|
|
53
|
+
* `expands` that admits nothing reads the query and stops. A caller that wants
|
|
54
|
+
* that says so to its adapter, which is the layer that knows whether its format
|
|
55
|
+
* can honour it — see `Gff3TabixAdapter`, and `GtfTabixAdapter` for a format
|
|
56
|
+
* that cannot.
|
|
51
57
|
*
|
|
52
58
|
* **The flanks, not the union.** Re-reading `[minStart, maxEnd]` returns
|
|
53
59
|
* everything the first read already had — a second full read of the query range
|
|
@@ -76,7 +82,7 @@ export declare function readTabixLinesRedispatched(file: TabixLineSource, query:
|
|
|
76
82
|
refName: string;
|
|
77
83
|
start: number;
|
|
78
84
|
end: number;
|
|
79
|
-
},
|
|
85
|
+
}, expands: (line: TabixLine) => boolean, opts?: {
|
|
80
86
|
statusCallback?: StatusCallback;
|
|
81
87
|
stopToken?: StopToken;
|
|
82
88
|
}): Promise<TabixLine[]>;
|
package/esm/util/tabix.js
CHANGED
|
@@ -44,9 +44,15 @@ export function readTabixLines(gff, refName, start, end, statusCallback, stopTok
|
|
|
44
44
|
* Read a region's tabix lines, then — if any feature found there extends past
|
|
45
45
|
* the query — read the overhang on either side, so a parent line's children (a
|
|
46
46
|
* gene's exons, a transcript's CDS) outside the original window are pulled in
|
|
47
|
-
* and the parent/child tree resolves fully.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* and the parent/child tree resolves fully. `expands` decides which lines widen
|
|
48
|
+
* the bound, so one chromosome-spanning record can't force a whole-chromosome
|
|
49
|
+
* refetch; {@link calculateRedispatchRange} says why that is a predicate.
|
|
50
|
+
*
|
|
51
|
+
* **There is one control, and skipping the expansion is a value of it**: an
|
|
52
|
+
* `expands` that admits nothing reads the query and stops. A caller that wants
|
|
53
|
+
* that says so to its adapter, which is the layer that knows whether its format
|
|
54
|
+
* can honour it — see `Gff3TabixAdapter`, and `GtfTabixAdapter` for a format
|
|
55
|
+
* that cannot.
|
|
50
56
|
*
|
|
51
57
|
* **The flanks, not the union.** Re-reading `[minStart, maxEnd]` returns
|
|
52
58
|
* everything the first read already had — a second full read of the query range
|
|
@@ -74,13 +80,13 @@ export function readTabixLines(gff, refName, start, end, statusCallback, stopTok
|
|
|
74
80
|
export async function readTabixLinesRedispatched(file,
|
|
75
81
|
// the region's locus only — kept structural, like TabixLineSource above, so
|
|
76
82
|
// this module stays free of the `types` barrel and its MST models
|
|
77
|
-
query,
|
|
83
|
+
query, expands, opts = {}) {
|
|
78
84
|
const { statusCallback, stopToken } = opts;
|
|
79
85
|
// `cb` is not optional: each read decides where it reports, and the flanks
|
|
80
86
|
// report somewhere the first read does not
|
|
81
87
|
const read = (start, end, cb) => readTabixLines(file, query.refName, start, end, cb, stopToken);
|
|
82
88
|
const lines = await read(query.start, query.end, statusCallback);
|
|
83
|
-
const redispatch = calculateRedispatchRange(lines,
|
|
89
|
+
const redispatch = calculateRedispatchRange(lines, expands, query.start, query.end);
|
|
84
90
|
if (redispatch) {
|
|
85
91
|
const slot = createStatusFanOut(statusCallback);
|
|
86
92
|
const flanks = await Promise.all([
|
package/esm/util/tracks.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { getRpcSessionId } from './parentWalk.ts';
|
|
|
3
3
|
import type PluginManager from '../PluginManager.ts';
|
|
4
4
|
import type { AnyConfigurationModel } from '../configuration/index.ts';
|
|
5
5
|
import type { BlobLocation, FileHandleLocation, FileLocation, PreFileLocation } from './types/data.ts';
|
|
6
|
+
import type { AbstractViewContainer } from './types/index.ts';
|
|
6
7
|
import type { IAnyStateTreeNode, IAnyType, IStateTreeNode, Instance, types } from '@jbrowse/mobx-state-tree';
|
|
7
8
|
export declare function getTrackAssemblyNames(track: IStateTreeNode & {
|
|
8
9
|
configuration: AnyConfigurationModel;
|
|
@@ -242,6 +243,23 @@ export declare function guessTrackConf(input: string | LooseTrackInput, pluginMa
|
|
|
242
243
|
assemblyNames: string[];
|
|
243
244
|
adapter: AdapterConfig;
|
|
244
245
|
};
|
|
246
|
+
/**
|
|
247
|
+
* {@link guessTrackConf} over an already-built {@link FileLocation}, for a
|
|
248
|
+
* caller holding a location the loose `{ uri }` vocabulary cannot spell — a
|
|
249
|
+
* desktop local path. Kept separate so `LooseTrackInput` stays URI-only: it
|
|
250
|
+
* sits on the track-config union and the root config's `tracks`, where a
|
|
251
|
+
* local path would be a config a web deployment could not open.
|
|
252
|
+
*
|
|
253
|
+
* The `trackId` is a content hash of the adapter, not a timestamp, so adding
|
|
254
|
+
* the same file twice is idempotent — `addToSession` dedupes on it.
|
|
255
|
+
*/
|
|
256
|
+
export declare function guessTrackConfForLocation(file: FileLocation, index: FileLocation | undefined, pluginManager: PluginManager, assemblyName?: string): {
|
|
257
|
+
trackId: string;
|
|
258
|
+
type: string;
|
|
259
|
+
name: string;
|
|
260
|
+
assemblyNames: string[];
|
|
261
|
+
adapter: AdapterConfig;
|
|
262
|
+
};
|
|
245
263
|
/**
|
|
246
264
|
* Whether a track snapshot is the loose `{ uri, ... }` form — a data file with
|
|
247
265
|
* no `adapter` — that {@link expandLooseTrackConfig} expands.
|
|
@@ -294,6 +312,7 @@ interface MinimalTrack extends IAnyType {
|
|
|
294
312
|
interface GenericView {
|
|
295
313
|
type: string;
|
|
296
314
|
tracks: MSTArray<MinimalTrack>;
|
|
315
|
+
showTrack?: (trackId: string, initialSnapshot?: object, displayInitialSnapshot?: DisplayInitialSnapshot, inlineConf?: Record<string, unknown>) => unknown;
|
|
297
316
|
}
|
|
298
317
|
/**
|
|
299
318
|
* The display-snapshot argument of `showTrackGeneric`, exported so a view's own
|
|
@@ -403,6 +422,8 @@ export declare function stripTrackIds(tracks: TrackSnapshot[]): {
|
|
|
403
422
|
}[];
|
|
404
423
|
export declare function hideTrackGeneric(self: GenericView, trackId: string): boolean;
|
|
405
424
|
export declare function toggleTrackGeneric(self: GenericView, trackId: string): boolean;
|
|
425
|
+
export declare function launchTrackGeneric(self: GenericView, trackId: string, initialSnapshot?: object, displayInitialSnapshot?: DisplayInitialSnapshot, inlineConf?: Record<string, unknown>): Promise<any>;
|
|
426
|
+
export declare function launchToggleTrackGeneric(self: GenericView, trackId: string): Promise<boolean>;
|
|
406
427
|
/**
|
|
407
428
|
* Every track config the session can show, connection-supplied ones included.
|
|
408
429
|
*
|
|
@@ -424,3 +445,30 @@ export declare function allSessionTracks(session: {
|
|
|
424
445
|
setSubschema(slotName: string, data: Record<string, unknown>): any;
|
|
425
446
|
setSlot(slotName: string, value: unknown): void;
|
|
426
447
|
} & IStateTreeNode<import("../configuration/types.ts").AnyConfigurationSchemaType>)[];
|
|
448
|
+
/**
|
|
449
|
+
* The session's annotation for `assemblyName`, as trackIds: what a view opened
|
|
450
|
+
* on another genome starts with. Only FeatureTracks, not everything configured
|
|
451
|
+
* for the assembly — an alignments track turned on behind the user's back
|
|
452
|
+
* fetches data nobody asked for, and a FeatureTrack over a few kb is one tabix
|
|
453
|
+
* range query.
|
|
454
|
+
*/
|
|
455
|
+
export declare function annotationTrackIds(session: {
|
|
456
|
+
tracks: AnyConfigurationModel[];
|
|
457
|
+
connectionInstances?: {
|
|
458
|
+
tracks: AnyConfigurationModel[];
|
|
459
|
+
}[];
|
|
460
|
+
assemblyManager: AssemblyNameResolver;
|
|
461
|
+
}, assemblyName: string): string[];
|
|
462
|
+
/**
|
|
463
|
+
* One assembly on its own in a linear genome view, at `loc`. Keyed on `id`:
|
|
464
|
+
* following several alignments to one genome re-navigates the view rather than
|
|
465
|
+
* stacking a new one each time.
|
|
466
|
+
*/
|
|
467
|
+
export declare function openAssemblyInLinearView({ session, id, assemblyName, loc, tracks, }: {
|
|
468
|
+
session: AbstractViewContainer;
|
|
469
|
+
id: string;
|
|
470
|
+
assemblyName: string;
|
|
471
|
+
loc: string;
|
|
472
|
+
tracks: TrackInit[];
|
|
473
|
+
}): Promise<void>;
|
|
474
|
+
export declare function openMateLabel(assemblyName: string): string;
|