@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,6 +1,7 @@
1
1
  import PhasedScheduler from './PhasedScheduler.ts';
2
2
  import './util/tracks.ts';
3
3
  import type Plugin from './Plugin.ts';
4
+ import type { PluginConstructor } from './Plugin.ts';
4
5
  import type AdapterType from './pluggableElementTypes/AdapterType.ts';
5
6
  import type AddTrackWorkflowType from './pluggableElementTypes/AddTrackWorkflowType.ts';
6
7
  import type ConnectionType from './pluggableElementTypes/ConnectionType.ts';
@@ -20,7 +21,7 @@ import './ui/buildExtraTrackMenuItems.ts';
20
21
  import './ui/multiTrackMenuItems.ts';
21
22
  import './util/addTrackComponent.ts';
22
23
  import type { AbstractRootModel, AbstractSessionModel, SimpleFeatureSerialized } from './util/index.ts';
23
- import type { IAnyModelType, IStateTreeNode, IAnyType } from '@jbrowse/mobx-state-tree';
24
+ import type { IAnyModelType, IStateTreeNode, IAnyType, SnapshotIn } from '@jbrowse/mobx-state-tree';
24
25
  import type { ComponentType, ReactNode } from 'react';
25
26
  /**
26
27
  * Every pluggable element group, in the order `elementCreationSchedule` builds
@@ -137,7 +138,18 @@ export interface ReplaceWidgetProps {
137
138
  *
138
139
  * That is what makes `extendViewType(pm, 'LinearGenomeView', …)` hand back a
139
140
  * typed state model, and a misspelled or renamed name a compile error rather
140
- * than an extension that quietly never applies. Declaring them here rather than
141
+ * than an extension that quietly never applies. `getViewType` reads it too, so
142
+ * a caller composing another view's model into its own gets the real type
143
+ * instead of `IAnyModelType`.
144
+ *
145
+ * AN AUGMENTATION IS HAND-WRITTEN, so each registration annotates the model it
146
+ * passes to `ViewType` against its own entry here (`const stateModel:
147
+ * ViewTypeRegistry['CircularView'] = …`). That one line is what makes the
148
+ * registry earn what `getViewType` promises: without it a drifted entry is
149
+ * worse than the `IAnyModelType` it replaced, since a caller would get a
150
+ * confidently wrong type rather than an unchecked one. It cannot live on
151
+ * `ViewType`'s constructor — TypeScript forbids type parameters there, and a
152
+ * class parameter widens `name` to `string`, so the conditional never fires. Declaring them here rather than
141
153
  * beside the helpers is load-bearing: an interface can only be augmented
142
154
  * through the module that declares it, and a barrel re-exporting the type would
143
155
  * give every augmentation its own unrelated copy.
@@ -148,6 +160,21 @@ export interface DisplayTypeRegistry {
148
160
  }
149
161
  export type ViewTypeName = keyof ViewTypeRegistry;
150
162
  export type DisplayTypeName = keyof DisplayTypeRegistry;
163
+ /**
164
+ * What `addView(name, snapshot)` takes for a view type name. A registered name
165
+ * gets the view's authored shape — the snapshot type `withLaunchInput` widened
166
+ * to accept the launch keys beside the declared properties — so a misspelled
167
+ * key is a compile error at the literal site. `type` is optional because
168
+ * `addView` writes it. An unregistered name stays `Record<string, unknown>`,
169
+ * which is what an out-of-tree view's `addView` has always been.
170
+ *
171
+ * Read off `ViewTypeRegistry` rather than registered a second time, so the
172
+ * snapshot type cannot drift from the model: a view that augments the registry
173
+ * has typed `addView`, `replaceView` and `addOrReplaceView` for free.
174
+ */
175
+ export type ViewSnapshotInput<N extends string> = N extends ViewTypeName ? Omit<SnapshotIn<ViewTypeRegistry[N]>, 'type'> & {
176
+ type?: N;
177
+ } : Record<string, unknown>;
151
178
  export interface ExtensionPointRegistry {
152
179
  'Core-extendSession': {
153
180
  args: IAnyModelType;
@@ -264,10 +291,20 @@ export interface PluginLoadRecord {
264
291
  metadata?: PluginMetadata;
265
292
  plugin: Plugin;
266
293
  }
294
+ /**
295
+ * Instantiate an application's bundled plugin list as `isCore` load records —
296
+ * what tells the plugin store these came with the bundle rather than being
297
+ * installed by the user, so `InstalledPluginsList` filters them out and the
298
+ * About widget lists them. Every product and every test harness builds its
299
+ * manager from one of these lists, and a harness that passed bare instances
300
+ * silently disagreed with the app it was testing.
301
+ */
302
+ export declare function corePluginRecords(plugins: PluginConstructor[]): PluginLoadRecord[];
267
303
  export interface RuntimePluginLoadRecord extends PluginLoadRecord {
268
304
  definition: PluginDefinition;
269
305
  }
270
306
  export default class PluginManager {
307
+ #private;
271
308
  plugins: Plugin[];
272
309
  jexl: import("@jbrowse/jexl").Jexl;
273
310
  pluginMetadata: Record<string, PluginMetadata>;
@@ -289,25 +326,27 @@ export default class PluginManager {
289
326
  extensionPoints: Map<string, ExtensionPointCallback[]>;
290
327
  warnedClobber: Set<string>;
291
328
  /**
292
- * Lazy-hydration cache for `TrackConfigurationReference`/
293
- * `DisplayConfigurationReference` (configuration/configurationSchema.ts).
329
+ * The lazily-hydrated MST config node for one frozen track config, built by
330
+ * `build` on first ask and returned by identity after that.
331
+ * `configuration/configurationSchema.ts` is the only caller.
332
+ *
294
333
  * `jbrowse.tracks` is `types.frozen` for large-tracklist performance, so a
295
- * track config is a plain JS object until first referenced; hydrating it
296
- * into an MST node is deferred to that read. MST's custom-reference
297
- * `getValue` has no memoization of its own — it reruns on every property
298
- * access — so without this cache, every read of `track.configuration` would
299
- * fabricate a fresh, non-identical MST node. Keyed by schemaType (each track
300
- * type's config schema is rebuilt fresh per PluginManager instance, see
301
- * addTrackType) then by the frozen object itself, so a cache hit can only
302
- * ever come from this same PluginManager instance and this same track type.
303
- * See ADR-031.
334
+ * track config is a plain JS object until first referenced; hydrating it into
335
+ * an MST node is deferred to that read. MST's custom-reference `getValue` has
336
+ * no memoization of its own — it reruns on every property access — so without
337
+ * this, every read of `track.configuration` would fabricate a fresh,
338
+ * non-identical node. Keyed by schemaType (each track type's config schema is
339
+ * rebuilt fresh per PluginManager instance, see addTrackType) then by the
340
+ * frozen object, so a hit can only ever come from this same instance and this
341
+ * same track type. See ADR-031.
304
342
  *
305
- * This node is never mutated: admin edits replace the frozen entry (new
343
+ * The node is never mutated: admin edits replace the frozen entry (new
306
344
  * identity drops the WeakMap entry), and a non-admin's edits go to a private
307
345
  * session working copy, not here (ADR-032). Both levels are `WeakMap`s so
308
- * entries collect normally — no manual invalidation needed.
346
+ * entries collect normally — no manual invalidation needed, which is why this
347
+ * is a memo and not a cache with a lifecycle to expose.
309
348
  */
310
- trackConfigHydrationCache: WeakMap<object, WeakMap<object, unknown>>;
349
+ hydratedTrackConfig(schemaType: IAnyType, frozen: object, build: () => unknown): unknown;
311
350
  constructor(initialPlugins?: (Plugin | PluginLoadRecord | RuntimePluginLoadRecord)[]);
312
351
  pluginConfigurationNamespacedSchemas(): Record<string, unknown>;
313
352
  pluginConfigurationUnnamespacedSchemas(): Record<string, unknown>;
@@ -330,10 +369,22 @@ export default class PluginManager {
330
369
  getRpcElements(): RpcMethodType[];
331
370
  getDisplayElements(): DisplayType[];
332
371
  getAdapterElements(): AdapterType[];
333
- /** get a MST type for the union of all specified pluggable MST types */
372
+ /**
373
+ * get a MST type for the union of all specified pluggable MST types.
374
+ * Membership is late-bound, so a lazily registered state model joins when
375
+ * its loader resolves; a snapshot naming one cannot be cast until then.
376
+ */
334
377
  pluggableMstType(groupName: PluggableElementTypeGroup, fieldName: string, fallback?: IAnyType): IAnyType;
335
378
  /** get a MST type for the union of all specified pluggable config schemas */
336
379
  pluggableConfigSchemaType(typeGroup: PluggableElementTypeGroup, fieldName?: string): IAnyType;
380
+ /**
381
+ * An adapter union dispatches on `type`, so a config still spelling an
382
+ * adapter's old name has to be rewritten to the name it registers under
383
+ * before the union sees it. Here rather than in each schema holding an
384
+ * adapter slot, so a track, an assembly's sequence and a connection all take
385
+ * it.
386
+ */
387
+ private canonicalizeAdapterSnapshot;
337
388
  jbrequireCache: Map<any, any>;
338
389
  get lib(): Record<string, unknown>;
339
390
  load: <FTYPE extends AnyFunction>(lib: FTYPE) => ReturnType<FTYPE>;
@@ -347,6 +398,12 @@ export default class PluginManager {
347
398
  default: AnyFunction;
348
399
  }) => any;
349
400
  getAdapterType(typeName: string): AdapterType;
401
+ /**
402
+ * The name an adapter `type` registers under: itself, or the adapter that
403
+ * declares it in `aliases`. A name nothing answers to comes back as written,
404
+ * so the registered-names error still names what the config said.
405
+ */
406
+ canonicalAdapterTypeName(typeName: string): string;
350
407
  /**
351
408
  * Whether an adapter type is registered, for a caller walking a config where
352
409
  * `type` names a track, a display and an adapter alike — `getAdapterType`
@@ -357,7 +414,32 @@ export default class PluginManager {
357
414
  getTextSearchAdapterType(typeName: string): TextSearchAdapterType;
358
415
  getTrackType(typeName: string): TrackType;
359
416
  getDisplayType(typeName: string): DisplayType;
417
+ getViewType<N extends ViewTypeName>(typeName: N): Omit<ViewType, 'stateModel'> & {
418
+ stateModel: ViewTypeRegistry[N];
419
+ };
360
420
  getViewType(typeName: string): ViewType;
421
+ /**
422
+ * The registered view and display types a snapshot names, from every `type`
423
+ * string anywhere in it — `heldForMissingPlugins` included. Over-collection
424
+ * is harmless: a name that resolves to neither is dropped here.
425
+ */
426
+ private sessionElementTypes;
427
+ /**
428
+ * Every async code path that hands a snapshot to a synchronous session
429
+ * instantiation (`setSession`, `cast`) must await this first.
430
+ */
431
+ preloadSessionTypes(sessionSnapshot: unknown): Promise<void>;
432
+ unloadedSessionTypes(sessionSnapshot: unknown): string[];
433
+ /**
434
+ * Without this, a snapshot naming an unloaded lazy type fails inside MST as
435
+ * a union mismatch that reads like a corrupt snapshot.
436
+ */
437
+ assertSessionTypesLoaded(sessionSnapshot: unknown): void;
438
+ /**
439
+ * Aliases resolve here because a legacy session names the alias, and the
440
+ * model that remaps it cannot be consulted before it is loaded.
441
+ */
442
+ resolveDisplayTypeRecord(name: string): DisplayType | undefined;
361
443
  getAddTrackWorkflow(typeName: string): AddTrackWorkflowType;
362
444
  getWidgetType(typeName: string): WidgetType;
363
445
  getConnectionType(typeName: string): ConnectionType;
@@ -3,6 +3,7 @@ import CorePlugin from './CorePlugin.js';
3
3
  import PhasedScheduler from './PhasedScheduler.js';
4
4
  import { getReExportRegistry } from './ReExports/registry.js';
5
5
  import { ConfigurationSchema, isBareConfigurationSchemaType, } from './configuration/index.js';
6
+ import LazyStateModelElement from './pluggableElementTypes/LazyStateModelElement.js';
6
7
  import createJexlInstance from './util/jexl.js';
7
8
  import { expandLooseTrackConfig } from './util/tracks.js';
8
9
  /**
@@ -24,6 +25,9 @@ export const pluggableElementTypeGroups = [
24
25
  'internet account',
25
26
  'add track workflow',
26
27
  ];
28
+ function isUnloadedLazyElement(element) {
29
+ return element instanceof LazyStateModelElement && !element.isStateModelLoaded;
30
+ }
27
31
  /** internal class that holds the info for a certain element type */
28
32
  class TypeRecord {
29
33
  registeredTypes = {};
@@ -90,6 +94,17 @@ function nextAccumulator(name, accumulator, result) {
90
94
  function logCallbackError(name, error) {
91
95
  console.error(`error in a ${name} extension point callback`, error);
92
96
  }
97
+ /**
98
+ * Instantiate an application's bundled plugin list as `isCore` load records —
99
+ * what tells the plugin store these came with the bundle rather than being
100
+ * installed by the user, so `InstalledPluginsList` filters them out and the
101
+ * About widget lists them. Every product and every test harness builds its
102
+ * manager from one of these lists, and a harness that passed bare instances
103
+ * silently disagreed with the app it was testing.
104
+ */
105
+ export function corePluginRecords(plugins) {
106
+ return plugins.map(P => ({ plugin: new P(), metadata: { isCore: true } }));
107
+ }
93
108
  export default class PluginManager {
94
109
  plugins = [];
95
110
  jexl = createJexlInstance();
@@ -113,26 +128,41 @@ export default class PluginManager {
113
128
  // singular component points are re-evaluated on every render, so the
114
129
  // clobber warning is emitted once per point per app
115
130
  warnedClobber = new Set();
131
+ #trackConfigHydrationCache = new WeakMap();
116
132
  /**
117
- * Lazy-hydration cache for `TrackConfigurationReference`/
118
- * `DisplayConfigurationReference` (configuration/configurationSchema.ts).
133
+ * The lazily-hydrated MST config node for one frozen track config, built by
134
+ * `build` on first ask and returned by identity after that.
135
+ * `configuration/configurationSchema.ts` is the only caller.
136
+ *
119
137
  * `jbrowse.tracks` is `types.frozen` for large-tracklist performance, so a
120
- * track config is a plain JS object until first referenced; hydrating it
121
- * into an MST node is deferred to that read. MST's custom-reference
122
- * `getValue` has no memoization of its own — it reruns on every property
123
- * access — so without this cache, every read of `track.configuration` would
124
- * fabricate a fresh, non-identical MST node. Keyed by schemaType (each track
125
- * type's config schema is rebuilt fresh per PluginManager instance, see
126
- * addTrackType) then by the frozen object itself, so a cache hit can only
127
- * ever come from this same PluginManager instance and this same track type.
128
- * See ADR-031.
138
+ * track config is a plain JS object until first referenced; hydrating it into
139
+ * an MST node is deferred to that read. MST's custom-reference `getValue` has
140
+ * no memoization of its own — it reruns on every property access — so without
141
+ * this, every read of `track.configuration` would fabricate a fresh,
142
+ * non-identical node. Keyed by schemaType (each track type's config schema is
143
+ * rebuilt fresh per PluginManager instance, see addTrackType) then by the
144
+ * frozen object, so a hit can only ever come from this same instance and this
145
+ * same track type. See ADR-031.
129
146
  *
130
- * This node is never mutated: admin edits replace the frozen entry (new
147
+ * The node is never mutated: admin edits replace the frozen entry (new
131
148
  * identity drops the WeakMap entry), and a non-admin's edits go to a private
132
149
  * session working copy, not here (ADR-032). Both levels are `WeakMap`s so
133
- * entries collect normally — no manual invalidation needed.
150
+ * entries collect normally — no manual invalidation needed, which is why this
151
+ * is a memo and not a cache with a lifecycle to expose.
134
152
  */
135
- trackConfigHydrationCache = new WeakMap();
153
+ hydratedTrackConfig(schemaType, frozen, build) {
154
+ let byFrozen = this.#trackConfigHydrationCache.get(schemaType);
155
+ if (!byFrozen) {
156
+ byFrozen = new WeakMap();
157
+ this.#trackConfigHydrationCache.set(schemaType, byFrozen);
158
+ }
159
+ let node = byFrozen.get(frozen);
160
+ if (!node) {
161
+ node = build();
162
+ byFrozen.set(frozen, node);
163
+ }
164
+ return node;
165
+ }
136
166
  constructor(initialPlugins = []) {
137
167
  // add the core plugin
138
168
  this.addPlugin({
@@ -292,9 +322,28 @@ export default class PluginManager {
292
322
  console.warn(`${groupName} ${newElement.name} already registered, cannot register it again`);
293
323
  }
294
324
  else {
295
- typeRecord.add(newElement.name, this.evaluateExtensionPoint(
325
+ const extend = () => this.evaluateExtensionPoint(
296
326
  /** #extensionPoint Core-extendPluggableElement | sync | Mutate any pluggable element after it is created */
297
- 'Core-extendPluggableElement', newElement, { group: groupName }));
327
+ 'Core-extendPluggableElement', newElement, { group: groupName });
328
+ // A lazily registered state model is not there for a callback to
329
+ // extend yet, so the point fires when the loader resolves instead.
330
+ // agent-docs/reference/EAGER_BUNDLE.md
331
+ if (isUnloadedLazyElement(newElement)) {
332
+ newElement.onStateModelLoaded = () => {
333
+ const extended = extend();
334
+ if (extended !== newElement) {
335
+ typeRecord.add(newElement.name, extended);
336
+ if (extended instanceof LazyStateModelElement &&
337
+ extended.isStateModelLoaded) {
338
+ newElement.stateModel = extended.stateModel;
339
+ }
340
+ }
341
+ };
342
+ typeRecord.add(newElement.name, newElement);
343
+ }
344
+ else {
345
+ typeRecord.add(newElement.name, extend());
346
+ }
298
347
  }
299
348
  });
300
349
  return this;
@@ -326,16 +375,38 @@ export default class PluginManager {
326
375
  getAdapterElements() {
327
376
  return this.getElementTypesInGroup('adapter');
328
377
  }
329
- /** get a MST type for the union of all specified pluggable MST types */
378
+ /**
379
+ * get a MST type for the union of all specified pluggable MST types.
380
+ * Membership is late-bound, so a lazily registered state model joins when
381
+ * its loader resolves; a snapshot naming one cannot be cast until then.
382
+ */
330
383
  pluggableMstType(groupName, fieldName, fallback = types.maybe(types.null)) {
331
- const pluggableTypes = this.getElementTypeRecord(groupName)
332
- .all()
333
- .map(t => t[fieldName])
334
- .filter(t => isType(t) && isModelType(t));
335
- if (pluggableTypes.length === 0) {
384
+ const record = this.getElementTypeRecord(groupName);
385
+ const fieldOf = (t) => isUnloadedLazyElement(t)
386
+ ? undefined
387
+ : t[fieldName];
388
+ const anyMember = record.all().some(t => {
389
+ const field = fieldOf(t);
390
+ return ((isType(field) && isModelType(field)) ||
391
+ typeof t[`${fieldName}Loader`] === 'function');
392
+ });
393
+ if (!anyMember) {
336
394
  return fallback;
337
395
  }
338
- return types.union(...pluggableTypes);
396
+ const modelsOf = () => record
397
+ .all()
398
+ .map(t => fieldOf(t))
399
+ .filter(t => isType(t) && isModelType(t));
400
+ let members = modelsOf();
401
+ return types.union({
402
+ name: `pluggable(${groupName} ${fieldName})`,
403
+ members: () => {
404
+ if (members.length !== modelsOf().length) {
405
+ members = modelsOf();
406
+ }
407
+ return members;
408
+ },
409
+ });
339
410
  }
340
411
  /** get a MST type for the union of all specified pluggable config schemas */
341
412
  pluggableConfigSchemaType(typeGroup, fieldName = 'configSchema') {
@@ -360,7 +431,28 @@ export default class PluginManager {
360
431
  ? types.snapshotProcessor(union, {
361
432
  preProcessor: (snap) => expandLooseTrackConfig(snap, this),
362
433
  })
363
- : union;
434
+ : typeGroup === 'adapter'
435
+ ? types.snapshotProcessor(union, {
436
+ preProcessor: (snap) => this.canonicalizeAdapterSnapshot(snap),
437
+ })
438
+ : union;
439
+ }
440
+ /**
441
+ * An adapter union dispatches on `type`, so a config still spelling an
442
+ * adapter's old name has to be rewritten to the name it registers under
443
+ * before the union sees it. Here rather than in each schema holding an
444
+ * adapter slot, so a track, an assembly's sequence and a connection all take
445
+ * it.
446
+ */
447
+ canonicalizeAdapterSnapshot(snap) {
448
+ if (typeof snap === 'object' &&
449
+ snap !== null &&
450
+ 'type' in snap &&
451
+ typeof snap.type === 'string') {
452
+ const canonical = this.canonicalAdapterTypeName(snap.type);
453
+ return canonical === snap.type ? snap : { ...snap, type: canonical };
454
+ }
455
+ return snap;
364
456
  }
365
457
  jbrequireCache = new Map();
366
458
  // populated by PluginLoader once a runtime plugin is actually being loaded,
@@ -399,7 +491,18 @@ export default class PluginManager {
399
491
  throw new TypeError('lib passed to jbrequire must be either a string or a function');
400
492
  };
401
493
  getAdapterType(typeName) {
402
- return this.adapterTypes.get(typeName);
494
+ return this.adapterTypes.get(this.canonicalAdapterTypeName(typeName));
495
+ }
496
+ /**
497
+ * The name an adapter `type` registers under: itself, or the adapter that
498
+ * declares it in `aliases`. A name nothing answers to comes back as written,
499
+ * so the registered-names error still names what the config said.
500
+ */
501
+ canonicalAdapterTypeName(typeName) {
502
+ return this.adapterTypes.has(typeName)
503
+ ? typeName
504
+ : (this.adapterTypes.all().find(t => t.aliases?.includes(typeName))
505
+ ?.name ?? typeName);
403
506
  }
404
507
  /**
405
508
  * Whether an adapter type is registered, for a caller walking a config where
@@ -408,7 +511,7 @@ export default class PluginManager {
408
511
  * claims to be an adapter and wrong when you are asking whether it is one.
409
512
  */
410
513
  hasAdapterType(typeName) {
411
- return this.adapterTypes.has(typeName);
514
+ return this.adapterTypes.has(this.canonicalAdapterTypeName(typeName));
412
515
  }
413
516
  getTextSearchAdapterType(typeName) {
414
517
  return this.textSearchAdapterTypes.get(typeName);
@@ -422,6 +525,71 @@ export default class PluginManager {
422
525
  getViewType(typeName) {
423
526
  return this.viewTypes.get(typeName);
424
527
  }
528
+ /**
529
+ * The registered view and display types a snapshot names, from every `type`
530
+ * string anywhere in it — `heldForMissingPlugins` included. Over-collection
531
+ * is harmless: a name that resolves to neither is dropped here.
532
+ */
533
+ sessionElementTypes(sessionSnapshot) {
534
+ const names = new Set();
535
+ const walk = (node) => {
536
+ if (Array.isArray(node)) {
537
+ for (const item of node) {
538
+ walk(item);
539
+ }
540
+ }
541
+ else if (node && typeof node === 'object') {
542
+ for (const [key, value] of Object.entries(node)) {
543
+ if (key === 'type' && typeof value === 'string') {
544
+ names.add(value);
545
+ }
546
+ else {
547
+ walk(value);
548
+ }
549
+ }
550
+ }
551
+ };
552
+ walk(sessionSnapshot);
553
+ return [...names].flatMap(name => {
554
+ const display = this.resolveDisplayTypeRecord(name);
555
+ return [
556
+ ...(this.viewTypes.has(name) ? [this.getViewType(name)] : []),
557
+ ...(display ? [display] : []),
558
+ ];
559
+ });
560
+ }
561
+ /**
562
+ * Every async code path that hands a snapshot to a synchronous session
563
+ * instantiation (`setSession`, `cast`) must await this first.
564
+ */
565
+ async preloadSessionTypes(sessionSnapshot) {
566
+ await Promise.all(this.sessionElementTypes(sessionSnapshot).map(t => t.loadStateModel()));
567
+ }
568
+ unloadedSessionTypes(sessionSnapshot) {
569
+ return this.sessionElementTypes(sessionSnapshot)
570
+ .filter(t => !t.isStateModelLoaded)
571
+ .map(t => t.name);
572
+ }
573
+ /**
574
+ * Without this, a snapshot naming an unloaded lazy type fails inside MST as
575
+ * a union mismatch that reads like a corrupt snapshot.
576
+ */
577
+ assertSessionTypesLoaded(sessionSnapshot) {
578
+ const unloaded = this.unloadedSessionTypes(sessionSnapshot);
579
+ if (unloaded.length > 0) {
580
+ throw new Error(`session names lazily loaded types that are not loaded yet: ${unloaded.join(', ')}. Await pluginManager.preloadSessionTypes(snapshot) before setting the session`);
581
+ }
582
+ }
583
+ /**
584
+ * Aliases resolve here because a legacy session names the alias, and the
585
+ * model that remaps it cannot be consulted before it is loaded.
586
+ */
587
+ resolveDisplayTypeRecord(name) {
588
+ if (this.displayTypes.has(name)) {
589
+ return this.getDisplayType(name);
590
+ }
591
+ return this.getDisplayElements().find(d => d.aliases?.includes(name));
592
+ }
425
593
  getAddTrackWorkflow(typeName) {
426
594
  return this.addTrackWidgets.get(typeName);
427
595
  }
@@ -7,4 +7,4 @@ export const BaseFeatureDetail = lazyMap({
7
7
  BaseCard: lazy(() => import('../BaseFeatureWidget/BaseFeatureDetail/BaseCard.js')),
8
8
  BaseAttributes: lazy(() => import('../BaseFeatureWidget/BaseFeatureDetail/BaseAttributes.js')),
9
9
  BaseCoreDetails: lazy(() => import('../BaseFeatureWidget/BaseFeatureDetail/BaseCoreDetails.js')),
10
- }, '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/');
10
+ });
@@ -1,2 +1,3 @@
1
1
  import type { ComponentType } from 'react';
2
+ export declare const DATA_GRID_NAMES: string[];
2
3
  export declare const DataGridEntries: Record<string, ComponentType<any>>;
@@ -3,7 +3,7 @@ import { lazy } from 'react';
3
3
  // export off it, so they're generated from a name list rather than written out
4
4
  // 45 times. The static import specifier means webpack still emits a single
5
5
  // x-data-grid chunk shared by all entries.
6
- const names = [
6
+ export const DATA_GRID_NAMES = [
7
7
  'DataGrid',
8
8
  'GridActionsCellItem',
9
9
  'GridAddIcon',
@@ -51,7 +51,7 @@ const names = [
51
51
  'GridViewHeadlineIcon',
52
52
  'GridViewStreamIcon',
53
53
  ];
54
- export const DataGridEntries = Object.fromEntries(names.map(name => [
54
+ export const DataGridEntries = Object.fromEntries(DATA_GRID_NAMES.map(name => [
55
55
  name,
56
56
  lazy(() => import('@mui/x-data-grid').then(m => ({
57
57
  default: m[name],
@@ -0,0 +1,5 @@
1
+ export declare const BARREL_ONLY_NAMES: {
2
+ '@jbrowse/core/ui': never[];
3
+ '@jbrowse/core/util/tracks': never[];
4
+ '@jbrowse/core/util': string[];
5
+ };
@@ -0,0 +1,63 @@
1
+ // Names a source barrel exports that its ABI file deliberately does not serve.
2
+ //
3
+ // The public*.ts split stopped a barrel removal from silently narrowing the ABI
4
+ // -- take a name out of ui/index.ts and publicUi.tsx no longer compiles. This
5
+ // closes the other direction, which the split opened: a name *added* to a barrel
6
+ // is not served until someone adds it here too, and an external plugin cannot
7
+ // see that. Its `import { X } from '@jbrowse/core/ui'` type-checks, because
8
+ // package.json's `./ui` resolves to the barrel, and reads undefined off
9
+ // JBrowseExports at runtime -- the same shape of failure a removal used to
10
+ // cause, just for new code instead of old.
11
+ //
12
+ // So publicSurface.test.ts requires barrel-minus-ABI to be exactly this list.
13
+ // Adding an export to a barrel then fails until the ABI question is answered,
14
+ // and either answer is one line: serve it from the public*.ts file, or name it
15
+ // here. Nothing about being on this list is wrong -- most of these are helpers
16
+ // for app code, session plumbing and in-tree hooks that a plugin has no use for.
17
+ export const BARREL_ONLY_NAMES = {
18
+ '@jbrowse/core/ui': [],
19
+ '@jbrowse/core/util/tracks': [],
20
+ '@jbrowse/core/util': [
21
+ 'MAX_GLOB_REGIONS',
22
+ 'adapterNeedsAddTrackComponent',
23
+ 'assembleLocStrings',
24
+ 'canonicalizeViewRefName',
25
+ 'clampToContig',
26
+ 'clampToListedContig',
27
+ 'containerDisplaysAssembly',
28
+ 'downloadPhase',
29
+ 'finishAddTrack',
30
+ 'formatBytes',
31
+ 'getDisplayStr',
32
+ 'getLocationUri',
33
+ 'handleFetchError',
34
+ 'indexedDBAvailable',
35
+ 'installablePlugins',
36
+ 'isSessionWithMultipleViews',
37
+ 'isSessionWithPermanentPlugins',
38
+ 'isWebWorker',
39
+ 'localStorageAvailable',
40
+ 'localStorageGetStringArray',
41
+ 'matchRefNames',
42
+ 'measuredFont',
43
+ 'namesTemporaryAssembly',
44
+ 'notifyFeatureDetailsMiss',
45
+ 'notifyLocalStorageKey',
46
+ 'parseBpString',
47
+ 'parseRegionNames',
48
+ 'resolveNamedRegions',
49
+ 'resolveStorePluginRefs',
50
+ 'resolveStoreRefs',
51
+ 'resolveUri',
52
+ 'sessionStorageAvailable',
53
+ 'sessionStorageGetItem',
54
+ 'sessionStorageRemoveItem',
55
+ 'sessionStorageSetItem',
56
+ 'statusSource',
57
+ 'subscribeToLocalStorageKey',
58
+ 'throttleStatusEmits',
59
+ 'useCreateOnce',
60
+ 'useFinalUnmount',
61
+ 'withFeatureDetails',
62
+ ],
63
+ };