@jbrowse/product-core 4.2.1 → 5.0.0-beta.1

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 (133) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +629 -304
  2. package/esm/RootModel/BaseRootModel.js +138 -14
  3. package/esm/RootModel/HierarchicalConfig.d.ts +57 -21
  4. package/esm/RootModel/HierarchicalConfig.js +36 -0
  5. package/esm/RootModel/InternetAccounts.d.ts +18 -1
  6. package/esm/RootModel/InternetAccounts.js +120 -22
  7. package/esm/RootModel/PreferencesConfig.d.ts +53 -0
  8. package/esm/RootModel/PreferencesConfig.js +61 -0
  9. package/esm/RootModel/createConfigModel.d.ts +232 -0
  10. package/esm/RootModel/createConfigModel.js +56 -0
  11. package/esm/RootModel/index.d.ts +10 -5
  12. package/esm/RootModel/index.js +10 -5
  13. package/esm/RootModel/menuItems.d.ts +24 -0
  14. package/esm/RootModel/menuItems.js +143 -0
  15. package/esm/Session/BaseSession.d.ts +310 -11
  16. package/esm/Session/BaseSession.js +448 -14
  17. package/esm/Session/Connections.d.ts +114 -8
  18. package/esm/Session/Connections.js +195 -74
  19. package/esm/Session/DrawerWidgets.d.ts +115 -12
  20. package/esm/Session/DrawerWidgets.js +161 -42
  21. package/esm/Session/MultipleViews.d.ts +1266 -1128
  22. package/esm/Session/MultipleViews.js +357 -66
  23. package/esm/Session/Preferences.d.ts +1665 -0
  24. package/esm/Session/Preferences.js +54 -0
  25. package/esm/Session/ReferenceManagement.d.ts +28 -3
  26. package/esm/Session/ReferenceManagement.js +56 -41
  27. package/esm/Session/SessionTracks.d.ts +1275 -1138
  28. package/esm/Session/SessionTracks.js +532 -35
  29. package/esm/Session/Themes.d.ts +87 -2
  30. package/esm/Session/Themes.js +156 -23
  31. package/esm/Session/TrackMenu.d.ts +112 -0
  32. package/esm/Session/TrackMenu.js +200 -0
  33. package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
  34. package/esm/Session/TrackMenuSessionMixin.js +41 -0
  35. package/esm/Session/Tracks.d.ts +1169 -1117
  36. package/esm/Session/Tracks.js +160 -20
  37. package/esm/Session/assertSessionModel.d.ts +21 -0
  38. package/esm/Session/assertSessionModel.js +1 -0
  39. package/esm/Session/finalizeSession.d.ts +16 -0
  40. package/esm/Session/finalizeSession.js +29 -0
  41. package/esm/Session/index.d.ts +22 -10
  42. package/esm/Session/index.js +13 -10
  43. package/esm/Session/shareableSnapshot.d.ts +77 -0
  44. package/esm/Session/shareableSnapshot.js +196 -0
  45. package/esm/Session/temporaryAssemblyTracks.d.ts +23 -0
  46. package/esm/Session/temporaryAssemblyTracks.js +38 -0
  47. package/esm/assertExtends.d.ts +55 -0
  48. package/esm/assertExtends.js +1 -0
  49. package/esm/controllerTracks.d.ts +56 -0
  50. package/esm/controllerTracks.js +77 -0
  51. package/esm/destroyViewState.d.ts +21 -0
  52. package/esm/destroyViewState.js +29 -0
  53. package/esm/index.d.ts +31 -4
  54. package/esm/index.js +31 -4
  55. package/esm/loadPlugins.d.ts +33 -0
  56. package/esm/loadPlugins.js +38 -0
  57. package/esm/localFiles.d.ts +74 -0
  58. package/esm/localFiles.js +156 -0
  59. package/esm/observeSession.d.ts +56 -0
  60. package/esm/observeSession.js +91 -0
  61. package/esm/pluginInput.d.ts +32 -0
  62. package/esm/pluginInput.js +19 -0
  63. package/esm/pruneUnbuildableNodes.d.ts +109 -0
  64. package/esm/pruneUnbuildableNodes.js +568 -0
  65. package/esm/resolveAssemblies.d.ts +44 -0
  66. package/esm/resolveAssemblies.js +55 -0
  67. package/esm/rpcWorker.d.ts +26 -0
  68. package/esm/rpcWorker.js +96 -15
  69. package/esm/scheduleDetachedDestroy.d.ts +18 -0
  70. package/esm/scheduleDetachedDestroy.js +24 -0
  71. package/esm/sessionMigrations/index.d.ts +53 -0
  72. package/esm/sessionMigrations/index.js +432 -0
  73. package/esm/sessionUrl.d.ts +41 -0
  74. package/esm/sessionUrl.js +53 -0
  75. package/esm/sessionUtils.d.ts +69 -0
  76. package/esm/sessionUtils.js +613 -0
  77. package/esm/siblingCast.d.ts +14 -0
  78. package/esm/siblingCast.js +16 -0
  79. package/esm/snapshotUtils.d.ts +8 -0
  80. package/esm/snapshotUtils.js +12 -0
  81. package/esm/ui/AboutDialog.d.ts +3 -6
  82. package/esm/ui/AboutDialog.js +4 -4
  83. package/esm/ui/AboutDialogContents.d.ts +2 -6
  84. package/esm/ui/AboutDialogContents.js +34 -13
  85. package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
  86. package/esm/ui/AssemblyInfoPanel.js +69 -0
  87. package/esm/ui/DisplayDefaultsSection.d.ts +36 -0
  88. package/esm/ui/DisplayDefaultsSection.js +65 -0
  89. package/esm/ui/Drawer.d.ts +21 -0
  90. package/esm/ui/Drawer.js +60 -0
  91. package/esm/ui/DrawerControls.d.ts +5 -0
  92. package/esm/ui/DrawerControls.js +28 -0
  93. package/esm/ui/DrawerHeader.d.ts +6 -0
  94. package/esm/ui/DrawerHeader.js +36 -0
  95. package/esm/ui/DrawerWidget.d.ts +5 -0
  96. package/esm/ui/DrawerWidget.js +12 -0
  97. package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
  98. package/esm/ui/DrawerWidgetSelector.js +40 -0
  99. package/esm/ui/FileInfoPanel.d.ts +3 -6
  100. package/esm/ui/FileInfoPanel.js +35 -26
  101. package/esm/ui/HeaderButtons.d.ts +7 -1
  102. package/esm/ui/HeaderButtons.js +23 -18
  103. package/esm/ui/ModalWidget.d.ts +12 -0
  104. package/esm/ui/ModalWidget.js +33 -0
  105. package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
  106. package/esm/ui/ModalWidgetAppBar.js +9 -0
  107. package/esm/ui/PreferencesDialog.d.ts +56 -0
  108. package/esm/ui/PreferencesDialog.js +145 -0
  109. package/esm/ui/PreferencesResetDialog.d.ts +8 -0
  110. package/esm/ui/PreferencesResetDialog.js +19 -0
  111. package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
  112. package/esm/ui/RefNameAliasesDialog.js +65 -0
  113. package/esm/ui/RefNameInfoDialog.d.ts +3 -6
  114. package/esm/ui/RefNameInfoDialog.js +57 -63
  115. package/esm/ui/WidgetBody.d.ts +18 -0
  116. package/esm/ui/WidgetBody.js +22 -0
  117. package/esm/ui/WidgetHeading.d.ts +7 -0
  118. package/esm/ui/WidgetHeading.js +7 -0
  119. package/esm/ui/drawerLayout.d.ts +18 -0
  120. package/esm/ui/drawerLayout.js +22 -0
  121. package/esm/ui/index.d.ts +13 -1
  122. package/esm/ui/index.js +12 -1
  123. package/esm/ui/util.d.ts +37 -4
  124. package/esm/ui/util.js +35 -38
  125. package/esm/useEngineLifecycle.d.ts +13 -0
  126. package/esm/useEngineLifecycle.js +22 -0
  127. package/package.json +11 -15
  128. package/esm/RootModel/FormatAbout.d.ts +0 -12
  129. package/esm/RootModel/FormatAbout.js +0 -15
  130. package/esm/RootModel/FormatDetails.d.ts +0 -24
  131. package/esm/RootModel/FormatDetails.js +0 -27
  132. package/esm/Session/DialogQueue.d.ts +0 -17
  133. package/esm/Session/DialogQueue.js +0 -31
