@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
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* remembered.
|
|
5
5
|
*
|
|
6
6
|
* The shared `installFetch` skeleton takes this shape, and so does the LGV
|
|
7
|
-
* global family (`installGlobalFetchAutorun`, over
|
|
8
|
-
* was the same contract declared twice, with the same three rules
|
|
9
|
-
* twice, which is a drift axis over rules whose whole value is being
|
|
10
|
-
* everywhere.
|
|
7
|
+
* global family (`installGlobalFetchAutorun`, a declaration over that same
|
|
8
|
+
* skeleton). It was the same contract declared twice, with the same three rules
|
|
9
|
+
* explained twice, which is a drift axis over rules whose whole value is being
|
|
10
|
+
* the same everywhere.
|
|
11
11
|
*
|
|
12
12
|
* `TCtx` is what a family hands its `run`: a `FetchContext` for most of them, a
|
|
13
13
|
* `ComparativeFetchContext` (that plus adapter config and refName rename) for
|
|
@@ -56,11 +56,11 @@ export interface FetchPhases<TArgs, TResult, TCtx> {
|
|
|
56
56
|
* **Nothing it reads is tracked**, so the payload it assembles cannot widen
|
|
57
57
|
* the autorun's dependency set the way calling `rpcProps()` in the body would.
|
|
58
58
|
* Being async is not what buys that — `run` is *called* synchronously, so its
|
|
59
|
-
* own prefix down to its first `await` runs wherever the caller was.
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* own prefix down to its first `await` runs wherever the caller was. The
|
|
60
|
+
* `installFetch` skeleton calls it inside `untracked`, which covers the
|
|
61
|
+
* global and comparative families with it; the per-region family reaches its
|
|
62
|
+
* work through `FetchMixin.runFetch`, an MST flow and so an action, which
|
|
63
|
+
* MobX runs untracked.
|
|
64
64
|
*/
|
|
65
65
|
run: (args: TArgs, ctx: TCtx) => Promise<TResult | undefined>;
|
|
66
66
|
/**
|
package/esm/util/hooks.d.ts
CHANGED
|
@@ -20,6 +20,14 @@ export declare function useWidthSetter(view: {
|
|
|
20
20
|
* "avoiding recreating the ref contents" pattern.
|
|
21
21
|
*/
|
|
22
22
|
export declare function useCreateOnce<T>(create: () => T): T;
|
|
23
|
+
/**
|
|
24
|
+
* `useCreateOnce` for an asynchronous factory: returns undefined until
|
|
25
|
+
* `create` resolves, and rethrows a rejection from render. The value can
|
|
26
|
+
* resolve after the component is gone — a caller whose value is expensive to
|
|
27
|
+
* leak should use product-core's `useAsyncEngineLifecycle`, which discards
|
|
28
|
+
* that late arrival.
|
|
29
|
+
*/
|
|
30
|
+
export declare function useCreateOnceAsync<T>(create: () => Promise<T>): T | undefined;
|
|
23
31
|
/**
|
|
24
32
|
* Run `cleanup` when the component *really* unmounts.
|
|
25
33
|
*
|
package/esm/util/hooks.js
CHANGED
|
@@ -112,6 +112,33 @@ export function useCreateOnce(create) {
|
|
|
112
112
|
ref.current ??= { value: create() };
|
|
113
113
|
return ref.current.value;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* `useCreateOnce` for an asynchronous factory: returns undefined until
|
|
117
|
+
* `create` resolves, and rethrows a rejection from render. The value can
|
|
118
|
+
* resolve after the component is gone — a caller whose value is expensive to
|
|
119
|
+
* leak should use product-core's `useAsyncEngineLifecycle`, which discards
|
|
120
|
+
* that late arrival.
|
|
121
|
+
*/
|
|
122
|
+
export function useCreateOnceAsync(create) {
|
|
123
|
+
const started = useRef(false);
|
|
124
|
+
const [state, setState] = useState();
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (!started.current) {
|
|
127
|
+
started.current = true;
|
|
128
|
+
create().then(value => {
|
|
129
|
+
setState({ value });
|
|
130
|
+
}, (error) => {
|
|
131
|
+
setState({ error });
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// create is read on the first effect only, as in useCreateOnce
|
|
135
|
+
// eslint-disable-next-line @eslint-react/exhaustive-deps
|
|
136
|
+
}, []);
|
|
137
|
+
if (state && 'error' in state) {
|
|
138
|
+
throw state.error;
|
|
139
|
+
}
|
|
140
|
+
return state?.value;
|
|
141
|
+
}
|
|
115
142
|
/**
|
|
116
143
|
* Run `cleanup` when the component *really* unmounts.
|
|
117
144
|
*
|
package/esm/util/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { capitalizeFirst, pluralize, shorten, shorten2, truncateMiddle, } from '
|
|
|
3
3
|
export { formatBytes } from './formatBytes.ts';
|
|
4
4
|
export { getFillProps, getStrokeProps, stripAlpha } from './svgColorProps.ts';
|
|
5
5
|
export { fetchAndMaybeUnzip, fetchAndMaybeUnzipText, isGzip, } from './fetchAndMaybeUnzip.ts';
|
|
6
|
-
export { type AbstractDisplayModel, type AbstractMenuManager, type AbstractRootModel, type AbstractSessionModel, type AbstractTrackModel, type AbstractViewContainer, type AbstractViewModel, type AnimationMode, type AnyReactComponentType, type AppRootModel, type AssemblyHost, type AssemblyManager, type AugmentedRegion, AuthNeededError, type BasePlugin, type BlobLocation, type ClassReturnedBy, type ConnectionInstance, type DialogComponentType, type DialogHost, type DrawerPosition, type FileHandleLocation, type FileLocation, type InstanceTypeRestrictive, type JBrowsePlugin, type JBrowsePluginVersion, type LocalPathLocation, type NoAssemblyRegion, type NotificationLevel, type NotificationSink, type PaletteHost, type PreBlobLocation, type PreFileHandleLocation, type PreFileLocation, type PreLocalPathLocation, type PreUriLocation, type Region, type RenderingServices, type RpcCaller, type RpcHost, type RootModelWithInternetAccounts, type SelectionContainer, type SessionServices, type SessionWithAddAssembly, type SessionWithAddSessionTrack, type SessionWithPublishTrackConf, type SessionWithConfigEditing, type SessionWithConnectionEditing, type SessionWithConnections, type SessionWithDeleteTrackConf, type SessionWithDrawerWidgets, type SessionWithFocusedViewAndDrawerWidgets, type SessionWithPermanentPlugins, type SessionWithSessionPlugins, type SessionWithShareURL, type SessionWithViewReplacement, type SessionWithWidgets, type SnackAction, type TrackActionView, type TrackCatalog, type TrackContainer, type TrackViewModel, type TypeTestedByPredicate, type UriLocation, type Widget, type WidgetMap, addOrReplaceView, canReplaceView, isAbstractMenuManager, isAppRootModel, isAuthNeededException, isBlobLocation, isDisplayModel, isFileHandleLocation, isLocalPathLocation, isRootModelWithInternetAccounts, isSelectionContainer, isSessionModel, isSessionServices, isSessionModelWithConfigEditing, isSessionModelWithConnectionEditing, isSessionModelWithConnections, isSessionModelWithWidgets, isSessionWithAddAssembly, isSessionWithAddSessionTrack, isSessionWithAddTracks, isSessionWithPublishTrackConf, isSessionWithDeleteTrackConf, isSessionWithPermanentPlugins, isSessionWithSessionPlugins, isSessionWithSessionTracks, isSessionWithShareURL, isSessionWithViewReplacement, isTrackModel, isTrackViewModel, isUriLocation, isViewContainer, isViewModel, } from './types/index.ts';
|
|
6
|
+
export { type AbstractDisplayModel, type AbstractMenuManager, type AbstractRootModel, type AbstractSessionModel, type AbstractTrackModel, type AbstractViewContainer, type AbstractViewModel, type AnimationMode, type AnyReactComponentType, type AppRootModel, type AssemblyHost, type AssemblyManager, type AugmentedRegion, AuthNeededError, type BasePlugin, type BlobLocation, type ClassReturnedBy, type ConnectionInstance, type DialogComponentType, type DialogHost, type DrawerPosition, type FileHandleLocation, type FileLocation, type InstanceTypeRestrictive, type JBrowsePlugin, type JBrowsePluginVersion, type LocalPathLocation, type NoAssemblyRegion, type NotificationLevel, type NotificationSink, type PaletteHost, type PreBlobLocation, type PreFileHandleLocation, type PreFileLocation, type PreLocalPathLocation, type PreUriLocation, type Region, type RenderingServices, type RpcCaller, type RpcHost, type RootModelWithInternetAccounts, type SelectionContainer, type SessionServices, type SessionWithAddAssembly, type SessionWithAddSessionTrack, type SessionWithPublishTrackConf, type SessionWithConfigEditing, type SessionWithConnectionEditing, type SessionWithConnections, type SessionWithDeleteTrackConf, type SessionWithDrawerWidgets, type SessionWithFocusedViewAndDrawerWidgets, type SessionWithPermanentPlugins, type SessionWithSessionPlugins, type SessionWithShareURL, type SessionWithViewReplacement, type SessionWithWidgets, type SnackAction, type TrackActionView, type TrackCatalog, type TrackContainer, type TrackViewModel, type TypeTestedByPredicate, type UriLocation, type Widget, type WidgetMap, addOrReplaceView, canReplaceView, isAbstractMenuManager, isAppRootModel, isAuthNeededException, isBlobLocation, isDisplayModel, isFileHandleLocation, isLocalPathLocation, isRootModelWithInternetAccounts, isSelectionContainer, isSessionModel, isSessionServices, isSessionModelWithConfigEditing, isSessionModelWithConnectionEditing, isSessionModelWithConnections, isSessionModelWithWidgets, isSessionWithAddAssembly, isSessionWithAddSessionTrack, isSessionWithAddTracks, isSessionWithPublishTrackConf, isSessionWithDeleteTrackConf, isSessionWithMultipleViews, isSessionWithPermanentPlugins, isSessionWithSessionPlugins, isSessionWithSessionTracks, isSessionWithShareURL, isSessionWithViewReplacement, isTrackModel, isTrackViewModel, isUriLocation, isViewContainer, isViewModel, } from './types/index.ts';
|
|
7
7
|
export { type PluginUpdate, type ResolvedPlugin, type StorePluginFailure, type StorePluginResolution, getPluginUpdate, installablePlugins, installedVersionFromUrl, isPluginInstalled, resolvePlugin, resolveStorePluginRefs, resolveStoreRefs, } from './pluginStore.ts';
|
|
8
8
|
export { type PhaseFailure, type ProgressReporter, type RpcStatus, type StatusCallback, type StatusPhase, type StatusStream, type StatusWindow, type StatusWithProgress, createProgressReporter, createStatusFanOut, createStatusWindow, downloadStatus, progressLabel, statusFraction, statusMessageText, statusProgressLabel, statusReading, statusSource, throttleStatusEmits, updateStatus, withProgress, } from './progress.ts';
|
|
9
9
|
export { calculateRedispatchRange, doesIntersect2, intersection2, } from './range.ts';
|
|
@@ -46,7 +46,7 @@ export { isRpcResult } from './rpc.ts';
|
|
|
46
46
|
export { aesDecrypt, aesEncrypt, sha256, sha256Base64, sha256Base64Url, toBase64, toBase64Url, } from './crypto.ts';
|
|
47
47
|
export { type FeatureWidgetTypeRef, notifyFeatureDetailsMiss, openFeatureWidget, } from './openFeatureWidget.ts';
|
|
48
48
|
export { withFeatureDetails } from './withFeatureDetails.ts';
|
|
49
|
-
export { canonicalizeViewRefName, findParentThat, findParentThatIs, getAssemblyHost, getContainingDisplay, getContainingTrack, getContainingView, getEnv, getRenderingServices, getSession, hashCode, objectHash, } from './mstUtils.ts';
|
|
49
|
+
export { canonicalizeViewRefName, findParentThat, findParentThatIs, getAssemblyHost, getContainingDisplay, getContainingTrack, getContainingView, getEnv, getRenderingServices, launchOrReplaceView, getSession, hashCode, objectHash, } from './mstUtils.ts';
|
|
50
50
|
export { getDialogHost, getNotificationSink, getPaletteHost, getRpcHost, getSessionServices, } from './sessionServices.ts';
|
|
51
51
|
/**
|
|
52
52
|
* Convert a browser File (from a drop zone or file input) into a FileLocation:
|
|
@@ -56,7 +56,6 @@ export declare function fileToLocation(file: File): FileLocation;
|
|
|
56
56
|
export { type Frame, complement, complementTable, defaultStarts, getFrame, revcom, reverse, stitch, } from './seqUtils.ts';
|
|
57
57
|
export { revlist } from './revlist.ts';
|
|
58
58
|
export { useCreateOnce, useDebounce, useFinalUnmount, useLocalStorage, useWidthSetter, } from './hooks.ts';
|
|
59
|
-
export { renderToStaticMarkup } from './renderToStaticMarkup.ts';
|
|
60
59
|
export { codonTable, defaultCodonTable, generateCodonTable, } from './geneticCodes.ts';
|
|
61
60
|
export { IUPAC_MOTIF_REGEX, isPalindromic, iupacToRegex, reverseComplementIupac, } from './iupac.ts';
|
|
62
61
|
export { type MotifListParse, type MotifParseError, type ParsedMotif, parseMotifList, } from './parseMotifList.ts';
|
package/esm/util/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { capitalizeFirst, pluralize, shorten, shorten2, truncateMiddle, } from '
|
|
|
12
12
|
export { formatBytes } from './formatBytes.js';
|
|
13
13
|
export { getFillProps, getStrokeProps, stripAlpha } from './svgColorProps.js';
|
|
14
14
|
export { fetchAndMaybeUnzip, fetchAndMaybeUnzipText, isGzip, } from './fetchAndMaybeUnzip.js';
|
|
15
|
-
export { AuthNeededError, addOrReplaceView, canReplaceView, isAbstractMenuManager, isAppRootModel, isAuthNeededException, isBlobLocation, isDisplayModel, isFileHandleLocation, isLocalPathLocation, isRootModelWithInternetAccounts, isSelectionContainer, isSessionModel, isSessionServices, isSessionModelWithConfigEditing, isSessionModelWithConnectionEditing, isSessionModelWithConnections, isSessionModelWithWidgets, isSessionWithAddAssembly, isSessionWithAddSessionTrack, isSessionWithAddTracks, isSessionWithPublishTrackConf, isSessionWithDeleteTrackConf, isSessionWithPermanentPlugins, isSessionWithSessionPlugins, isSessionWithSessionTracks, isSessionWithShareURL, isSessionWithViewReplacement, isTrackModel, isTrackViewModel, isUriLocation, isViewContainer, isViewModel, } from './types/index.js';
|
|
15
|
+
export { AuthNeededError, addOrReplaceView, canReplaceView, isAbstractMenuManager, isAppRootModel, isAuthNeededException, isBlobLocation, isDisplayModel, isFileHandleLocation, isLocalPathLocation, isRootModelWithInternetAccounts, isSelectionContainer, isSessionModel, isSessionServices, isSessionModelWithConfigEditing, isSessionModelWithConnectionEditing, isSessionModelWithConnections, isSessionModelWithWidgets, isSessionWithAddAssembly, isSessionWithAddSessionTrack, isSessionWithAddTracks, isSessionWithPublishTrackConf, isSessionWithDeleteTrackConf, isSessionWithMultipleViews, isSessionWithPermanentPlugins, isSessionWithSessionPlugins, isSessionWithSessionTracks, isSessionWithShareURL, isSessionWithViewReplacement, isTrackModel, isTrackViewModel, isUriLocation, isViewContainer, isViewModel, } from './types/index.js';
|
|
16
16
|
export { getPluginUpdate, installablePlugins, installedVersionFromUrl, isPluginInstalled, resolvePlugin, resolveStorePluginRefs, resolveStoreRefs, } from './pluginStore.js';
|
|
17
17
|
export { createProgressReporter, createStatusFanOut, createStatusWindow, downloadStatus, progressLabel, statusFraction, statusMessageText, statusProgressLabel, statusReading, statusSource, throttleStatusEmits, updateStatus, withProgress, } from './progress.js';
|
|
18
18
|
export { calculateRedispatchRange, doesIntersect2, intersection2, } from './range.js';
|
|
@@ -53,7 +53,7 @@ export { isRpcResult } from './rpc.js';
|
|
|
53
53
|
export { aesDecrypt, aesEncrypt, sha256, sha256Base64, sha256Base64Url, toBase64, toBase64Url, } from './crypto.js';
|
|
54
54
|
export { notifyFeatureDetailsMiss, openFeatureWidget, } from './openFeatureWidget.js';
|
|
55
55
|
export { withFeatureDetails } from './withFeatureDetails.js';
|
|
56
|
-
export { canonicalizeViewRefName, findParentThat, findParentThatIs, getAssemblyHost, getContainingDisplay, getContainingTrack, getContainingView, getEnv, getRenderingServices, getSession, hashCode, objectHash, } from './mstUtils.js';
|
|
56
|
+
export { canonicalizeViewRefName, findParentThat, findParentThatIs, getAssemblyHost, getContainingDisplay, getContainingTrack, getContainingView, getEnv, getRenderingServices, launchOrReplaceView, getSession, hashCode, objectHash, } from './mstUtils.js';
|
|
57
57
|
export { getDialogHost, getNotificationSink, getPaletteHost, getRpcHost, getSessionServices, } from './sessionServices.js';
|
|
58
58
|
// this is recommended in a later comment in
|
|
59
59
|
// https://github.com/electron/electron/issues/2288 for detecting electron in a
|
|
@@ -91,7 +91,6 @@ export { revlist } from './revlist.js';
|
|
|
91
91
|
// without it that is `(0, PR.useLocalStorage) is not a function` when the panel
|
|
92
92
|
// renders. The implementations never went anywhere -- only these lines did.
|
|
93
93
|
export { useCreateOnce, useDebounce, useFinalUnmount, useLocalStorage, useWidthSetter, } from './hooks.js';
|
|
94
|
-
export { renderToStaticMarkup } from './renderToStaticMarkup.js';
|
|
95
94
|
export { codonTable, defaultCodonTable, generateCodonTable, } from './geneticCodes.js';
|
|
96
95
|
export { IUPAC_MOTIF_REGEX, isPalindromic, iupacToRegex, reverseComplementIupac, } from './iupac.js';
|
|
97
96
|
export { parseMotifList, } from './parseMotifList.js';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { ActiveFetch, StatusReporter } from './createStopTokenRotation.ts';
|
|
1
|
+
import type { ActiveFetch, StatusReporter, StopTokenRotation } from './createStopTokenRotation.ts';
|
|
2
2
|
import type { FetchContext } from './fetchContext.ts';
|
|
3
3
|
import type { FetchPhases } from './fetchPhases.ts';
|
|
4
|
+
import type { StopToken } from './stopToken.ts';
|
|
4
5
|
import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
5
6
|
/**
|
|
6
7
|
* The bookkeeping a family hangs off one fetch, beside the phases. Each of the
|
|
@@ -27,8 +28,13 @@ export interface FetchLifecycle {
|
|
|
27
28
|
* the comparative family's `fetching`), and anything a display blanks
|
|
28
29
|
* because it has no freshness signature to keep stale data honest with
|
|
29
30
|
* (chord's refName map).
|
|
31
|
+
*
|
|
32
|
+
* Takes the token rather than the whole {@link ActiveFetch}: publishing it is
|
|
33
|
+
* the only thing any implementation does with the fetch, and the guard and
|
|
34
|
+
* the status slot are the skeleton's to hand out — through `ctx`, where a
|
|
35
|
+
* `run` gets them pre-wired.
|
|
30
36
|
*/
|
|
31
|
-
onBegin?: (
|
|
37
|
+
onBegin?: (stopToken: StopToken) => void;
|
|
32
38
|
/**
|
|
33
39
|
* Runs in the `finally`, before the rotation's `end()`. `current` is this
|
|
34
40
|
* run's own guard, already evaluated: a **superseded** run must not clear the
|
|
@@ -80,7 +86,7 @@ export interface FetchSkeletonHost extends IStateTreeNode {
|
|
|
80
86
|
reloadCounter: number;
|
|
81
87
|
/**
|
|
82
88
|
* The states where this host deliberately fetches nothing, read only by the
|
|
83
|
-
*
|
|
89
|
+
* retry contract check. A decline there is not a dead Retry.
|
|
84
90
|
*/
|
|
85
91
|
fetchInert: boolean;
|
|
86
92
|
/**
|
|
@@ -97,19 +103,11 @@ export interface FetchSkeletonHost extends IStateTreeNode {
|
|
|
97
103
|
*/
|
|
98
104
|
fetchCanceled?: boolean;
|
|
99
105
|
}
|
|
100
|
-
|
|
106
|
+
interface InstallFetchOptionsBase<TArgs, TResult> extends FetchPhases<TArgs, TResult, FetchContext>, FetchLifecycle {
|
|
101
107
|
/** autorun name, for the MobX devtools/spy stream */
|
|
102
108
|
name: string;
|
|
103
109
|
/** how long to coalesce reruns once the body has started work once */
|
|
104
110
|
delay: number;
|
|
105
|
-
/**
|
|
106
|
-
* Where this fetch's status lands. A display passes itself — its status
|
|
107
|
-
* fields are part of its own API, and one composing `FetchMixin` lends the
|
|
108
|
-
* model-wide window so this fetch takes a slot on the one field beside the
|
|
109
|
-
* display's other operations rather than opening a second writer over it.
|
|
110
|
-
* Anything with one operation to narrate passes a `createStatusChannel`.
|
|
111
|
-
*/
|
|
112
|
-
report: StatusReporter;
|
|
113
111
|
/**
|
|
114
112
|
* An extra skip above `prepare`, reported as `gated` rather than `declined`:
|
|
115
113
|
* the skeleton decided before the family's own gate was consulted, so it is
|
|
@@ -161,7 +159,7 @@ export interface InstallFetchOptions<TArgs, TResult> extends FetchPhases<TArgs,
|
|
|
161
159
|
*/
|
|
162
160
|
committedKey?: () => string | undefined;
|
|
163
161
|
/**
|
|
164
|
-
* Install the two
|
|
162
|
+
* Install the two display-contract checks under this name. Omitted
|
|
165
163
|
* by a **secondary** fetch on a host whose primary foundation already
|
|
166
164
|
* installed them: `assertDisplayContract` would report the second install as
|
|
167
165
|
* the double-attach it is built to catch, and a second retry check would
|
|
@@ -169,19 +167,46 @@ export interface InstallFetchOptions<TArgs, TResult> extends FetchPhases<TArgs,
|
|
|
169
167
|
*/
|
|
170
168
|
contract?: string;
|
|
171
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* The rotation half is either/or: `report` says where a rotation this installer
|
|
172
|
+
* creates for itself reports, and `rotation` lends the host's own instead — a
|
|
173
|
+
* display composing `FetchMixin` passes its `fetchRotation`, so `cancelFetch`
|
|
174
|
+
* and `cancelFetchByUser` reach the fetch this installs rather than a second
|
|
175
|
+
* rotation they cannot see. A lent rotation already carries its status window,
|
|
176
|
+
* so `report` would be dead beside it — the same either/or `StatusReporter`
|
|
177
|
+
* itself is a union for.
|
|
178
|
+
*/
|
|
179
|
+
export type InstallFetchOptions<TArgs, TResult> = InstallFetchOptionsBase<TArgs, TResult> & ({
|
|
180
|
+
/**
|
|
181
|
+
* Where this fetch's status lands. A display passes itself — its status
|
|
182
|
+
* fields are part of its own API, and one composing `FetchMixin` lends
|
|
183
|
+
* the model-wide window so this fetch takes a slot on the one field
|
|
184
|
+
* beside the display's other operations rather than opening a second
|
|
185
|
+
* writer over it. Anything with one operation to narrate passes a
|
|
186
|
+
* `createStatusChannel`.
|
|
187
|
+
*/
|
|
188
|
+
report: StatusReporter;
|
|
189
|
+
rotation?: never;
|
|
190
|
+
} | {
|
|
191
|
+
/** the host's own latest-wins rotation, lent whole — see above */
|
|
192
|
+
rotation: StopTokenRotation;
|
|
193
|
+
report?: never;
|
|
194
|
+
});
|
|
172
195
|
/**
|
|
173
196
|
* A whole fetch, installed: the rotation, the leading-edge autorun, the
|
|
174
197
|
* unconditional trigger reads, the gates, and {@link runFetchOnce} under them.
|
|
175
198
|
*
|
|
176
|
-
* Every fetch in the tree is this shape except one — the prerequisite
|
|
177
|
-
* (HiC's header, the multi-sample sample list),
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* `
|
|
199
|
+
* Every fetch in the tree is this shape except one family — the prerequisite
|
|
200
|
+
* reads (HiC's header, the multi-sample sample list), the LGV global family
|
|
201
|
+
* (`installGlobalFetchAutorun`, which lends `FetchMixin`'s rotation through the
|
|
202
|
+
* `rotation` option so `cancelFetch` reaches the fetch it installs), both
|
|
203
|
+
* comparative displays, the circular view's chord fetch, the breakpoint overlay
|
|
204
|
+
* fetch and the multi-way synteny display's two DEPENDENT fetches (lane genes,
|
|
205
|
+
* lane links, each gated on the key its own commit stamps) all take it. The
|
|
206
|
+
* exception is the per-region family's `FetchMixin.runFetch`: its trigger is
|
|
207
|
+
* `planRegionFetch`'s autorun and its commits stream per region through
|
|
208
|
+
* `ctx.commitRegion`, so it holds `runFetchOnce` directly and the autorun above
|
|
209
|
+
* it is that foundation's.
|
|
185
210
|
*
|
|
186
211
|
* What a caller supplies is exactly what differs between them: which reads wake
|
|
187
212
|
* it (`prepare`, plus `gate`), what a round trip commits, where the loading
|
|
@@ -191,4 +216,5 @@ export interface InstallFetchOptions<TArgs, TResult> extends FetchPhases<TArgs,
|
|
|
191
216
|
* Cancel button: the flag alone is not a cancel — nothing rotates the token, the
|
|
192
217
|
* stopped RPC stays current, and it commits over the load the user just stopped.
|
|
193
218
|
*/
|
|
194
|
-
export declare function installFetch<TArgs, TResult>(self: FetchSkeletonHost,
|
|
219
|
+
export declare function installFetch<TArgs, TResult>(self: FetchSkeletonHost, opts: InstallFetchOptions<TArgs, TResult>): () => void;
|
|
220
|
+
export {};
|
package/esm/util/installFetch.js
CHANGED
|
@@ -33,7 +33,7 @@ export async function runFetchOnce(self, active, args, { run, commit, setError,
|
|
|
33
33
|
const { stopToken, isCurrent, statusCallback, end } = active;
|
|
34
34
|
// synchronous, so it lands while this run is by construction the current one
|
|
35
35
|
setError(undefined);
|
|
36
|
-
onBegin?.(
|
|
36
|
+
onBegin?.(stopToken);
|
|
37
37
|
try {
|
|
38
38
|
const result = await run(args, makeFetchContext(self, {
|
|
39
39
|
stopToken,
|
|
@@ -65,7 +65,7 @@ export async function runFetchOnce(self, active, args, { run, commit, setError,
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* The two
|
|
68
|
+
* The two contract checks every fetch installer owes, together because they are
|
|
69
69
|
* the same doctrine: one reports a foundation attached twice, the other reports
|
|
70
70
|
* a `reload()` the gate still declines — the dead Retry button.
|
|
71
71
|
*/
|
|
@@ -73,19 +73,33 @@ function installContractChecks(self, contract) {
|
|
|
73
73
|
assertDisplayContract(self, contract);
|
|
74
74
|
return makeRetryContractCheck(self);
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* The rotation for an installation that was lent none: created here, so
|
|
78
|
+
* disposed here — a lent one is the host's, and resetting its status window
|
|
79
|
+
* would drop a write the host's other operations are still owed.
|
|
80
|
+
*/
|
|
81
|
+
function ownRotation(self, report) {
|
|
82
|
+
const rotation = createStopTokenRotation(self, report);
|
|
83
|
+
addDisposer(self, () => {
|
|
84
|
+
rotation.dispose();
|
|
85
|
+
});
|
|
86
|
+
return rotation;
|
|
87
|
+
}
|
|
76
88
|
/**
|
|
77
89
|
* A whole fetch, installed: the rotation, the leading-edge autorun, the
|
|
78
90
|
* unconditional trigger reads, the gates, and {@link runFetchOnce} under them.
|
|
79
91
|
*
|
|
80
|
-
* Every fetch in the tree is this shape except one — the prerequisite
|
|
81
|
-
* (HiC's header, the multi-sample sample list),
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* `
|
|
92
|
+
* Every fetch in the tree is this shape except one family — the prerequisite
|
|
93
|
+
* reads (HiC's header, the multi-sample sample list), the LGV global family
|
|
94
|
+
* (`installGlobalFetchAutorun`, which lends `FetchMixin`'s rotation through the
|
|
95
|
+
* `rotation` option so `cancelFetch` reaches the fetch it installs), both
|
|
96
|
+
* comparative displays, the circular view's chord fetch, the breakpoint overlay
|
|
97
|
+
* fetch and the multi-way synteny display's two DEPENDENT fetches (lane genes,
|
|
98
|
+
* lane links, each gated on the key its own commit stamps) all take it. The
|
|
99
|
+
* exception is the per-region family's `FetchMixin.runFetch`: its trigger is
|
|
100
|
+
* `planRegionFetch`'s autorun and its commits stream per region through
|
|
101
|
+
* `ctx.commitRegion`, so it holds `runFetchOnce` directly and the autorun above
|
|
102
|
+
* it is that foundation's.
|
|
89
103
|
*
|
|
90
104
|
* What a caller supplies is exactly what differs between them: which reads wake
|
|
91
105
|
* it (`prepare`, plus `gate`), what a round trip commits, where the loading
|
|
@@ -95,16 +109,23 @@ function installContractChecks(self, contract) {
|
|
|
95
109
|
* Cancel button: the flag alone is not a cancel — nothing rotates the token, the
|
|
96
110
|
* stopped RPC stays current, and it commits over the load the user just stopped.
|
|
97
111
|
*/
|
|
98
|
-
export function installFetch(self,
|
|
112
|
+
export function installFetch(self, opts) {
|
|
113
|
+
const { name, delay, gate, contract, fetchKey, committedKey, prepare, run, commit, setError, onBegin, onEnd, } = opts;
|
|
99
114
|
const noteFetchAutorunRun = contract === undefined ? undefined : installContractChecks(self, contract);
|
|
100
|
-
const
|
|
115
|
+
const lent = opts.rotation;
|
|
116
|
+
const rotation = lent === undefined ? ownRotation(self, opts.report) : lent;
|
|
101
117
|
// The `reloadCounter` this installation last issued a fetch at. Per
|
|
102
118
|
// installation and per instance, so a host with several fetches gives each its
|
|
103
119
|
// own — and a closure rather than model state, because nothing outside reads
|
|
104
120
|
// it and a volatile would be one more thing a host has to declare.
|
|
105
121
|
let issuedEpoch;
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
// The skeleton's own stamp exists only where there is a key to stamp — a
|
|
123
|
+
// fetch with no `fetchKey` never reads it, so allocating one would only feed
|
|
124
|
+
// it `undefined` on every commit.
|
|
125
|
+
const stamp = committedKey === undefined && fetchKey !== undefined
|
|
126
|
+
? observable.box()
|
|
127
|
+
: undefined;
|
|
128
|
+
const heldKey = committedKey ?? (() => stamp?.get());
|
|
108
129
|
const commitAndStamp = stamp
|
|
109
130
|
? (result, args) => {
|
|
110
131
|
runInAction(() => {
|
|
@@ -113,9 +134,6 @@ export function installFetch(self, { name, delay, report, gate, contract, fetchK
|
|
|
113
134
|
});
|
|
114
135
|
}
|
|
115
136
|
: commit;
|
|
116
|
-
addDisposer(self, () => {
|
|
117
|
-
rotation.dispose();
|
|
118
|
-
});
|
|
119
137
|
leadingEdgeAutorun(self,
|
|
120
138
|
// #region voidTracking
|
|
121
139
|
() => {
|
|
@@ -128,13 +146,23 @@ export function installFetch(self, { name, delay, report, gate, contract, fetchK
|
|
|
128
146
|
// Cancel would be a one-way door with a Retry button on it. Order, not
|
|
129
147
|
// just position: counter first, then this, then the gates.
|
|
130
148
|
const canceled = self.fetchCanceled === true;
|
|
149
|
+
// Above the gate, not only above `prepare`: teardown mutates the
|
|
150
|
+
// observables this body reads before the disposers run, and every `gate`
|
|
151
|
+
// but the breakpoint view's reaches the containing view or track through a
|
|
152
|
+
// parent walk (`host.initialized`, `isMinimized`, `getContainingView`),
|
|
153
|
+
// which throws once the node has left the tree. This catches the destroyed
|
|
154
|
+
// node; the detached-and-still-alive window (ADR-069) is covered only by
|
|
155
|
+
// the walk's own cache. Nothing is reported for a dead node — there is no
|
|
156
|
+
// Retry button left to be dead, and the check would read three more
|
|
157
|
+
// members of a corpse to decide it.
|
|
158
|
+
if (!isAlive(self)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
131
161
|
if (canceled || gate?.() === false) {
|
|
132
162
|
noteFetchAutorunRun?.('gated');
|
|
133
163
|
return false;
|
|
134
164
|
}
|
|
135
|
-
|
|
136
|
-
// and getContainingView on a detached node warns then throws.
|
|
137
|
-
const args = isAlive(self) ? prepare() : undefined;
|
|
165
|
+
const args = prepare();
|
|
138
166
|
if (args === undefined) {
|
|
139
167
|
noteFetchAutorunRun?.('declined');
|
|
140
168
|
return false;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
2
|
interface InitAutorunHost<T> extends IStateTreeNode {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
pendingLaunch?: T;
|
|
4
|
+
setLaunch: (launch?: T) => void;
|
|
5
5
|
setError: (error: unknown) => void;
|
|
6
6
|
}
|
|
7
7
|
export interface InitApplyContext {
|
|
8
8
|
/**
|
|
9
9
|
* True once this init can no longer be the one being applied: the node is
|
|
10
|
-
* gone, or a newer `
|
|
10
|
+
* gone, or a newer `setLaunch` has replaced it. Any mid-apply wait that can
|
|
11
11
|
* park indefinitely must fold this into its condition — an unbounded wait on
|
|
12
12
|
* state that never arrives holds the drain open, and the newer init that
|
|
13
13
|
* would have superseded it is exactly what gets stranded. This is the causal
|
|
@@ -35,7 +35,7 @@ interface InitAutorunOptions<T> {
|
|
|
35
35
|
apply: (init: T, ctx: InitApplyContext) => Promise<void>;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* The
|
|
38
|
+
* The launch state machine shared by the views that take a declarative launch
|
|
39
39
|
* blob (LinearGenomeView, DotplotView, LinearSyntenyView): the re-entry guard,
|
|
40
40
|
* the readiness gate, the serialized drain, the identity-checked clear, the
|
|
41
41
|
* isAlive guards, and one failure policy.
|
|
@@ -43,18 +43,18 @@ interface InitAutorunOptions<T> {
|
|
|
43
43
|
* The failure policy keys off `materialized`, the same line each view's
|
|
44
44
|
* postProcessSnapshot already draws for persistence:
|
|
45
45
|
*
|
|
46
|
-
* - Not materialized — nothing is on screen and `
|
|
46
|
+
* - Not materialized — nothing is on screen and `launch` is still persisted, so
|
|
47
47
|
* keep it for a reload retry and `setError`. That flips the view onto its
|
|
48
48
|
* import form, which renders the error in its own banner; a snackbar would
|
|
49
49
|
* state the same failure twice and the banner is the one that persists.
|
|
50
50
|
* - Materialized — the view works, so a later failure is one sub-step's
|
|
51
51
|
* problem and must not take the view down with it. `setError` would, since
|
|
52
52
|
* showImportForm keys off `error` alone and would discard rows that loaded
|
|
53
|
-
* fine, so report it as a snackbar instead. Clearing `
|
|
53
|
+
* fine, so report it as a snackbar instead. Clearing `launch` is also what
|
|
54
54
|
* disarms the re-fire: `ready` usually folds in the measured width, so
|
|
55
|
-
* leaving it set lets any resize re-run `apply` over a half-applied
|
|
55
|
+
* leaving it set lets any resize re-run `apply` over a half-applied blob.
|
|
56
56
|
*
|
|
57
|
-
* `apply` therefore never clears `
|
|
57
|
+
* `apply` therefore never clears `launch` and never catches for reporting — it
|
|
58
58
|
* catches only the failures it wants to keep going through (a bad locstring in
|
|
59
59
|
* one row), and everything it lets escape is fatal-as-of-that-point.
|
|
60
60
|
*/
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { addDisposer, isAlive } from '@jbrowse/mobx-state-tree';
|
|
2
2
|
import { autorun } from 'mobx';
|
|
3
3
|
import { getNotificationSink } from './sessionServices.js';
|
|
4
|
-
// Clear only when
|
|
5
|
-
// lands while `apply` is awaiting (React StrictMode remounts, a
|
|
6
|
-
// re-launch) swaps in a new object; clearing unconditionally would
|
|
7
|
-
// drop
|
|
4
|
+
// Clear only when the pending blob is still the exact object we applied. A
|
|
5
|
+
// setLaunch that lands while `apply` is awaiting (React StrictMode remounts, a
|
|
6
|
+
// programmatic re-launch) swaps in a new object; clearing unconditionally would
|
|
7
|
+
// silently drop it. Leaving it lets the drain loop apply it next.
|
|
8
8
|
function clearIfUnchanged(self, init) {
|
|
9
|
-
if (isAlive(self) && self.
|
|
10
|
-
self.
|
|
9
|
+
if (isAlive(self) && self.pendingLaunch === init) {
|
|
10
|
+
self.setLaunch(undefined);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
async function applyInitOnce(self, init, { apply, materialized }) {
|
|
14
14
|
try {
|
|
15
15
|
await apply(init, {
|
|
16
|
-
superseded: () => !isAlive(self) || self.
|
|
16
|
+
superseded: () => !isAlive(self) || self.pendingLaunch !== init,
|
|
17
17
|
});
|
|
18
18
|
clearIfUnchanged(self, init);
|
|
19
19
|
}
|
|
@@ -34,7 +34,7 @@ async function applyInitOnce(self, init, { apply, materialized }) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
function currentInit(self, { ready }) {
|
|
37
|
-
return isAlive(self) && ready() ? self.
|
|
37
|
+
return isAlive(self) && ready() ? self.pendingLaunch : undefined;
|
|
38
38
|
}
|
|
39
39
|
// Apply pending inits one at a time until none remain. Serializing (rather
|
|
40
40
|
// than letting the autorun run `apply` concurrently) is what keeps re-entrant
|
|
@@ -47,13 +47,13 @@ async function drainInit(self, opts) {
|
|
|
47
47
|
while (pending !== undefined) {
|
|
48
48
|
await applyInitOnce(self, pending, opts);
|
|
49
49
|
const next = currentInit(self, opts);
|
|
50
|
-
// a fatal failure keeps `
|
|
50
|
+
// a fatal failure keeps `launch` for a reload retry; the same object still
|
|
51
51
|
// sitting there means it was not consumed, so stop rather than spin on it
|
|
52
52
|
pending = next === pending ? undefined : next;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* The
|
|
56
|
+
* The launch state machine shared by the views that take a declarative launch
|
|
57
57
|
* blob (LinearGenomeView, DotplotView, LinearSyntenyView): the re-entry guard,
|
|
58
58
|
* the readiness gate, the serialized drain, the identity-checked clear, the
|
|
59
59
|
* isAlive guards, and one failure policy.
|
|
@@ -61,18 +61,18 @@ async function drainInit(self, opts) {
|
|
|
61
61
|
* The failure policy keys off `materialized`, the same line each view's
|
|
62
62
|
* postProcessSnapshot already draws for persistence:
|
|
63
63
|
*
|
|
64
|
-
* - Not materialized — nothing is on screen and `
|
|
64
|
+
* - Not materialized — nothing is on screen and `launch` is still persisted, so
|
|
65
65
|
* keep it for a reload retry and `setError`. That flips the view onto its
|
|
66
66
|
* import form, which renders the error in its own banner; a snackbar would
|
|
67
67
|
* state the same failure twice and the banner is the one that persists.
|
|
68
68
|
* - Materialized — the view works, so a later failure is one sub-step's
|
|
69
69
|
* problem and must not take the view down with it. `setError` would, since
|
|
70
70
|
* showImportForm keys off `error` alone and would discard rows that loaded
|
|
71
|
-
* fine, so report it as a snackbar instead. Clearing `
|
|
71
|
+
* fine, so report it as a snackbar instead. Clearing `launch` is also what
|
|
72
72
|
* disarms the re-fire: `ready` usually folds in the measured width, so
|
|
73
|
-
* leaving it set lets any resize re-run `apply` over a half-applied
|
|
73
|
+
* leaving it set lets any resize re-run `apply` over a half-applied blob.
|
|
74
74
|
*
|
|
75
|
-
* `apply` therefore never clears `
|
|
75
|
+
* `apply` therefore never clears `launch` and never catches for reporting — it
|
|
76
76
|
* catches only the failures it wants to keep going through (a bad locstring in
|
|
77
77
|
* one row), and everything it lets escape is fatal-as-of-that-point.
|
|
78
78
|
*/
|
|
@@ -93,7 +93,7 @@ export function installInitAutorun(self, opts) {
|
|
|
93
93
|
// reading a detached node throws — here into an unawaited promise.
|
|
94
94
|
const alive = isAlive(self);
|
|
95
95
|
const isReady = alive && opts.ready();
|
|
96
|
-
const init = alive ? self.
|
|
96
|
+
const init = alive ? self.pendingLaunch : undefined;
|
|
97
97
|
if (isReady && init !== undefined && !draining) {
|
|
98
98
|
draining = true;
|
|
99
99
|
try {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
/**
|
|
3
|
+
* #api core/util
|
|
4
|
+
* Run a display's `afterAttach` body from a module loaded on demand, so the
|
|
5
|
+
* autorun installers stay out of the display's eager bundle.
|
|
6
|
+
*
|
|
7
|
+
* One policy for the gap the `await` opens: a node torn down before the module
|
|
8
|
+
* lands installs nothing, and a module that fails to load is reported where the
|
|
9
|
+
* user can see it rather than onto the display's own error slot — that slot is
|
|
10
|
+
* what `reload()` clears, and nothing would re-run the install behind it.
|
|
11
|
+
* Three displays hand-rolled this IIFE and each drew the lines differently.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runLazyAfterAttach<Self extends IStateTreeNode>(self: Self, load: () => Promise<(self: Self) => void>): void;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { isAlive } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
import { getNotificationSink } from './sessionServices.js';
|
|
3
|
+
/**
|
|
4
|
+
* #api core/util
|
|
5
|
+
* Run a display's `afterAttach` body from a module loaded on demand, so the
|
|
6
|
+
* autorun installers stay out of the display's eager bundle.
|
|
7
|
+
*
|
|
8
|
+
* One policy for the gap the `await` opens: a node torn down before the module
|
|
9
|
+
* lands installs nothing, and a module that fails to load is reported where the
|
|
10
|
+
* user can see it rather than onto the display's own error slot — that slot is
|
|
11
|
+
* what `reload()` clears, and nothing would re-run the install behind it.
|
|
12
|
+
* Three displays hand-rolled this IIFE and each drew the lines differently.
|
|
13
|
+
*/
|
|
14
|
+
export function runLazyAfterAttach(self, load) {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
16
|
+
;
|
|
17
|
+
(async () => {
|
|
18
|
+
try {
|
|
19
|
+
const install = await load();
|
|
20
|
+
if (isAlive(self)) {
|
|
21
|
+
install(self);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
if (isAlive(self)) {
|
|
26
|
+
console.error(e);
|
|
27
|
+
getNotificationSink(self).notifyError(`${e}`, e);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})();
|
|
31
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface LegendCandidate {
|
|
2
|
+
rowIndex: number;
|
|
3
|
+
label: string;
|
|
4
|
+
color: number;
|
|
5
|
+
}
|
|
6
|
+
export interface LegendEntry {
|
|
7
|
+
label: string;
|
|
8
|
+
color: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const MAX_LEGEND_ENTRIES = 30;
|
|
11
|
+
export declare 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 declare function createLegendCandidateCollector(maxCandidates?: number): {
|
|
27
|
+
candidates: LegendCandidate[];
|
|
28
|
+
add(rowIndex: number, label: string, color: number): void;
|
|
29
|
+
};
|
|
30
|
+
export interface LegendCandidateSource {
|
|
31
|
+
candidates: readonly LegendCandidate[];
|
|
32
|
+
rowPaintsCandidateColor: (rowIndex: number) => boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The categorical color key over any number of packed regions: one entry per
|
|
36
|
+
* distinct color, labeled by the first name seen in that color, in first-seen
|
|
37
|
+
* order.
|
|
38
|
+
*
|
|
39
|
+
* Keyed by color rather than by name, so each key row is 1:1 with a color — a
|
|
40
|
+
* row is normally a toggle and hiding a category hides features BY color, so two
|
|
41
|
+
* names sharing one color must collapse to a single row. A name reused across
|
|
42
|
+
* two colors keeps its first-seen color.
|
|
43
|
+
*
|
|
44
|
+
* `[]` when the data carries no categorical signal: nothing named, or more than
|
|
45
|
+
* `maxEntries` distinct colors.
|
|
46
|
+
*
|
|
47
|
+
* `regions` is consumed lazily, so a caller able to answer without reading any
|
|
48
|
+
* of them can pass a generator and have none of them touched.
|
|
49
|
+
*/
|
|
50
|
+
export declare function unionLegendCandidates<T>(regions: Iterable<T>, resolve: (region: T) => LegendCandidateSource, maxEntries?: number): LegendEntry[];
|