@jbrowse/core 5.0.0-beta.1 → 5.0.0-beta.3

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 (199) hide show
  1. package/README.md +112 -43
  2. package/esm/BaseFeatureWidget/BaseFeatureDetail/FeatureDetails.d.ts +2 -3
  3. package/esm/BaseFeatureWidget/BaseFeatureDetail/ParentFeatureLine.d.ts +4 -0
  4. package/esm/BaseFeatureWidget/BaseFeatureDetail/ParentFeatureLine.js +17 -0
  5. package/esm/BaseFeatureWidget/BaseFeatureDetail/index.js +4 -3
  6. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.d.ts +2 -2
  7. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +2 -2
  8. package/esm/BaseFeatureWidget/index.d.ts +1 -1
  9. package/esm/BaseFeatureWidget/stateModelFactory.d.ts +8 -1
  10. package/esm/BaseFeatureWidget/stateModelFactory.js +6 -0
  11. package/esm/BaseFeatureWidget/types.d.ts +10 -2
  12. package/esm/PluginLoader.d.ts +1 -0
  13. package/esm/PluginLoader.js +52 -8
  14. package/esm/PluginManager.d.ts +99 -17
  15. package/esm/PluginManager.js +194 -26
  16. package/esm/ReExports/BaseFeatureDetails.js +1 -1
  17. package/esm/ReExports/MuiDataGridReExports.d.ts +1 -0
  18. package/esm/ReExports/MuiDataGridReExports.js +2 -2
  19. package/esm/ReExports/barrelOnlyNames.d.ts +5 -0
  20. package/esm/ReExports/barrelOnlyNames.js +63 -0
  21. package/esm/ReExports/modules.js +31 -109
  22. package/esm/ReExports/publicTracks.d.ts +1 -0
  23. package/esm/ReExports/publicTracks.js +14 -0
  24. package/esm/ReExports/publicUi.d.ts +4 -0
  25. package/esm/ReExports/publicUi.js +33 -0
  26. package/esm/ReExports/publicUtil.d.ts +1 -1
  27. package/esm/ReExports/publicUtil.js +8 -4
  28. package/esm/ReExports/sharedModules.d.ts +96 -0
  29. package/esm/ReExports/sharedModules.js +66 -0
  30. package/esm/ReExports/uiStub.d.ts +2 -0
  31. package/esm/ReExports/uiStub.js +11 -0
  32. package/esm/ReExports/workerModules.d.ts +2 -0
  33. package/esm/ReExports/workerModules.js +15 -0
  34. package/esm/ReExports/workerNamespaceNames.d.ts +11 -0
  35. package/esm/ReExports/workerNamespaceNames.js +281 -0
  36. package/esm/configuration/configurationSchema.d.ts +5 -38
  37. package/esm/configuration/configurationSchema.js +64 -40
  38. package/esm/configuration/index.d.ts +2 -2
  39. package/esm/configuration/index.js +1 -1
  40. package/esm/configuration/promotableDefaults.d.ts +54 -19
  41. package/esm/configuration/promotableDefaults.js +117 -48
  42. package/esm/configuration/promotablePin.d.ts +50 -40
  43. package/esm/configuration/promotableResolve.d.ts +1 -1
  44. package/esm/configuration/promotableResolve.js +1 -1
  45. package/esm/configuration/tabixIndexFields.d.ts +2 -2
  46. package/esm/configuration/tabixIndexFields.js +2 -2
  47. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +12 -0
  48. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +35 -4
  49. package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +41 -0
  50. package/esm/data_adapters/BaseAdapter/featureDensity.js +32 -0
  51. package/esm/data_adapters/BaseAdapter/index.d.ts +2 -1
  52. package/esm/data_adapters/BaseAdapter/index.js +1 -0
  53. package/esm/data_adapters/BaseAdapter/stats.d.ts +33 -1
  54. package/esm/data_adapters/BaseAdapter/stats.js +52 -13
  55. package/esm/data_adapters/BaseAdapter/types.d.ts +3 -8
  56. package/esm/pluggableElementTypes/AdapterType.d.ts +2 -1
  57. package/esm/pluggableElementTypes/AdapterType.js +1 -1
  58. package/esm/pluggableElementTypes/DisplayType.d.ts +4 -12
  59. package/esm/pluggableElementTypes/DisplayType.js +3 -10
  60. package/esm/pluggableElementTypes/LazyStateModelElement.d.ts +27 -0
  61. package/esm/pluggableElementTypes/LazyStateModelElement.js +71 -0
  62. package/esm/pluggableElementTypes/ViewType.d.ts +20 -4
  63. package/esm/pluggableElementTypes/ViewType.js +32 -4
  64. package/esm/pluggableElementTypes/extendElementType.d.ts +1 -2
  65. package/esm/pluggableElementTypes/extendElementType.js +9 -5
  66. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +44 -3
  67. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +75 -2
  68. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +21 -0
  69. package/esm/pluggableElementTypes/models/BaseTrackModel.js +34 -2
  70. package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +49 -0
  71. package/esm/pluggableElementTypes/models/BaseViewModel.js +28 -0
  72. package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +14 -10
  73. package/esm/pluggableElementTypes/models/assertDisplayContract.js +25 -32
  74. package/esm/pluggableElementTypes/models/baseTrackConfig.js +7 -1
  75. package/esm/rpc/RpcRegistry.d.ts +10 -0
  76. package/esm/rpc/byteBudget.d.ts +9 -0
  77. package/esm/rpc/coreRpcMethods.d.ts +1 -0
  78. package/esm/rpc/coreRpcMethods.js +1 -0
  79. package/esm/rpc/methods/CoreGetFeatureDensity.d.ts +11 -0
  80. package/esm/rpc/methods/CoreGetFeatureDensity.js +22 -0
  81. package/esm/svg/svgReady.d.ts +1 -0
  82. package/esm/ui/BaseExportSvgDialog.d.ts +1 -1
  83. package/esm/ui/CascadingMenu.js +30 -8
  84. package/esm/ui/ErrorMessageStackTraceDialog.js +5 -6
  85. package/esm/ui/ExportSvgDialog.d.ts +1 -1
  86. package/esm/ui/LoadingOverlay.d.ts +14 -0
  87. package/esm/ui/LoadingOverlay.js +1 -1
  88. package/esm/ui/MenuTypes.d.ts +14 -5
  89. package/esm/ui/MenuTypes.js +3 -0
  90. package/esm/ui/PaletteContext.d.ts +11 -4
  91. package/esm/ui/PaletteContext.js +34 -5
  92. package/esm/ui/PinAdornment.js +31 -43
  93. package/esm/ui/ScrollChrome.d.ts +16 -0
  94. package/esm/ui/ScrollChrome.js +12 -0
  95. package/esm/ui/ScrollZoomToggle.d.ts +4 -3
  96. package/esm/ui/ScrollZoomToggle.js +4 -3
  97. package/esm/ui/SizeSliderRow.js +1 -1
  98. package/esm/ui/index.d.ts +3 -3
  99. package/esm/ui/index.js +4 -4
  100. package/esm/ui/launchViewMenu.js +7 -1
  101. package/esm/ui/legendMenuItem.d.ts +10 -16
  102. package/esm/ui/legendMenuItem.js +9 -20
  103. package/esm/ui/makeSizeMenu.js +1 -1
  104. package/esm/ui/menuItemAdornment.d.ts +2 -3
  105. package/esm/ui/menuItemAdornment.js +2 -3
  106. package/esm/ui/menuItems.d.ts +2 -3
  107. package/esm/ui/menuItems.js +1 -2
  108. package/esm/ui/palette.d.ts +11 -11
  109. package/esm/ui/palette.js +11 -11
  110. package/esm/ui/promotablePinCoverage.d.ts +16 -2
  111. package/esm/ui/promotablePinCoverage.js +40 -3
  112. package/esm/ui/theme.js +5 -0
  113. package/esm/ui/toggleMenuItems.d.ts +14 -9
  114. package/esm/ui/toggleMenuItems.js +32 -15
  115. package/esm/util/Base1DUtils.d.ts +4 -2
  116. package/esm/util/Base1DUtils.js +12 -4
  117. package/esm/util/SvgCanvas.js +4 -3
  118. package/esm/util/adapterConfigKey.d.ts +12 -0
  119. package/esm/util/adapterConfigKey.js +14 -0
  120. package/esm/util/adapterMetadata.js +2 -1
  121. package/esm/util/addAndShowTrack.d.ts +1 -1
  122. package/esm/util/addAndShowTrack.js +5 -1
  123. package/esm/util/addTrackFromWidget.js +2 -2
  124. package/esm/util/analytics.js +4 -5
  125. package/esm/util/cacheBudgets.d.ts +22 -19
  126. package/esm/util/cacheBudgets.js +22 -19
  127. package/esm/util/clampToContig.d.ts +17 -0
  128. package/esm/util/clampToContig.js +19 -0
  129. package/esm/util/colorBits.d.ts +2 -1
  130. package/esm/util/colorBits.js +2 -5
  131. package/esm/util/createStopTokenRotation.d.ts +9 -7
  132. package/esm/util/createStopTokenRotation.js +7 -27
  133. package/esm/util/featureTypes.d.ts +24 -0
  134. package/esm/util/featureTypes.js +35 -0
  135. package/esm/util/fetchContext.d.ts +2 -2
  136. package/esm/util/fetchContext.js +2 -2
  137. package/esm/util/fetchPhases.d.ts +10 -10
  138. package/esm/util/hooks.d.ts +8 -0
  139. package/esm/util/hooks.js +27 -0
  140. package/esm/util/index.d.ts +6 -5
  141. package/esm/util/index.js +5 -4
  142. package/esm/util/installFetch.d.ts +71 -34
  143. package/esm/util/installFetch.js +57 -29
  144. package/esm/util/installInitAutorun.d.ts +14 -13
  145. package/esm/util/installInitAutorun.js +17 -17
  146. package/esm/util/installPrerequisiteFetch.d.ts +86 -0
  147. package/esm/util/installPrerequisiteFetch.js +73 -0
  148. package/esm/util/lazyAfterAttach.d.ts +13 -0
  149. package/esm/util/lazyAfterAttach.js +31 -0
  150. package/esm/util/legendCandidates.d.ts +50 -0
  151. package/esm/util/legendCandidates.js +87 -0
  152. package/esm/util/mstUtils.d.ts +12 -1
  153. package/esm/util/mstUtils.js +20 -2
  154. package/esm/util/openFeatureWidget.d.ts +5 -0
  155. package/esm/util/openFeatureWidget.js +1 -0
  156. package/esm/util/openViews.d.ts +16 -0
  157. package/esm/util/openViews.js +22 -0
  158. package/esm/util/parentWalk.d.ts +1 -1
  159. package/esm/util/parentWalk.js +1 -1
  160. package/esm/util/pluginStore.d.ts +1 -0
  161. package/esm/util/pluginStore.js +8 -1
  162. package/esm/util/range.d.ts +13 -9
  163. package/esm/util/range.js +17 -8
  164. package/esm/util/renderToStaticMarkup.js +10 -0
  165. package/esm/util/rxjs.d.ts +16 -0
  166. package/esm/util/rxjs.js +49 -0
  167. package/esm/util/scheduleDetachedDestroy.d.ts +18 -0
  168. package/esm/util/scheduleDetachedDestroy.js +24 -0
  169. package/esm/util/scrollLatch.d.ts +1 -0
  170. package/esm/util/scrollLatch.js +8 -0
  171. package/esm/util/stopToken.d.ts +6 -5
  172. package/esm/util/tabix.d.ts +10 -4
  173. package/esm/util/tabix.js +11 -5
  174. package/esm/util/trackConfigDelta.d.ts +6 -6
  175. package/esm/util/trackConfigDelta.js +3 -3
  176. package/esm/util/tracks.d.ts +48 -0
  177. package/esm/util/tracks.js +192 -66
  178. package/esm/util/types/index.d.ts +58 -10
  179. package/esm/util/types/index.js +3 -0
  180. package/esm/util/unknownSnapshotKeys.d.ts +36 -0
  181. package/esm/util/unknownSnapshotKeys.js +69 -0
  182. package/esm/util/usePanelVirtualScroll.d.ts +6 -2
  183. package/esm/util/usePanelVirtualScroll.js +6 -2
  184. package/esm/util/useRowVirtualScroll.d.ts +1 -1
  185. package/esm/util/useRowVirtualScroll.js +1 -1
  186. package/esm/util/useVirtualScrollWheel.js +11 -1
  187. package/esm/util/viewStatus.d.ts +23 -0
  188. package/esm/util/viewStatus.js +17 -0
  189. package/esm/util/wheelZoom.d.ts +3 -1
  190. package/esm/util/wheelZoom.js +39 -14
  191. package/esm/util/whenViewSettled.d.ts +17 -4
  192. package/esm/util/whenViewSettled.js +19 -6
  193. package/esm/util/withLaunchInput.d.ts +168 -0
  194. package/esm/util/withLaunchInput.js +273 -0
  195. package/package.json +19 -11
  196. package/esm/ui/promotableMenuItems.d.ts +0 -19
  197. package/esm/ui/promotableMenuItems.js +0 -89
  198. package/esm/util/applyInitSettings.d.ts +0 -79
  199. package/esm/util/applyInitSettings.js +0 -91