@@ -6,7 +6,33 @@ declare global {
6
6
  rpcServer?: RpcServer;
7
7
  }
8
8
  }
9
+ type RpcFunc = (args: unknown) => Promise<unknown>;
10
+ /**
11
+ * Wire the method's `statusCallback` to the status channel the driver opened
12
+ * for this call — and to nothing at all when it opened none.
13
+ *
14
+ * The `channel` is the driver's statement of whether anyone is listening
15
+ * (`WebWorkerHandle.call`), so a method run without one has to see the
16
+ * `undefined` its caller passed. Injecting a callback regardless made every
17
+ * "no statusCallback" branch in the worker unreachable: the byte reporter an
18
+ * adapter hands its reader, the emit `createProgressReporter` gates, and the
19
+ * postMessage each of those cost to reach a main-thread listener that dropped
20
+ * it.
21
+ *
22
+ * `channel` is transport bookkeeping and comes back OFF here, the mirror of
23
+ * `BaseRpcDriver.call` stripping `statusCallback` on the way out. It used to
24
+ * ride through into every worker method's arguments, where nothing read it and
25
+ * `RpcExecuteArgs<M>` did not declare it — a key the main-thread driver never
26
+ * adds, so the two drivers handed `execute` different bags. `rpcDriverParity`
27
+ * is what says so.
28
+ *
29
+ * Exported for its test and not from the package barrel: `initializeWorker` is
30
+ * the only thing a product's worker entry calls, and the branch here is a claim
31
+ * about the driver on the other side of postMessage that nothing else asserts.
32
+ */
33
+ export declare function wrapForRpc(func: RpcFunc): (args: unknown) => Promise<unknown>;
9
34
  export declare function initializeWorker(corePlugins: PluginConstructor[], opts: {
10
35
  fetchESM?: (url: string) => Promise<LoadedPlugin>;
11
36
  fetchCJS?: (url: string) => Promise<LoadedPlugin>;
12
37
  }): Promise<void>;
