@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.
Files changed (115) hide show
  1. package/README.md +42 -3
  2. package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +2 -2
  3. package/esm/PluginLoader.d.ts +1 -0
  4. package/esm/PluginLoader.js +52 -8
  5. package/esm/PluginManager.d.ts +70 -17
  6. package/esm/PluginManager.js +151 -20
  7. package/esm/ReExports/modules.js +3 -53
  8. package/esm/ReExports/publicUtil.d.ts +1 -1
  9. package/esm/ReExports/publicUtil.js +1 -1
  10. package/esm/ReExports/sharedModules.d.ts +96 -0
  11. package/esm/ReExports/sharedModules.js +59 -0
  12. package/esm/ReExports/uiStub.d.ts +1 -0
  13. package/esm/ReExports/uiStub.js +13 -0
  14. package/esm/ReExports/workerModules.d.ts +2 -0
  15. package/esm/ReExports/workerModules.js +10 -0
  16. package/esm/configuration/configurationSchema.d.ts +5 -38
  17. package/esm/configuration/configurationSchema.js +53 -40
  18. package/esm/configuration/promotableDefaults.js +12 -6
  19. package/esm/configuration/promotableResolve.d.ts +1 -1
  20. package/esm/configuration/promotableResolve.js +1 -1
  21. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +12 -0
  22. package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +39 -0
  23. package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +29 -0
  24. package/esm/data_adapters/BaseAdapter/featureDensity.js +17 -0
  25. package/esm/data_adapters/BaseAdapter/index.d.ts +1 -0
  26. package/esm/data_adapters/BaseAdapter/index.js +1 -0
  27. package/esm/data_adapters/BaseAdapter/stats.d.ts +33 -1
  28. package/esm/data_adapters/BaseAdapter/stats.js +52 -13
  29. package/esm/data_adapters/BaseAdapter/types.d.ts +1 -0
  30. package/esm/pluggableElementTypes/DisplayType.d.ts +9 -10
  31. package/esm/pluggableElementTypes/DisplayType.js +43 -8
  32. package/esm/pluggableElementTypes/ViewType.d.ts +25 -2
  33. package/esm/pluggableElementTypes/ViewType.js +75 -2
  34. package/esm/pluggableElementTypes/extendElementType.js +8 -3
  35. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +41 -0
  36. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +74 -1
  37. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +21 -0
  38. package/esm/pluggableElementTypes/models/BaseTrackModel.js +37 -2
  39. package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +49 -0
  40. package/esm/pluggableElementTypes/models/BaseViewModel.js +39 -0
  41. package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +10 -7
  42. package/esm/pluggableElementTypes/models/assertDisplayContract.js +23 -30
  43. package/esm/rpc/RpcRegistry.d.ts +10 -0
  44. package/esm/rpc/byteBudget.d.ts +9 -0
  45. package/esm/rpc/coreRpcMethods.d.ts +1 -0
  46. package/esm/rpc/coreRpcMethods.js +1 -0
  47. package/esm/rpc/methods/CoreGetFeatureDensity.d.ts +11 -0
  48. package/esm/rpc/methods/CoreGetFeatureDensity.js +22 -0
  49. package/esm/svg/svgReady.d.ts +1 -0
  50. package/esm/ui/CascadingMenu.js +30 -8
  51. package/esm/ui/LoadingOverlay.d.ts +14 -0
  52. package/esm/ui/LoadingOverlay.js +1 -1
  53. package/esm/ui/MenuTypes.d.ts +11 -2
  54. package/esm/ui/MenuTypes.js +3 -0
  55. package/esm/ui/PaletteContext.d.ts +11 -4
  56. package/esm/ui/PaletteContext.js +34 -5
  57. package/esm/ui/PinAdornment.js +12 -6
  58. package/esm/ui/index.d.ts +1 -1
  59. package/esm/ui/index.js +1 -1
  60. package/esm/ui/launchViewMenu.js +7 -1
  61. package/esm/ui/legendMenuItem.d.ts +5 -5
  62. package/esm/ui/legendMenuItem.js +5 -5
  63. package/esm/ui/makeSizeMenu.js +1 -1
  64. package/esm/ui/menuItems.d.ts +1 -1
  65. package/esm/ui/menuItems.js +1 -1
  66. package/esm/ui/promotableMenuItems.js +8 -6
  67. package/esm/ui/promotablePinCoverage.js +2 -1
  68. package/esm/ui/toggleMenuItems.d.ts +1 -3
  69. package/esm/util/addAndShowTrack.d.ts +1 -1
  70. package/esm/util/addAndShowTrack.js +5 -1
  71. package/esm/util/addTrackFromWidget.js +2 -2
  72. package/esm/util/cacheBudgets.d.ts +22 -19
  73. package/esm/util/cacheBudgets.js +22 -19
  74. package/esm/util/createStopTokenRotation.d.ts +1 -0
  75. package/esm/util/createStopTokenRotation.js +7 -27
  76. package/esm/util/fetchContext.d.ts +2 -2
  77. package/esm/util/fetchContext.js +2 -2
  78. package/esm/util/fetchPhases.d.ts +9 -9
  79. package/esm/util/hooks.d.ts +8 -0
  80. package/esm/util/hooks.js +27 -0
  81. package/esm/util/index.d.ts +2 -3
  82. package/esm/util/index.js +2 -3
  83. package/esm/util/installFetch.d.ts +49 -23
  84. package/esm/util/installFetch.js +49 -21
  85. package/esm/util/installInitAutorun.d.ts +8 -8
  86. package/esm/util/installInitAutorun.js +15 -15
  87. package/esm/util/lazyAfterAttach.d.ts +13 -0
  88. package/esm/util/lazyAfterAttach.js +31 -0
  89. package/esm/util/legendCandidates.d.ts +50 -0
  90. package/esm/util/legendCandidates.js +87 -0
  91. package/esm/util/mstUtils.d.ts +12 -1
  92. package/esm/util/mstUtils.js +20 -2
  93. package/esm/util/pluginStore.d.ts +1 -0
  94. package/esm/util/pluginStore.js +8 -1
  95. package/esm/util/range.d.ts +13 -9
  96. package/esm/util/range.js +17 -8
  97. package/esm/util/rxjs.d.ts +16 -0
  98. package/esm/util/rxjs.js +49 -0
  99. package/esm/util/tabix.d.ts +10 -4
  100. package/esm/util/tabix.js +11 -5
  101. package/esm/util/tracks.d.ts +48 -0
  102. package/esm/util/tracks.js +192 -66
  103. package/esm/util/types/index.d.ts +48 -5
  104. package/esm/util/types/index.js +3 -0
  105. package/esm/util/unknownSnapshotKeys.d.ts +36 -0
  106. package/esm/util/unknownSnapshotKeys.js +69 -0
  107. package/esm/util/viewStatus.d.ts +23 -0
  108. package/esm/util/viewStatus.js +17 -0
  109. package/esm/util/whenViewSettled.d.ts +17 -4
  110. package/esm/util/whenViewSettled.js +19 -6
  111. package/esm/util/withLaunchInput.d.ts +123 -0
  112. package/esm/util/withLaunchInput.js +228 -0
  113. package/package.json +12 -9
  114. package/esm/util/applyInitSettings.d.ts +0 -79
  115. package/esm/util/applyInitSettings.js +0 -91
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Wait until a view has either initialized or failed, and report which.
2
+ * Wait until a view has either initialized — launch blob consumed and all — or
3
+ * failed, and report which.
3
4
  *