@@ -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;
@@ -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) {
@@ -5,10 +5,15 @@ export interface FeatureWidgetTypeRef {
5
5
  type: string;
6
6
  id: string;
7
7
  }
8
+ export interface ParentFeatureSummary {
9
+ name: string;
10
+ type?: string;
11
+ }
8
12
  export declare function openFeatureWidget(node: IAnyStateTreeNode, featureData: SimpleFeatureSerialized, opts?: {
9
13
  widget?: FeatureWidgetTypeRef;
10
14
  extra?: Record<string, unknown>;
11
15
  feature?: Feature;
16
+ parentFeature?: ParentFeatureSummary;
12
17
  }): Widget | undefined;
13
18
  /**
14
19
  * The other half of the flow above: the details lookup came back with nothing,
@@ -32,6 +32,7 @@ export function openFeatureWidget(node, featureData, opts = {}) {
32
32
  const { type, id } = opts.widget ?? DEFAULT_FEATURE_WIDGET;
33
33
  const widget = session.addWidget(type, id, {
34
34
  featureData,
35
+ parentFeature: opts.parentFeature,
35
36
  view: getContainingView(node),
36
37
  track: getContainingTrack(node),
37
38
  ...opts.extra,
@@ -0,0 +1,16 @@
1
+ import type { AbstractSessionModel, AbstractTrackModel, AbstractViewModel } from './types/index.ts';
2
+ /**
3
+ * "What is open" — the reduction four consumers wanted and each used to walk
4
+ * for itself (AppReadyMarker, jbApi, and capture's session gate and busy
5
+ * check), on different and drifting spellings of the view nesting.
6
+ *
7
+ * The recursion lives here, once. What each view contributes lives on the view,
8
+ * once, as `ownViews`/`ownTracks`. Neither half guesses at the other: the
9
+ * census is declared intent, not a shape that can be discovered, because the
10
+ * two things that look enumerable are not — the dotplot's `views` prop holds
11
+ * view-shaped axis models the user never opened, and react-msaview's `tracks`
12
+ * getter holds annotation rows that are not tracks.
13
+ */
14
+ export declare function openViews(session: AbstractSessionModel): AbstractViewModel[];
15
+ /** Every track on every open view. */
16
+ export declare function openTracks(session: AbstractSessionModel): AbstractTrackModel[];
@@ -0,0 +1,22 @@
1
+ /**
2
+ * "What is open" — the reduction four consumers wanted and each used to walk
3
+ * for itself (AppReadyMarker, jbApi, and capture's session gate and busy
4
+ * check), on different and drifting spellings of the view nesting.
5
+ *
6
+ * The recursion lives here, once. What each view contributes lives on the view,
7
+ * once, as `ownViews`/`ownTracks`. Neither half guesses at the other: the
8
+ * census is declared intent, not a shape that can be discovered, because the
9
+ * two things that look enumerable are not — the dotplot's `views` prop holds
10
+ * view-shaped axis models the user never opened, and react-msaview's `tracks`
11
+ * getter holds annotation rows that are not tracks.
12
+ */
13
+ export function openViews(session) {
14
+ return session.views.flatMap(withNested);
15
+ }
16
+ /** Every track on every open view. */
17
+ export function openTracks(session) {
18
+ return openViews(session).flatMap(v => v.ownTracks ?? []);
19
+ }
20
+ function withNested(view) {
21
+ return [view, ...(view.ownViews ?? []).flatMap(withNested)];
22
+ }
@@ -14,7 +14,7 @@ export declare function findParentThatIs<T extends (a: IAnyStateTreeNode) => boo
14
14
  * Keying on the node is safe because nothing re-parents one — both `detach`
15
15
  * call sites hand the node straight to `scheduleDetachedDestroy` (ADR-069), so
16
16
  * a cached ancestor can only go stale by dying, which the `isAlive` check
17
- * catches. That is the difference from the three memos REJECTED_IDEAS records
17
+ * catches. That is the difference from the three memos agent-docs/architecture-decision-records/ records
18
18
  * removing: each keyed on something its caller had just allocated.
19
19
  */
20
20
  export declare function cachedParent<T>(cache: WeakMap<IAnyStateTreeNode, T>, node: IAnyStateTreeNode, finder: () => T, errorMsg: string): T;
@@ -39,7 +39,7 @@ export function findParentThatIs(node, predicate) {
39
39
  * Keying on the node is safe because nothing re-parents one — both `detach`
40
40
  * call sites hand the node straight to `scheduleDetachedDestroy` (ADR-069), so
41
41
  * a cached ancestor can only go stale by dying, which the `isAlive` check
42
- * catches. That is the difference from the three memos REJECTED_IDEAS records
42
+ * catches. That is the difference from the three memos agent-docs/architecture-decision-records/ records
43
43
  * removing: each keyed on something its caller had just allocated.
44
44
  */
45
45
  export function cachedParent(cache, node, finder, errorMsg) {
@@ -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
  /**
@@ -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;
@@ -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 feature whose type is not in
24
- * `dontRedispatchSet`, or `undefined` when nothing extends past the query (no
25
- * redispatch needed).
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 a dontRedispatch-typed feature that the first
34
- * fetch found inside the query but that falls outside the expanded range, since
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(features: {
42
+ export declare function calculateRedispatchRange<T extends {
38
43
  start: number;
39
44
  end: number;
40
- type: string;
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 feature whose type is not in
49
- * `dontRedispatchSet`, or `undefined` when nothing extends past the query (no
50
- * redispatch needed).
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 a dontRedispatch-typed feature that the first
59
- * fetch found inside the query but that falls outside the expanded range, since
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, dontRedispatchSet, queryStart, queryEnd) {
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 (!dontRedispatchSet.has(feature.type)) {
74
+ if ((feature.start < minStart || feature.end > maxEnd) &&
75
+ expands(feature)) {
67
76
  if (feature.start < minStart) {
68
77
  minStart = feature.start;
69
78
  }
@@ -7,6 +7,16 @@ import { createRoot } from 'react-dom/client';
7
7
  // reason to pull react/react-dom/@emotion, and the barrel is imported by
8
8
  // worker-side adapter code that never renders.
9
9
  //
10
+ // It is not part of the plugin ABI either, and that removal did break two
11
+ // published bundles before anyone noticed: react-msaview's SVG export read it
12
+ // off `@jbrowse/core/util`, and jbrowse-plugin-msaview and -tview bundle
13
+ // react-msaview. Resolved on their side -- react-msaview owns its own copy now
14
+ // (packages/lib/src/renderToStaticMarkup.ts there) -- because a rendering
15
+ // library asking its host for a renderer was the coupling worth removing, and
16
+ // because react-dom is a host external for a plugin anyway, so it costs them a
17
+ // few hundred bytes rather than a duplicate react. sharedModules.purity.test.ts
18
+ // is what keeps this file off the worker's graph now.
19
+ //
10
20
  // https://react.dev/reference/react-dom/server/renderToString#removing-rendertostring-from-the-client-code
11
21
  export function renderToStaticMarkup(node) {
12
22
  const div = document.createElement('div');
@@ -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
+ }
@@ -0,0 +1,18 @@
1
+ import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
2
+ /**
3
+ * Free a tree an action has already let go of — a superseded session, a view
4
+ * taken out of one, a superseded rootModel the React host has detached.
5
+ *
6
+ * A task rather than a microtask: the wait is for React to unmount the
7
+ * components rendering it, which happens on the re-render that the action's own
8
+ * reactions schedule. Deferring is acceptable here, and is not elsewhere,
9
+ * because the node stays alive and detached until it fires — a late read gets a
10
+ * live tree nobody is rendering. ADR-069.
11
+ *
12
+ * The destroy itself is not optional. `beforeDestroy` is a plugin-facing
13
+ * contract (jbrowse-plugin-apollo aborts its in-flight fetches there) and core's
14
+ * `BaseTrackModel` releases the `rpcSessionId` claim that lets
15
+ * `CoreFreeResources` evict a parsed adapter from the worker — so detaching and
16
+ * forgetting would trade a console warning for an unbounded leak.
17
+ */
18
+ export declare function scheduleDetachedDestroy(node: IAnyStateTreeNode): void;
@@ -0,0 +1,24 @@
1
+ import { destroy, isAlive } from '@jbrowse/mobx-state-tree';
2
+ /**
3
+ * Free a tree an action has already let go of — a superseded session, a view
4
+ * taken out of one, a superseded rootModel the React host has detached.
5
+ *
6
+ * A task rather than a microtask: the wait is for React to unmount the
7
+ * components rendering it, which happens on the re-render that the action's own
8
+ * reactions schedule. Deferring is acceptable here, and is not elsewhere,
9
+ * because the node stays alive and detached until it fires — a late read gets a
10
+ * live tree nobody is rendering. ADR-069.
11
+ *
12
+ * The destroy itself is not optional. `beforeDestroy` is a plugin-facing
13
+ * contract (jbrowse-plugin-apollo aborts its in-flight fetches there) and core's
14
+ * `BaseTrackModel` releases the `rpcSessionId` claim that lets
15
+ * `CoreFreeResources` evict a parsed adapter from the worker — so detaching and
16
+ * forgetting would trade a console warning for an unbounded leak.
17
+ */
18
+ export function scheduleDetachedDestroy(node) {
19
+ setTimeout(() => {
20
+ if (isAlive(node)) {
21
+ destroy(node);
22
+ }
23
+ }, 0);
24
+ }
@@ -1,4 +1,5 @@
1
1
  export declare function createScrollLatch(timeoutMs?: number): {
2
2
  scroll(e: WheelEvent, cur: number, delta: number, max: number, min?: number): number | null;
3
+ holds(e: WheelEvent): boolean;
3
4
  reset(): void;
4
5
  };
@@ -47,6 +47,14 @@ export function createScrollLatch(timeoutMs = LATCH_TIMEOUT_MS) {
47
47
  }
48
48
  return null;
49
49
  },
50
+ // Whether this panel already owns the gesture `e` belongs to, i.e. it has
51
+ // scrolled during a run of events this one continues. Callers ask before
52
+ // declining an event on their own rules: a gesture that has latched keeps
53
+ // every event it fires, cross-axis momentum included, which is the whole
54
+ // point of latching.
55
+ holds(e) {
56
+ return latched && e.timeStamp - lastEvent < timeoutMs;
57
+ },
50
58
  // Forget the gesture state so the next event is treated as a fresh gesture.
51
59
  // Called when the pointer leaves the panel: a gesture the browser has
52
60
  // latched to this element must not keep the page suppressed once the cursor
@@ -31,11 +31,12 @@ import type { TimeGate } from './timeGate.ts';
31
31
  * either way over a 2000x BAM cancel burst) and that measurement was sound but
32
32
  * scoped: every loop on the alignments path is already chunked by awaits at
33
33
  * region granularity, so there was nothing there for an intra-loop probe to
34
- * interrupt. The counter-example is `getLDMatrix.ts`'s O(n²) Float32Array fill —
35
- * millions of pair computations with no await anywhere, where this probe is the
36
- * only thing that can stop the work, and which that bench never exercises. Any
37
- * future attempt to delete this needs a cancel measurement on an
38
- * await-free workload (LD or multi-sample-variant), not on a pileup.
34
+ * interrupt. The counter-example is the multi-sample-variant genotype matrix
35
+ * fill (`VariantRPC/getGenotypeMatrix.ts`) — millions of cells with no await
36
+ * anywhere, where this probe is the only thing that can stop the work, and
37
+ * which that bench never exercises. Any future attempt to delete this needs a
38
+ * cancel measurement on an await-free workload, not on a pileup. LD's O(n²)
39
+ * pair fill was the original example and went with the in-browser estimator.
39
40
  *
40
41
  * ## Which path runs where
41
42
  *
@@ -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. Types in `dontRedispatchSet` are
49
- * excluded from the bounds, so one chromosome-spanning record can't force a
50
- * whole-chromosome refetch.
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
- }, dontRedispatchSet: Set<string>, opts?: {
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. Types in `dontRedispatchSet` are
48
- * excluded from the bounds, so one chromosome-spanning record can't force a
49
- * whole-chromosome refetch.
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, dontRedispatchSet, opts = {}) {
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, dontRedispatchSet, query.start, query.end);
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([
@@ -23,9 +23,9 @@
23
23
  * empty delta, and `SessionTracks.updateTrackConfiguration` used to read that as
24
24
  * "the edit netted back to the base" and revert the track's working copy — which
25
25
  * undid a removal on screen 400ms after it landed. Promotable slots made that
26
- * reachable rather than rare: the promoted-default snackbar's "Override N
27
- * customized tracks" exists to unset a slot, and an admin config is free to set
28
- * one. That branch now keeps the working copy; see the comment there.
26
+ * reachable rather than rare: a slider row's reset unsets its slot, and an
27
+ * admin config is free to set one. That branch now keeps the working copy; see
28
+ * the comment there.
29
29
  *
30
30
  * `displays` is merged by `displayId` so an edit to one display doesn't pin the
31
31
  * others. Nested config objects (e.g. `adapter`) recurse. Any other array (value
@@ -59,9 +59,9 @@ export declare function mergeTrackConfig(base: Record<string, unknown>, delta: R
59
59
  export interface TrackConfigChange {
60
60
  /**
61
61
  * Where the setting lives, and — for a row a UI can revert — its **address**:
62
- * `resetPreferenceChange` parses a promoted display-type default's path back
63
- * into the type and slot it clears. So a producer that wants a friendlier
64
- * heading sets `label` and leaves this alone.
62
+ * product-core's `resetDisplayDefault` parses a promoted display-type
63
+ * default's path back into the type and slot it clears. So a producer that
64
+ * wants a friendlier heading sets `label` and leaves this alone.
65
65
  */
66
66
  path: string[];
67
67
  from: Json;
@@ -23,9 +23,9 @@
23
23
  * empty delta, and `SessionTracks.updateTrackConfiguration` used to read that as
24
24
  * "the edit netted back to the base" and revert the track's working copy — which
25
25
  * undid a removal on screen 400ms after it landed. Promotable slots made that
26
- * reachable rather than rare: the promoted-default snackbar's "Override N
27
- * customized tracks" exists to unset a slot, and an admin config is free to set
28
- * one. That branch now keeps the working copy; see the comment there.
26
+ * reachable rather than rare: a slider row's reset unsets its slot, and an
27
+ * admin config is free to set one. That branch now keeps the working copy; see
28
+ * the comment there.
29
29
  *
30
30
  * `displays` is merged by `displayId` so an edit to one display doesn't pin the
31
31
  * others. Nested config objects (e.g. `adapter`) recurse. Any other array (value
@@ -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;