38
+ export {};
package/esm/rpcWorker.js CHANGED
@@ -1,10 +1,15 @@
1
1
  import PluginLoader from '@jbrowse/core/PluginLoader';
2
2
  import PluginManager from '@jbrowse/core/PluginManager';
3
+ import { setNumberGrouping, throttleStatusEmits } from '@jbrowse/core/util';
3
4
  import { RpcServer, serializeError } from '@jbrowse/core/util/librpc';
5
+ import { setStackTraceLimit } from '@jbrowse/core/util/setStackTraceLimit';
6
+ import { enableStaticRendering } from 'mobx-react';
7
+ // waits for a message from the main thread containing our configuration, which
8
+ // must be sent on boot
4
9
  function receiveConfiguration() {
5
10
  const configurationP = new Promise(resolve => {
6
11
  function listener(e) {
7
- if (e.data.message === 'config') {
12
+ if (e.data.message === 'config' && e.data.config) {
8
13
  resolve(e.data.config);
9
14
  removeEventListener('message', listener);
10
15
  }
@@ -16,27 +21,79 @@ function receiveConfiguration() {
16
21
  }
17
22
  async function getPluginManager(corePlugins, opts) {
18
23
  const config = await receiveConfiguration();
24
+ // this realm formats its own strings — a jexl `mouseover` slot renders a
25
+ // tooltip against the full feature here rather than shipping it back — so it
26
+ // needs the main thread's display preference before any RPC method runs
27
+ setNumberGrouping(config.numberGrouping);
19
28
  const pluginLoader = new PluginLoader(config.plugins, opts).installGlobalReExports(self);
29
+ // Keep each runtime plugin's `definition` on its load record (mirroring the
30
+ // main thread's createPluginManager) so PluginManager populates
31
+ // runtimePluginDefinitions. A plugin that resolves a sibling asset from its
32
+ // own bundle url in the worker — e.g. GraphGenomeView's Bandage layout
33
+ // engine — needs its definition here, not just its instance.
34
+ const runtimePlugins = await pluginLoader.load(config.windowHref);
20
35
  return new PluginManager([
21
- ...corePlugins.map(p => ({ plugin: p })),
22
- ...(await pluginLoader.load(config.windowHref)),
23
- ].map(P => new P.plugin()))
36
+ ...corePlugins.map(P => ({ plugin: new P() })),
37
+ ...runtimePlugins.map(({ plugin: P, definition }) => ({
38
+ plugin: new P(),
39
+ definition,
40
+ })),
41
+ ])
24
42
  .createPluggableElements()
25
43
  .configure();
26
44
  }
27
- function wrapForRpc(func) {
45
+ /**
46
+ * Wire the method's `statusCallback` to the status channel the driver opened
47
+ * for this call — and to nothing at all when it opened none.
48
+ *
49
+ * The `channel` is the driver's statement of whether anyone is listening
50
+ * (`WebWorkerHandle.call`), so a method run without one has to see the
51
+ * `undefined` its caller passed. Injecting a callback regardless made every
52
+ * "no statusCallback" branch in the worker unreachable: the byte reporter an
53
+ * adapter hands its reader, the emit `createProgressReporter` gates, and the
54
+ * postMessage each of those cost to reach a main-thread listener that dropped
55
+ * it.
56
+ *
57
+ * `channel` is transport bookkeeping and comes back OFF here, the mirror of
58
+ * `BaseRpcDriver.call` stripping `statusCallback` on the way out. It used to
59
+ * ride through into every worker method's arguments, where nothing read it and
60
+ * `RpcExecuteArgs<M>` did not declare it — a key the main-thread driver never
61
+ * adds, so the two drivers handed `execute` different bags. `rpcDriverParity`
62
+ * is what says so.
63
+ *
64
+ * Exported for its test and not from the package barrel: `initializeWorker` is
65
+ * the only thing a product's worker entry calls, and the branch here is a claim
66
+ * about the driver on the other side of postMessage that nothing else asserts.
67
+ */
68
+ export function wrapForRpc(func) {
28
69
  return (args) => {
29
- const wrappedArgs = args;
30
- const { channel, rpcDriverClassName } = wrappedArgs;
31
- return func({
32
- ...wrappedArgs,
33
- statusCallback: (message) => {
34
- self.rpcServer?.emit(channel, message);
35
- },
36
- }, rpcDriverClassName);
70
+ const { channel, ...rest } = args;
71
+ return func(channel === undefined
72
+ ? rest
73
+ : {
74
+ ...rest,
75
+ // thinned here rather than only where it lands: every emit costs a
76
+ // postMessage, a task and a clone on the main thread, which then
77
+ // throttles the same stream again to decide what to draw
78
+ statusCallback: throttleStatusEmits((message) => {
79
+ self.rpcServer?.emit(channel, message);
80
+ }),
81
+ });
37
82
  };
38
83
  }
39
84
  export async function initializeWorker(corePlugins, opts) {
85
+ // a worker error's stack is captured here, not on the main thread, so the
86
+ // frame limit has to be raised in this scope to deepen it
87
+ setStackTraceLimit();
88
+ // Workers fetch data; rendering happens on the main thread, and no RPC method
89
+ // renders React. Kept as a guard: if plugin code ever pulls an observer into
90
+ // this realm, static rendering keeps it from setting up reactions that would
91
+ // leak here. Here rather than in each product's worker entry, where four
92
+ // copies meant a fifth product would silently ship without it — the plugins
93
+ // that could trip it are not loaded until getPluginManager below, so this is
94
+ // still ahead of anything that observes.
95
+ enableStaticRendering(true);
96
+ // Add global error handler to catch uncaught errors in the worker
40
97
  self.addEventListener('error', event => {
41
98
  console.error('[Worker uncaught error]', event.error || event.message);
42
99
  });
@@ -47,11 +104,35 @@ export async function initializeWorker(corePlugins, opts) {
47
104
  const pluginManager = await getPluginManager(corePlugins, opts);
48
105
  const rpcConfig = Object.fromEntries(pluginManager
49
106
  .getRpcElements()
50
- .map(e => [e.name, wrapForRpc(e.execute.bind(e))]));
107
+ // `invoke`, not `execute`: it deserializes the arguments before handing
108
+ // them on, which is what makes the blob map current and the declared
109
+ // arg types true. MainThreadRpcDriver binds the same one.
110
+ .map(e => [e.name, wrapForRpc(e.invoke.bind(e))]));
51
111
  self.rpcServer = new RpcServer(rpcConfig);
52
112
  postMessage({ message: 'ready' });
53
113
  }
54
114
  catch (e) {
55
- postMessage({ message: 'error', error: serializeError(e) });
115
+ // The boot handshake's last frame, and it must never fail to send — for the
116
+ // reason RpcServer.throw must not, one level worse. `serializeError` copies
117
+ // an error's own-enumerable properties and skips only the ones that are
118
+ // themselves functions, so an error carrying an object with a method on it
119
+ // (a `cause` that is a Response, an adapter instance) makes this postMessage
120
+ // throw DataCloneError. This runs floating, so that throw becomes an
121
+ // unhandled rejection the listener above only logs: no 'ready', no 'error',
122
+ // and no ErrorEvent either, since a rejected promise is not a script error.
123
+ // The boot promise in WebWorkerRpcDriver then never settles, and because
124
+ // `invalidate` hangs off its rejection, the pool hands every later caller
125
+ // the same pending promise — every RPC on that slot awaits forever, showing
126
+ // a spinner and no error, until the page is reloaded. One worker's failure
127
+ // to report a failure takes the whole session's worth of calls with it.
128
+ //
129
+ // deserializeError accepts a bare string, which is what makes the fallback
130
+ // sound: a string always clones.
131
+ try {
132
+ postMessage({ message: 'error', error: serializeError(e) });
133
+ }
134
+ catch {
135
+ postMessage({ message: 'error', error: `${e}` });
136
+ }
56
137
  }
57
138
  }
@@ -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
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Migrates old session and config snapshots to be compatible with the current
3
+ * display type registrations. Handles display types that were removed or
4
+ * renamed in the v4 rendering rearchitecture.
5
+ *
6
+ * Remapped display types (see `displayTypeMap` for the band settings each one
7
+ * carries over):
8
+ * LinearPileupDisplay → LinearAlignmentsDisplay
9
+ * LinearSNPCoverageDisplay → LinearAlignmentsDisplay
10
+ * LinearReadArcsDisplay → LinearAlignmentsDisplay
11
+ * LinearReadCloudDisplay → LinearAlignmentsDisplay
12
+ * LinearFeatureDisplay → LinearBasicDisplay
13
+ *
14
+ * Also lifts the per-instance alignments settings a v4.3.0 session persisted
15
+ * (`colorBySetting`, `filterBySetting`, `jexlFilters`, `trackMaxHeight`,
16
+ * `hideMismatchesSetting`) into the config where they now live as slots — off
17
+ * the nested `LinearAlignmentsDisplay` container's `PileupDisplay` /
18
+ * `SNPCoverageDisplay` sub-nodes and off a flat old display alike, see
19
+ * `extractAlignmentsInstanceSettings` — and routes the legacy per-instance
20
+ * `heightPreConfig` display-height prop onto the `height` config slot — see
21
+ * `extractInstanceHeight`.
22
+ */
23
+ /**
24
+ * Every display-instance key a session snapshot may still carry and have
25
+ * honored, keyed by the display type it applies to — `*` for the ones any
26
+ * display gets. A snapshot carrying one of these loads correctly, so `jbrowse
27
+ * validate` reports it as stale rather than dead; that command reads this map
28
+ * (through the generated config manifest) rather than keeping a copy that could
29
+ * drift.
30
+ *
31
+ * Not every entry is lifted *here*. The two multi-sample variant displays lift
32
+ * their own `jexlFilters` in the model's `preProcessSnapshot`, because the value
33
+ * stays on the instance (under `jexlFiltersSetting`) rather than moving to a
34
+ * config slot, which is the only shape this module handles. What decides
35
+ * membership is whether the key still works, not which file makes it work.
36
+ *
37
+ * Keyed rather than flat because the same name means different things on
38
+ * different displays: `jexlFilters` is lifted for the alignments display and,
39
+ * differently, for the two variant ones, while on a LinearBasicDisplay — whose
40
+ * own prop has always been `jexlFiltersSetting` — it is simply dead, and a flat
41
+ * list would call that one supported.
42
+ */
43
+ export declare const MIGRATED_DISPLAY_INSTANCE_KEYS: Record<string, string[]>;
44
+ /**
45
+ * Walks a session snapshot and remaps old display types to their new names.
46
+ * Handles displays nested inside views, tracks, and sessionTracks.
47
+ */
48
+ export declare function migrateSessionSnapshot(snapshot: Record<string, unknown>): Record<string, unknown>;
49
+ /**
50
+ * Walks a config snapshot and remaps old display types in track definitions.
51
+ * Config tracks have displays in their configuration (not in views).
52
+ */
53
+ export declare function migrateConfigSnapshot(snapshot: Record<string, unknown>): Record<string, unknown>;