4
5
  * `initialized` folds in a view's assemblies, so an assembly that fails to load
5
6
  * leaves it false **forever**. A bare `when(() => view.initialized)` therefore
@@ -9,6 +10,15 @@
9
10
  * export hung behind its dialog's spinner, and a launched breakpoint split view
10
11
  * that opened but was never navigated or zoomed.
11
12
  *
13
+ * `pendingLaunch` is the other half, and it matters because `initialized` can
14
+ * go true MID-launch: LGV's flips the moment displayedRegions land, while the
15
+ * same apply pass still has tracks to attach — so a caller acting there reads a
16
+ * positioned view with its tracks missing (an SVG export saves it that way).
17
+ * A launch failure cannot hang this: the state machine either clears the blob
18
+ * (materialized, reported as a snackbar) or sets the view's `error`
19
+ * (installInitAutorun's failure policy). A view with no launch machinery has no
20
+ * `pendingLaunch` and the term is vacuously satisfied.
21
+ *
12
22
  * Every view exposes a resolved `error` beside `initialized`, so waiting on the
13
23
  * pair turns the hang into something reportable. What to report is the caller's,
14
24
  * because the vocabularies genuinely differ — an export says "cannot export"
@@ -16,12 +26,15 @@
16
26
  * says the launch failed — which is why this returns the answer instead of
17
27
  * throwing one wording at both.
18
28
  *
19
- * No time bound, deliberately: `initialized || error` is a terminal pair, so a
20
- * slow-but-healthy remote assembly is waited out rather than guessed at.
29
+ * No time bound, deliberately: `(initialized && launch consumed) || error` is a
30
+ * terminal set, so a slow-but-healthy remote assembly is waited out rather than
31
+ * guessed at.
21
32
  *
22
- * @returns true if the view initialized, false if it settled on an error
33
+ * @returns true if the view initialized (and consumed any launch), false if it
34
+ * settled on an error
23
35
  */
24
36
  export declare function whenViewSettled(view: {
25
37
  initialized: boolean;
26
38
  error: unknown;
39
+ pendingLaunch?: unknown;
27
40
  }): Promise<boolean>;
@@ -1,6 +1,7 @@
1
1
  import { when } from 'mobx';
2
2
  /**
3
- * Wait until a view has either initialized or failed, and report which.
3
+ * Wait until a view has either initialized — launch blob consumed and all — or
4
+ * failed, and report which.
4
5
  *
5
6
  * `initialized` folds in a view's assemblies, so an assembly that fails to load
6
7
  * leaves it false **forever**. A bare `when(() => view.initialized)` therefore
@@ -10,6 +11,15 @@ import { when } from 'mobx';
10
11
  * export hung behind its dialog's spinner, and a launched breakpoint split view
11
12
  * that opened but was never navigated or zoomed.
12
13
  *
14
+ * `pendingLaunch` is the other half, and it matters because `initialized` can
15
+ * go true MID-launch: LGV's flips the moment displayedRegions land, while the
16
+ * same apply pass still has tracks to attach — so a caller acting there reads a
17
+ * positioned view with its tracks missing (an SVG export saves it that way).
18
+ * A launch failure cannot hang this: the state machine either clears the blob
19
+ * (materialized, reported as a snackbar) or sets the view's `error`
20
+ * (installInitAutorun's failure policy). A view with no launch machinery has no
21
+ * `pendingLaunch` and the term is vacuously satisfied.
22
+ *
13
23
  * Every view exposes a resolved `error` beside `initialized`, so waiting on the
14
24
  * pair turns the hang into something reportable. What to report is the caller's,
15
25
  * because the vocabularies genuinely differ — an export says "cannot export"
@@ -17,12 +27,15 @@ import { when } from 'mobx';
17
27
  * says the launch failed — which is why this returns the answer instead of
18
28
  * throwing one wording at both.
19
29
  *
20
- * No time bound, deliberately: `initialized || error` is a terminal pair, so a
21
- * slow-but-healthy remote assembly is waited out rather than guessed at.
30
+ * No time bound, deliberately: `(initialized && launch consumed) || error` is a
31
+ * terminal set, so a slow-but-healthy remote assembly is waited out rather than
32
+ * guessed at.
22
33
  *
23
- * @returns true if the view initialized, false if it settled on an error
34
+ * @returns true if the view initialized (and consumed any launch), false if it
35
+ * settled on an error
24
36
  */
25
37
  export async function whenViewSettled(view) {
26
- await when(() => view.initialized || !!view.error);
27
- return view.initialized;
38
+ const ready = () => view.initialized && view.pendingLaunch === undefined;
39
+ await when(() => ready() || !!view.error);
40
+ return ready();
28
41
  }
@@ -0,0 +1,123 @@
1
+ export { legacyInitMessage, unknownKeysMessage } from './unknownSnapshotKeys.ts';
2
+ import type { IAnyModelType, IModelType, SnapshotIn } from '@jbrowse/mobx-state-tree';
3
+ /**
4
+ * How a launch key's value is told apart from the state property of the same
5
+ * name. A key whose name collides with nothing is `launch`; the rest name the
6
+ * discriminator that splits one authored array into the recipe entries the
7
+ * launcher resolves and the built snapshots MST restores.
8
+ *
9
+ * `replay` is the one that is not remapped at all: the value lands on its
10
+ * property, and a copy rides in the blob so the launcher can run the ordered
11
+ * imperative step the property write alone does not produce.
12
+ */
13
+ export type LaunchKeyKind = 'launch' | 'trackEntries' | 'rows' | 'highlightEntries' | 'replay';
14
+ export interface LaunchKeySpec {
15
+ kind: LaunchKeyKind;
16
+ }
17
+ export interface LaunchKeyRegistration<Commands, PassThrough extends string = never> {
18
+ keys: Record<string, LaunchKeySpec>;
19
+ passThrough: readonly PassThrough[];
20
+ /** never assigned; it carries `Commands` to `withLaunchInput`'s return type */
21
+ commands?: Commands;
22
+ }
23
+ /**
24
+ * The launch keys a view captured, plus what it has to report.
25
+ *
26
+ * `Commands` is not made partial: hand-authored JSON is what fills this, so a
27
+ * key the interface marks required is not a guarantee here either way, and the
28
+ * launcher already guards the ones it cannot proceed without.
29
+ */
30
+ export type LaunchInput<Commands> = Commands & {
31
+ unknown?: Record<string, unknown>;
32
+ malformed?: Record<string, unknown>;
33
+ legacyInit?: boolean;
34
+ };
35
+ /**
36
+ * The blob when it still holds something to apply, else undefined. A view's
37
+ * gates ask this rather than the raw property: a snapshot whose only launch
38
+ * content was a typo has nothing to launch, and a view that thinks otherwise
39
+ * waits on an assembly nobody named and never leaves the spinner.
40
+ *
41
+ * Returns the blob itself, never a copy — the launch autorun clears by
42
+ * identity, and a fresh object per read strands the init it just applied.
43
+ */
44
+ export declare function pendingLaunch<C>(launch: LaunchInput<C> | undefined): LaunchInput<C> | undefined;
45
+ /**
46
+ * A view's snapshot type, widened to the authored shape: every launch key is
47
+ * accepted beside the declared properties, and a key that is both takes either
48
+ * meaning. Excess-property checking then makes a misspelled key an error at
49
+ * every literal site — a `defaultSession` view, an `addView` call, a
50
+ * `createViewState` spec.
51
+ */
52
+ export type LaunchSnapshotIn<M extends IAnyModelType, Commands, PassThrough extends string = never> = Omit<SnapshotIn<M>, keyof Commands> & {
53
+ [K in keyof Commands]?: K extends keyof SnapshotIn<M> ? Commands[K] | SnapshotIn<M>[K] : Commands[K];
54
+ } & {
55
+ [K in PassThrough]?: unknown;
56
+ } & {
57
+ /**
58
+ * @deprecated v4's nesting. Write every setting directly on the view object;
59
+ * this is unwrapped on the way in and warns.
60
+ */
61
+ init?: Commands;
62
+ };
63
+ type ReservedKey = 'id' | 'type' | 'launch';
64
+ /**
65
+ * What a view can be launched with, derived: the keys its launcher interprets
66
+ * (`Commands`) plus every declared property of its state model, optional, in
67
+ * the property's own snapshot type. Nothing is restated, so a property is
68
+ * authorable — and TYPED — from the line that declares it.
69
+ *
70
+ * ```ts
71
+ * export type LinearSyntenyViewInit = ViewInit<
72
+ * LinearSyntenyViewStateModel,
73
+ * LinearSyntenyViewCommands
74
+ * >
75
+ * ```
76
+ *
77
+ * The twin of `LaunchSnapshotIn` above, from the other end: that one widens a
78
+ * snapshot type for a literal MST site, this one is what a caller BUILDING a
79
+ * spec annotates, so its commands stay required and its properties optional.
80
+ */
81
+ export type ViewInit<M extends IAnyModelType, Commands> = Commands & Partial<Omit<SnapshotIn<M>, keyof Commands | ReservedKey>>;
82
+ export type LaunchInputModel<M extends IAnyModelType, Commands, PassThrough extends string = never> = M extends IModelType<infer P, infer O, any, infer S> ? IModelType<P, O, LaunchSnapshotIn<M, Commands, PassThrough>, S> : never;
83
+ /**
84
+ * Declare one view's launch keys. The argument is
85
+ * `Record<keyof Commands, LaunchKeySpec>`, so a command the view interprets and
86
+ * nobody registered is a compile error rather than a key that partitions as a
87
+ * typo and warns.
88
+ *
89
+ * `passThrough` names keys that are neither a launch key nor a declared
90
+ * property and still belong on the snapshot — a legacy spelling the view's own
91
+ * `preProcessSnapshot` converts.
92
+ */
93
+ export declare function defineLaunchKeys<Commands>(): <const P extends readonly string[] = readonly never[]>(keys: Record<keyof Commands, LaunchKeySpec>, { passThrough }?: {
94
+ passThrough?: P;
95
+ }) => LaunchKeyRegistration<Commands, P[number]>;
96
+ /**
97
+ * Accept a view's settings written directly on the view object, and move the
98
+ * ones a launcher has to resolve into the internal `launch` property.
99
+ *
100
+ * The partition is a `preProcessSnapshot` and is PURE. A session's view type is
101
+ * a `types.union`, so MST runs every member's preprocessor against every
102
+ * candidate snapshot while deciding which one matches, and runs it about twice
103
+ * more per instantiation; a warning from in here fires against snapshots that
104
+ * are about to be rejected. `afterAttach` — reached only by a snapshot that won
105
+ * — reports what the partition captured.
106
+ *
107
+ * ORDER: MST runs preprocessors in the reverse of the order they were added, so
108
+ * this belongs on the chain BEFORE a view's own legacy-key preprocessor, where
109
+ * it partitions the snapshot MST finally consumes rather than a key that remap
110
+ * is about to convert.
111
+ *
112
+ * The widening cast is the LAST link that may change the creation type: it
113
+ * replaces `CustomC`, so a `.props()` added after it is invisible to
114
+ * `SnapshotIn`. Further `preProcessSnapshot`/`postProcessSnapshot` links are
115
+ * fine — both carry `CustomC` through.
116
+ *
117
+ * Not `types.snapshotProcessor`, which carries the same widened input and stops
118
+ * being a `ModelType`. `PluginManager.pluggableMstType` filters union members on
119
+ * `isModelType`, so a wrapped view is dropped from the session's view union
120
+ * without a word, and `ViewType.stateModel` and every `.properties`
121
+ * introspection site break with it.
122
+ */
123
+ export declare function withLaunchInput<M extends IAnyModelType, Commands, PassThrough extends string>(model: M, registration: LaunchKeyRegistration<Commands, PassThrough>): LaunchInputModel<M, Commands, PassThrough>;
@@ -0,0 +1,228 @@
1
+ import { reportLegacyInit, reportMalformedRows, reportUnknownKeys, } from './unknownSnapshotKeys.js';
2
+ // re-exported here because the surfaces that classify their own keys — a
3
+ // session spec, which launches without ever building a snapshot — reach the
4
+ // partition's vocabulary through this module
5
+ export { legacyInitMessage, unknownKeysMessage } from './unknownSnapshotKeys.js';
6
+ // What afterAttach reports rather than applies. They live in the blob so the
7
+ // partition has one place to put everything, and `pendingLaunch` is what keeps
8
+ // them from reading as work to do.
9
+ const REPORTED = new Set(['unknown', 'malformed', 'legacyInit']);
10
+ /**
11
+ * The blob when it still holds something to apply, else undefined. A view's
12
+ * gates ask this rather than the raw property: a snapshot whose only launch
13
+ * content was a typo has nothing to launch, and a view that thinks otherwise
14
+ * waits on an assembly nobody named and never leaves the spinner.
15
+ *
16
+ * Returns the blob itself, never a copy — the launch autorun clears by
17
+ * identity, and a fresh object per read strands the init it just applied.
18
+ */
19
+ export function pendingLaunch(launch) {
20
+ return launch && Object.keys(launch).some(k => !REPORTED.has(k))
21
+ ? launch
22
+ : undefined;
23
+ }
24
+ /**
25
+ * Declare one view's launch keys. The argument is
26
+ * `Record<keyof Commands, LaunchKeySpec>`, so a command the view interprets and
27
+ * nobody registered is a compile error rather than a key that partitions as a
28
+ * typo and warns.
29
+ *
30
+ * `passThrough` names keys that are neither a launch key nor a declared
31
+ * property and still belong on the snapshot — a legacy spelling the view's own
32
+ * `preProcessSnapshot` converts.
33
+ */
34
+ export function defineLaunchKeys() {
35
+ return (keys, { passThrough } = {}) => ({
36
+ keys,
37
+ passThrough: passThrough ?? [],
38
+ });
39
+ }
40
+ // `id` picks up MST's optional identifier and `type` picks the view, so neither
41
+ // is ever a setting and neither can be lifted off the snapshot.
42
+ const IDENTITY = new Set(['id', 'type']);
43
+ const LAUNCH = 'launch';
44
+ function isObject(value) {
45
+ return !!value && typeof value === 'object';
46
+ }
47
+ // Each per-entry kind says only which entries are an authored recipe. A recipe
48
+ // goes to the blob for the launcher to resolve; everything else is a built
49
+ // snapshot and stays on the property, so a mixed array splits rather than
50
+ // picking one meaning for the whole thing.
51
+ function isRecipeEntry(kind, entry) {
52
+ switch (kind) {
53
+ case 'trackEntries':
54
+ // a built track snapshot cannot carry `trackId` — BaseTrackModel does not
55
+ // declare it. `type` does not discriminate: a spec writes display types
56
+ // inline
57
+ return (typeof entry === 'string' || (isObject(entry) && 'trackId' in entry));
58
+ case 'highlightEntries':
59
+ // a string needs coerceHighlight and the assembly manager, neither of
60
+ // which a pure preprocessor can reach; an object is the persisted shape
61
+ return typeof entry === 'string';
62
+ case 'rows':
63
+ // a built row is a view snapshot, and every view's `type` is a required
64
+ // literal
65
+ return !(isObject(entry) && 'type' in entry);
66
+ default:
67
+ return true;
68
+ }
69
+ }
70
+ function splitEntries(kind, value) {
71
+ const entries = Array.isArray(value) ? value : [value];
72
+ const recipes = entries.filter(e => isRecipeEntry(kind, e));
73
+ const built = entries.filter(e => !isRecipeEntry(kind, e));
74
+ return {
75
+ launch: recipes.length
76
+ ? Array.isArray(value)
77
+ ? recipes
78
+ : value
79
+ : undefined,
80
+ state: built.length ? (Array.isArray(value) ? built : value) : undefined,
81
+ };
82
+ }
83
+ function classify(snap, { keys, passThrough }, known, out) {
84
+ for (const [key, value] of Object.entries(snap)) {
85
+ const spec = keys[key];
86
+ if (IDENTITY.has(key)) {
87
+ out.state[key] = value;
88
+ }
89
+ else if (spec?.kind === 'launch') {
90
+ out.launch[key] = value;
91
+ }
92
+ else if (spec?.kind === 'replay') {
93
+ out.state[key] = value;
94
+ out.launch[key] = value;
95
+ }
96
+ else if (spec) {
97
+ const { launch, state } = splitEntries(spec.kind, value);
98
+ if (spec.kind === 'rows' && launch !== undefined && state !== undefined) {
99
+ // A row list indexes against `levels` and `tracks[i]`, so lifting half
100
+ // of it renumbers the other half. Nothing here can throw — a pure
101
+ // preprocessor runs against snapshots the union is about to reject — so
102
+ // the whole list goes to the bucket afterAttach reports and the view
103
+ // comes up on its import form rather than on a silently misaligned
104
+ // stack.
105
+ out.malformed[key] = value;
106
+ }
107
+ else {
108
+ if (launch !== undefined) {
109
+ out.launch[key] = launch;
110
+ }
111
+ if (state !== undefined) {
112
+ out.state[key] = state;
113
+ }
114
+ }
115
+ }
116
+ else if (known.has(key) || passThrough.includes(key)) {
117
+ out.state[key] = value;
118
+ }
119
+ else {
120
+ out.unknown[key] = value;
121
+ }
122
+ }
123
+ }
124
+ // BreakpointSplitView's v4 `init` was a bare array of panels rather than an
125
+ // object of settings. A positional list can only be the view's row list, and a
126
+ // view declares at most one, so it is read as that key rather than classified
127
+ // per index — which would name "0" and "1" as unknown keys.
128
+ function legacyInitSnapshot(init, { keys }) {
129
+ const rows = Object.entries(keys).find(([, spec]) => spec.kind === 'rows')?.[0];
130
+ return Array.isArray(init) && rows ? { [rows]: init } : init;
131
+ }
132
+ /**
133
+ * Accept a view's settings written directly on the view object, and move the
134
+ * ones a launcher has to resolve into the internal `launch` property.
135
+ *
136
+ * The partition is a `preProcessSnapshot` and is PURE. A session's view type is
137
+ * a `types.union`, so MST runs every member's preprocessor against every
138
+ * candidate snapshot while deciding which one matches, and runs it about twice
139
+ * more per instantiation; a warning from in here fires against snapshots that
140
+ * are about to be rejected. `afterAttach` — reached only by a snapshot that won
141
+ * — reports what the partition captured.
142
+ *
143
+ * ORDER: MST runs preprocessors in the reverse of the order they were added, so
144
+ * this belongs on the chain BEFORE a view's own legacy-key preprocessor, where
145
+ * it partitions the snapshot MST finally consumes rather than a key that remap
146
+ * is about to convert.
147
+ *
148
+ * The widening cast is the LAST link that may change the creation type: it
149
+ * replaces `CustomC`, so a `.props()` added after it is invisible to
150
+ * `SnapshotIn`. Further `preProcessSnapshot`/`postProcessSnapshot` links are
151
+ * fine — both carry `CustomC` through.
152
+ *
153
+ * Not `types.snapshotProcessor`, which carries the same widened input and stops
154
+ * being a `ModelType`. `PluginManager.pluggableMstType` filters union members on
155
+ * `isModelType`, so a wrapped view is dropped from the session's view union
156
+ * without a word, and `ViewType.stateModel` and every `.properties`
157
+ * introspection site break with it.
158
+ */
159
+ export function withLaunchInput(model, registration) {
160
+ const known = new Set(Object.keys(model.properties));
161
+ const partitioned = model
162
+ .preProcessSnapshot((snap) => {
163
+ if (!isObject(snap)) {
164
+ return snap;
165
+ }
166
+ const { init, ...rest } = snap;
167
+ const out = {
168
+ state: {},
169
+ launch: {},
170
+ unknown: {},
171
+ malformed: {},
172
+ };
173
+ if (isObject(init)) {
174
+ // v4's nesting, unwrapped rather than refused: a nested key sorts the
175
+ // same three ways a flat one does, so honoring it costs one more pass
176
+ // and a deprecation warning, where dropping it opens an admin's
177
+ // `defaultSession` on its defaults. First, so a snapshot spelling one
178
+ // key both ways resolves to the flat one the docs teach.
179
+ classify(legacyInitSnapshot(init, registration), registration, known, out);
180
+ out.launch.legacyInit = true;
181
+ }
182
+ classify(rest, registration, known, out);
183
+ if (Object.keys(out.unknown).length) {
184
+ out.launch.unknown = out.unknown;
185
+ }
186
+ if (Object.keys(out.malformed).length) {
187
+ out.launch.malformed = out.malformed;
188
+ }
189
+ const persisted = out.state[LAUNCH];
190
+ return Object.keys(out.launch).length
191
+ ? {
192
+ ...out.state,
193
+ [LAUNCH]: {
194
+ ...(isObject(persisted) ? persisted : {}),
195
+ ...out.launch,
196
+ },
197
+ }
198
+ : out.state;
199
+ })
200
+ .actions(self => ({
201
+ afterAttach() {
202
+ const { launch } = self;
203
+ if (launch?.legacyInit) {
204
+ reportLegacyInit(self);
205
+ }
206
+ reportUnknownKeys(self, Object.keys(launch?.unknown ?? {}));
207
+ reportMalformedRows(self, Object.keys(launch?.malformed ?? {}));
208
+ },
209
+ }))
210
+ // `legacyInit` names the SPELLING of the snapshot this view was opened from,
211
+ // and a saved one does not use it — so persisting the flag makes every later
212
+ // restore report nesting the snapshot it is reading does not contain,
213
+ // forever, since nothing clears it (`pendingLaunch` excludes the report
214
+ // keys, so the launch autorun never runs). `unknown` and `malformed` stay:
215
+ // those name content that was DISCARDED, which is still true of the saved
216
+ // snapshot, and the blob is the only record of it.
217
+ .postProcessSnapshot((snap) => {
218
+ const launch = snap[LAUNCH];
219
+ if (!isObject(launch) || !launch.legacyInit) {
220
+ return snap;
221
+ }
222
+ const { legacyInit, ...kept } = launch;
223
+ return Object.keys(kept).length
224
+ ? { ...snap, [LAUNCH]: kept }
225
+ : Object.fromEntries(Object.entries(snap).filter(([key]) => key !== LAUNCH));
226
+ });
227
+ return partitioned;
228
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/core",
3
- "version": "5.0.0-beta.1",
3
+ "version": "5.0.0-beta.2",
4
4
  "type": "module",
5
5
  "description": "JBrowse 2 core libraries used by plugins",
6
6
  "keywords": [
@@ -32,6 +32,8 @@
32
32
  "./PluginLoader": "./esm/PluginLoader.js",
33
33
  "./PluginManager": "./esm/PluginManager.js",
34
34
  "./ReExports/list": "./esm/ReExports/list.js",
35
+ "./ReExports/modules": "./esm/ReExports/modules.js",
36
+ "./ReExports/registry": "./esm/ReExports/registry.js",
35
37
  "./TextSearch/BaseResults": "./esm/TextSearch/BaseResults.js",
36
38
  "./TextSearch/TextSearchManager": "./esm/TextSearch/TextSearchManager.js",
37
39
  "./assemblyManager": "./esm/assemblyManager/index.js",
@@ -140,14 +142,12 @@
140
142
  "./util/Base1DUtils": "./esm/util/Base1DUtils.js",
141
143
  "./util/Base1DViewModel": "./esm/util/Base1DViewModel.js",
142
144
  "./util/FileSaver": "./esm/util/FileSaver/index.js",
143
- "./util/QuickLRU": "./esm/util/QuickLRU/index.js",
144
145
  "./util/SvgCanvas": "./esm/util/SvgCanvas.js",
145
146
  "./util/TimeTraveller": "./esm/util/TimeTraveller.js",
146
147
  "./util/aborting": "./esm/util/aborting.js",
147
148
  "./util/adapterMetadata": "./esm/util/adapterMetadata.js",
148
149
  "./util/addRelativeUris": "./esm/util/addRelativeUris.js",
149
150
  "./util/analytics": "./esm/util/analytics.js",
150
- "./util/applyInitSettings": "./esm/util/applyInitSettings.js",
151
151
  "./util/assemblyConfigUtils": "./esm/util/assemblyConfigUtils.js",
152
152
  "./util/bandHeight": "./esm/util/bandHeight.js",
153
153
  "./util/bandLayout": "./esm/util/bandLayout.js",
@@ -194,7 +194,9 @@
194
194
  "./util/layouts": "./esm/util/layouts/index.js",
195
195
  "./util/layouts/GranularRectLayout": "./esm/util/layouts/GranularRectLayout.js",
196
196
  "./util/layouts/placeRect": "./esm/util/layouts/placeRect.js",
197
+ "./util/lazyAfterAttach": "./esm/util/lazyAfterAttach.js",
197
198
  "./util/leadingEdgeAutorun": "./esm/util/leadingEdgeAutorun.js",
199
+ "./util/legendCandidates": "./esm/util/legendCandidates.js",
198
200
  "./util/librpc": "./esm/util/librpc.js",
199
201
  "./util/localStorage": "./esm/util/localStorage.js",
200
202
  "./util/makeAssembly": "./esm/util/makeAssembly.js",
@@ -235,7 +237,8 @@
235
237
  "./util/viewStatus": "./esm/util/viewStatus.js",
236
238
  "./util/virtualRange": "./esm/util/virtualRange.js",
237
239
  "./util/wheelZoom": "./esm/util/wheelZoom.js",
238
- "./util/whenViewSettled": "./esm/util/whenViewSettled.js"
240
+ "./util/whenViewSettled": "./esm/util/whenViewSettled.js",
241
+ "./util/withLaunchInput": "./esm/util/withLaunchInput.js"
239
242
  },
240
243
  "files": [
241
244
  "esm"
@@ -253,9 +256,9 @@
253
256
  "@jbrowse/mobx-state-tree": "^6.5.1",
254
257
  "@jbrowse/quick-lru": "^7.3.5",
255
258
  "@leeoniya/ufuzzy": "^1.0.19",
256
- "@mui/icons-material": "^9.3.1",
257
- "@mui/material": "^9.3.1",
258
- "@mui/system": "^9.3.0",
259
+ "@mui/icons-material": "^9.4.0",
260
+ "@mui/material": "^9.4.0",
261
+ "@mui/system": "^9.4.0",
259
262
  "@mui/x-data-grid": "^9.12.0",
260
263
  "canvas-sequencer-ts": "^3.1.3",
261
264
  "compare-versions": "^6.1.1",
@@ -267,8 +270,8 @@
267
270
  "pako-esm2": "^2.0.2",
268
271
  "rxjs": "^7.8.2",
269
272
  "source-map-js": "^1.2.1",
270
- "@jbrowse/add-track-core": "^5.0.0-beta.1",
271
- "@jbrowse/render-core": "^5.0.0-beta.1"
273
+ "@jbrowse/add-track-core": "^5.0.0-beta.2",
274
+ "@jbrowse/render-core": "^5.0.0-beta.2"
272
275
  },
273
276
  "peerDependencies": {
274
277
  "react": ">=19.0.0",
@@ -1,79 +0,0 @@
1
- import type { IAnyModelType, IStateTreeNode, SnapshotIn } from '@jbrowse/mobx-state-tree';
2
- /**
3
- * A view's init type, derived: the keys its launcher interprets (`Commands`)
4
- * plus every declared property of its state model, optional, in the property's
5
- * own snapshot type. Nothing is restated, so a property is authorable — and
6
- * TYPED — from the line that declares it.
7
- *
8
- * ```ts
9
- * export type LinearSyntenyViewInit = ViewInit<
10
- * LinearSyntenyViewStateModel,
11
- * LinearSyntenyViewCommands
12
- * >
13
- * ```
14
- *
15
- * THE ONE RULE THIS DEPENDS ON: the model's own `init` property must be typed
16
- * as the `Commands`, not as this. `init` is a property, so an `init` typed as
17
- * the whole thing makes the whole thing depend on itself — TS7023 on the
18
- * factory, then implicit-any across every file that touches the view (ADR-055,
19
- * same failure the view/display instance pair has). Commands never mention the
20
- * model, so typing `init` as those breaks the loop, and it costs nothing: the
21
- * view's own code reads the commands out of `self.init`, and the settings half
22
- * is read only by `applyInitSettings`, which asks the model rather than a type.
23
- */
24
- export type ViewInit<M extends IAnyModelType, Commands> = Commands & Partial<Omit<SnapshotIn<M>, keyof Commands | (typeof RESERVED_KEYS)[number]>>;
25
- export declare const RESERVED_KEYS: readonly ['id', 'type', 'init'];
26
- export interface InitSettingsReport {
27
- /** keys applied, in the order they were found */
28
- applied: string[];
29
- /** keys naming no declared property — a typo, or a prop that never existed */
30
- unknown: string[];
31
- /** declared keys whose value the property's own type rejected */
32
- invalid: string[];
33
- }
34
- /**
35
- * Apply the plain-settings half of an `init` blob: every key naming a declared
36
- * MST property of `self` is written straight to it, and the caller handles only
37
- * the keys it names in `commands`.
38
- *
39
- * THE POINT IS THAT THERE IS NO PER-SETTING CODE. A view used to need a
40
- * hand-written `if (init.x !== undefined) self.setX(init.x)` arm per property,
41
- * so a new toggle worked from its menu the moment it was declared and was
42
- * dropped in silence from every authored surface — a session spec, a share
43
- * link, a config `defaultSession` — until someone remembered the second edit.
44
- * Nobody did: `drawLocationMarkers` shipped unauthorable, and DotplotView's
45
- * `alpha`/`drawCigar`/`lineWidth`/`lockAspectRatio` never had an arm at all.
46
- * Reading the property list off the model at runtime means declaring the
47
- * property IS declaring it authorable, including properties a composed mixin
48
- * brings in (colorBy, showColorLegend), and there is nothing left to keep in
49
- * sync.
50
- *
51
- * `commands` is what a launcher genuinely has to interpret: keys with no
52
- * property behind them (`loc`, `autoDiagonalize`), keys whose name collides
53
- * with a property meaning something else (a spec's `views` is a list of
54
- * assemblies to open; the model's `views` is the built rows), and keys with an
55
- * ordering constraint (`levelHeights` must land after the auto-scale pass).
56
- * Listing one is a decision to write code for it; everything else needs none.
57
- *
58
- * WRITTEN AS A PATCH, not through `setX`. Applying init is state restoration,
59
- * and the authority on that is MST: `applyPatch` validates each value against
60
- * the property's own type and touches nothing else, where `applySnapshot`
61
- * would rebuild the view's children. That it skips the setters is correct
62
- * rather than a shortcut — a session reload restores the same state from a
63
- * snapshot and calls no setters either, so a property needing a setter's side
64
- * effect to be correct is already broken on reload, and the effect belongs in a
65
- * reaction on the property.
66
- *
67
- * A value the property's type rejects is dropped and reported rather than
68
- * thrown: init blobs come from URLs, and one bad number should not cost the
69
- * reader the whole view.
70
- */
71
- export declare function applyInitSettings(self: IStateTreeNode, init: object | undefined, { commands }?: {
72
- commands?: readonly string[];
73
- }): InitSettingsReport;
74
- /**
75
- * Say what `applyInitSettings` refused, naming the surface. Split from the
76
- * apply so a caller under test can assert the report without a console, and so
77
- * the two messages read the same wherever they come from.
78
- */
79
- export declare function warnInitSettings(surface: string, report: InitSettingsReport): void;