jbrowse-plugin-msaview 3.4.0 → 3.4.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 (86) hide show
  1. package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +4 -6
  2. package/dist/LaunchMsaView/components/BlastQuery/searchChoiceStorage.d.ts +7 -0
  3. package/dist/LaunchMsaView/components/BlastQuery/searchChoiceStorage.js +42 -0
  4. package/dist/LaunchMsaView/components/BlastQuery/searchChoiceStorage.test.d.ts +1 -0
  5. package/dist/LaunchMsaView/components/BlastQuery/searchChoiceStorage.test.js +32 -0
  6. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +2 -1
  7. package/dist/LaunchMsaViewExtensionPoint/index.js +11 -6
  8. package/dist/LaunchMsaViewExtensionPoint/index.test.d.ts +1 -0
  9. package/dist/LaunchMsaViewExtensionPoint/index.test.js +43 -0
  10. package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +15 -0
  11. package/dist/MsaViewPanel/afterCreateAutoruns.js +93 -70
  12. package/dist/MsaViewPanel/components/LaunchProgress.js +11 -2
  13. package/dist/MsaViewPanel/components/MsaViewPanel.test.js +19 -0
  14. package/dist/MsaViewPanel/doLaunchBlast.d.ts +3 -1
  15. package/dist/MsaViewPanel/doLaunchBlast.js +10 -12
  16. package/dist/MsaViewPanel/doLaunchOrthologs.d.ts +3 -1
  17. package/dist/MsaViewPanel/doLaunchOrthologs.js +6 -5
  18. package/dist/MsaViewPanel/doLaunchOrthologs.test.js +38 -24
  19. package/dist/MsaViewPanel/model.d.ts +160 -10
  20. package/dist/MsaViewPanel/model.js +51 -1
  21. package/dist/MsaViewPanel/msaDataStore.d.ts +5 -3
  22. package/dist/MsaViewPanel/msaDataStore.js +16 -7
  23. package/dist/MsaViewPanel/msaDataStore.test.d.ts +1 -0
  24. package/dist/MsaViewPanel/msaDataStore.test.js +44 -0
  25. package/dist/MsaViewPanel/runLaunch.d.ts +38 -0
  26. package/dist/MsaViewPanel/runLaunch.js +65 -0
  27. package/dist/MsaViewPanel/runLaunch.test.d.ts +1 -0
  28. package/dist/MsaViewPanel/runLaunch.test.js +129 -0
  29. package/dist/MsaViewPanel/storedData.test.d.ts +1 -0
  30. package/dist/MsaViewPanel/storedData.test.js +160 -0
  31. package/dist/jbrowse-plugin-msaview.umd.production.min.js +35 -35
  32. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  33. package/dist/utils/blastCache.js +32 -0
  34. package/dist/utils/blastCache.test.d.ts +1 -0
  35. package/dist/utils/blastCache.test.js +72 -0
  36. package/dist/utils/ebiBlast.d.ts +4 -2
  37. package/dist/utils/ebiBlast.js +6 -4
  38. package/dist/utils/ebiJobDispatcher.d.ts +6 -3
  39. package/dist/utils/ebiJobDispatcher.js +13 -6
  40. package/dist/utils/fetch.d.ts +8 -1
  41. package/dist/utils/fetch.js +28 -2
  42. package/dist/utils/msa.d.ts +4 -2
  43. package/dist/utils/msa.js +9 -5
  44. package/dist/utils/ncbiDomains.d.ts +2 -2
  45. package/dist/utils/ncbiOrthologs.d.ts +11 -1
  46. package/dist/utils/ncbiOrthologs.js +26 -7
  47. package/dist/utils/ncbiOrthologs.test.js +23 -1
  48. package/dist/utils/pantherOrthologs.js +2 -10
  49. package/dist/utils/phmmer.d.ts +2 -1
  50. package/dist/utils/phmmer.js +4 -2
  51. package/dist/utils/poll.d.ts +6 -1
  52. package/dist/utils/poll.js +7 -2
  53. package/dist/version.d.ts +1 -1
  54. package/dist/version.js +1 -1
  55. package/package.json +14 -14
  56. package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +7 -11
  57. package/src/LaunchMsaView/components/BlastQuery/searchChoiceStorage.test.ts +43 -0
  58. package/src/LaunchMsaView/components/BlastQuery/searchChoiceStorage.ts +64 -0
  59. package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +2 -2
  60. package/src/LaunchMsaViewExtensionPoint/index.test.ts +51 -0
  61. package/src/LaunchMsaViewExtensionPoint/index.ts +21 -6
  62. package/src/MsaViewPanel/afterCreateAutoruns.ts +100 -66
  63. package/src/MsaViewPanel/components/LaunchProgress.tsx +20 -2
  64. package/src/MsaViewPanel/components/MsaViewPanel.test.tsx +21 -0
  65. package/src/MsaViewPanel/doLaunchBlast.ts +14 -10
  66. package/src/MsaViewPanel/doLaunchOrthologs.test.ts +41 -26
  67. package/src/MsaViewPanel/doLaunchOrthologs.ts +8 -4
  68. package/src/MsaViewPanel/model.ts +54 -1
  69. package/src/MsaViewPanel/msaDataStore.test.ts +54 -0
  70. package/src/MsaViewPanel/msaDataStore.ts +22 -13
  71. package/src/MsaViewPanel/runLaunch.test.ts +154 -0
  72. package/src/MsaViewPanel/runLaunch.ts +102 -0
  73. package/src/MsaViewPanel/storedData.test.ts +196 -0
  74. package/src/utils/blastCache.test.ts +86 -0
  75. package/src/utils/blastCache.ts +34 -1
  76. package/src/utils/ebiBlast.ts +8 -2
  77. package/src/utils/ebiJobDispatcher.ts +18 -3
  78. package/src/utils/fetch.ts +29 -2
  79. package/src/utils/msa.ts +11 -3
  80. package/src/utils/ncbiDomains.ts +4 -2
  81. package/src/utils/ncbiOrthologs.test.ts +25 -0
  82. package/src/utils/ncbiOrthologs.ts +27 -7
  83. package/src/utils/pantherOrthologs.ts +6 -11
  84. package/src/utils/phmmer.ts +5 -1
  85. package/src/utils/poll.ts +8 -1
  86. package/src/version.ts +1 -1
@@ -33,10 +33,24 @@ const setQuerySeqName = vi.fn();
33
33
  function makeModel(orthologParams) {
34
34
  return {
35
35
  orthologParams,
36
- setProgress: () => { },
37
36
  setQuerySeqName,
38
37
  };
39
38
  }
39
+ // a scope that never cancels, so these tests see the launch's own behaviour --
40
+ // what the scope does when it IS cancelled is runLaunch.test.ts's subject
41
+ function launch({ self }) {
42
+ return doLaunchOrthologs({
43
+ self,
44
+ scope: {
45
+ signal: new AbortController().signal,
46
+ act: fn => {
47
+ fn();
48
+ },
49
+ onProgress: () => { },
50
+ onRid: () => { },
51
+ },
52
+ });
53
+ }
40
54
  function params(extra = {}) {
41
55
  return {
42
56
  taxId: HUMAN,
@@ -79,11 +93,11 @@ beforeEach(() => {
79
93
  });
80
94
  describe('which species become rows', () => {
81
95
  test('omitted taxa asks for no restriction at all, which is every ortholog NCBI has', async () => {
82
- await doLaunchOrthologs({ self: makeModel(params()) });
96
+ await launch({ self: makeModel(params()) });
83
97
  expect(rowRequest().taxa).toBeUndefined();
84
98
  });
85
99
  test('given taxa is taken as written', async () => {
86
- await doLaunchOrthologs({
100
+ await launch({
87
101
  self: makeModel(params({ taxa: [HUMAN, 10090, 9615] })),
88
102
  });
89
103
  expect(rowRequest().taxa).toEqual([9606, 9615, 10090]);
@@ -92,20 +106,20 @@ describe('which species become rows', () => {
92
106
  // and "the query row already covers this one" stay separable -- an unrestricted
93
107
  // launch still has to drop the query species.
94
108
  test('the query species is excluded whether or not taxa was given', async () => {
95
- await doLaunchOrthologs({ self: makeModel(params()) });
109
+ await launch({ self: makeModel(params()) });
96
110
  expect(rowRequest().exclude).toBe(HUMAN);
97
111
  vi.clearAllMocks();
98
112
  mockResolveGeneId.mockResolvedValue({ geneId: GENE_ID, matched: 'NLRP1' });
99
113
  mockFetchProtein.mockResolvedValue(REPRESENTATIVE);
100
114
  mockFetchRows.mockResolvedValue([]);
101
115
  mockLaunchMSA.mockResolvedValue({ msa: '', tree: '' });
102
- await doLaunchOrthologs({
116
+ await launch({
103
117
  self: makeModel(params({ taxa: [HUMAN, 10090] })),
104
118
  });
105
119
  expect(rowRequest().exclude).toBe(HUMAN);
106
120
  });
107
121
  test('an empty list is a request for no rows, not a request for all of them', async () => {
108
- await doLaunchOrthologs({ self: makeModel(params({ taxa: [] })) });
122
+ await launch({ self: makeModel(params({ taxa: [] })) });
109
123
  expect(rowRequest().taxa).toEqual([]);
110
124
  });
111
125
  });
@@ -114,11 +128,11 @@ describe('which species become rows', () => {
114
128
  // second a row.
115
129
  describe('the row cap', () => {
116
130
  test('is passed through when given', async () => {
117
- await doLaunchOrthologs({ self: makeModel(params({ maxSpecies: 12 })) });
131
+ await launch({ self: makeModel(params({ maxSpecies: 12 })) });
118
132
  expect(rowRequest().limit).toBe(12);
119
133
  });
120
134
  test('omitted leaves the default to fetchOrthologGenes rather than sending Infinity', async () => {
121
- await doLaunchOrthologs({ self: makeModel(params()) });
135
+ await launch({ self: makeModel(params()) });
122
136
  expect(rowRequest().limit).toBeUndefined();
123
137
  expect(defaultMaxSpecies).toBeGreaterThan(2);
124
138
  });
@@ -129,7 +143,7 @@ describe('the row cap', () => {
129
143
  // model's querySeqName and the header have to be the same string.
130
144
  describe('the query row name', () => {
131
145
  test('is the species, marked, rather than a bare QUERY among named rows', async () => {
132
- await doLaunchOrthologs({ self: makeModel(params()) });
146
+ await launch({ self: makeModel(params()) });
133
147
  expect(queryRowName()).toBe('human_query');
134
148
  expect(setQuerySeqName).toHaveBeenCalledWith('human_query');
135
149
  });
@@ -137,41 +151,41 @@ describe('the query row name', () => {
137
151
  mockFetchRows.mockResolvedValue([
138
152
  { label: 'human_query', sequence: 'MM' },
139
153
  ]);
140
- await doLaunchOrthologs({ self: makeModel(params()) });
154
+ await launch({ self: makeModel(params()) });
141
155
  expect(queryRowName()).toBe('human_query_2');
142
156
  expect(setQuerySeqName).toHaveBeenCalledWith('human_query_2');
143
157
  });
144
158
  test('falls back rather than throwing when NCBI cannot name the taxon', async () => {
145
159
  vi.spyOn(console, 'warn').mockImplementation(() => { });
146
160
  mockFetchTaxonomy.mockRejectedValue(new Error('429'));
147
- await doLaunchOrthologs({ self: makeModel(params()) });
161
+ await launch({ self: makeModel(params()) });
148
162
  expect(queryRowName()).toBe('query_query');
149
163
  });
150
164
  test('the metadata that drives the domain overlay is keyed to that same name', async () => {
151
- const result = await doLaunchOrthologs({ self: makeModel(params()) });
165
+ const result = await launch({ self: makeModel(params()) });
152
166
  expect(Object.keys(JSON.parse(result.treeMetadata))).toContain('human_query');
153
167
  });
154
168
  });
155
169
  describe('the query row sequence', () => {
156
170
  test('omitted proteinSequence falls back to the representative protein', async () => {
157
- await doLaunchOrthologs({ self: makeModel(params()) });
171
+ await launch({ self: makeModel(params()) });
158
172
  expect(queryRowSent()).toBe(REPRESENTATIVE.sequence);
159
173
  });
160
174
  test('a supplied sequence is used, and is cleaned first', async () => {
161
- await doLaunchOrthologs({
175
+ await launch({
162
176
  self: makeModel(params({ proteinSequence: 'MAGG*AWGR&' })),
163
177
  });
164
178
  expect(queryRowSent()).toBe('MAGGAWGR');
165
179
  });
166
180
  test('throws when neither a sequence nor a representative is available', async () => {
167
181
  mockFetchProtein.mockResolvedValue(undefined);
168
- await expect(doLaunchOrthologs({ self: makeModel(params()) })).rejects.toThrow(/No query protein/);
182
+ await expect(launch({ self: makeModel(params()) })).rejects.toThrow(/No query protein/);
169
183
  expect(mockLaunchMSA).not.toHaveBeenCalled();
170
184
  });
171
185
  test('a failed representative lookup does not take down a launch that brought its own sequence', async () => {
172
186
  vi.spyOn(console, 'warn').mockImplementation(() => { });
173
187
  mockFetchProtein.mockRejectedValue(new Error('429'));
174
- await doLaunchOrthologs({
188
+ await launch({
175
189
  self: makeModel(params({ proteinSequence: REPRESENTATIVE.sequence })),
176
190
  });
177
191
  expect(queryRowSent()).toBe(REPRESENTATIVE.sequence);
@@ -183,14 +197,14 @@ describe('the query row sequence', () => {
183
197
  // here, in both directions.
184
198
  describe('the Accession that drives the domain overlay', () => {
185
199
  test('is attached when the query row IS the representative protein', async () => {
186
- const result = await doLaunchOrthologs({ self: makeModel(params()) });
200
+ const result = await launch({ self: makeModel(params()) });
187
201
  expect(queryMetadata(result)).toMatchObject({
188
202
  'Gene ID': GENE_ID,
189
203
  Accession: REPRESENTATIVE.accession,
190
204
  });
191
205
  });
192
206
  test('is withheld from a non-representative isoform', async () => {
193
- const result = await doLaunchOrthologs({
207
+ const result = await launch({
194
208
  self: makeModel(params({ proteinSequence: 'MDIFFERENTISOFORM' })),
195
209
  });
196
210
  expect(queryMetadata(result).Accession).toBeUndefined();
@@ -198,7 +212,7 @@ describe('the Accession that drives the domain overlay', () => {
198
212
  test('is withheld when the representative lookup failed', async () => {
199
213
  vi.spyOn(console, 'warn').mockImplementation(() => { });
200
214
  mockFetchProtein.mockRejectedValue(new Error('429'));
201
- const result = await doLaunchOrthologs({
215
+ const result = await launch({
202
216
  self: makeModel(params({ proteinSequence: REPRESENTATIVE.sequence })),
203
217
  });
204
218
  expect(queryMetadata(result).Accession).toBeUndefined();
@@ -235,12 +249,12 @@ describe('the PANTHER source', () => {
235
249
  mockFetchTaxonomy.mockResolvedValue(new Map([[YEAST, { sciname: 'Saccharomyces cerevisiae' }]]));
236
250
  });
237
251
  test('source omitted is NCBI, so an old launch never reaches PANTHER', async () => {
238
- await doLaunchOrthologs({ self: makeModel(params()) });
252
+ await launch({ self: makeModel(params()) });
239
253
  expect(mockFetchPanther).not.toHaveBeenCalled();
240
254
  expect(mockResolveGeneId).toHaveBeenCalled();
241
255
  });
242
256
  test('source panther asks PANTHER with the same species semantics, and skips NCBI', async () => {
243
- await doLaunchOrthologs({
257
+ await launch({
244
258
  self: makeModel({
245
259
  taxId: YEAST,
246
260
  source: 'panther',
@@ -260,7 +274,7 @@ describe('the PANTHER source', () => {
260
274
  expect(limit).toBe(7);
261
275
  });
262
276
  test("the query row is PANTHER's own entry for the gene when no sequence was supplied, and carries its UniProt accession for the domain overlay", async () => {
263
- const result = await doLaunchOrthologs({
277
+ const result = await launch({
264
278
  self: makeModel({
265
279
  taxId: YEAST,
266
280
  source: 'panther',
@@ -280,7 +294,7 @@ describe('the PANTHER source', () => {
280
294
  });
281
295
  });
282
296
  test('a supplied sequence still wins, and a different isoform earns no Accession', async () => {
283
- const result = await doLaunchOrthologs({
297
+ const result = await launch({
284
298
  self: makeModel({
285
299
  taxId: YEAST,
286
300
  source: 'panther',
@@ -294,7 +308,7 @@ describe('the PANTHER source', () => {
294
308
  });
295
309
  test('names PANTHER when it has no protein for the query row', async () => {
296
310
  mockFetchPanther.mockResolvedValue({ ...found, query: undefined });
297
- await expect(doLaunchOrthologs({
311
+ await expect(launch({
298
312
  self: makeModel({
299
313
  taxId: YEAST,
300
314
  source: 'panther',
@@ -1,5 +1,6 @@
1
1
  export type { MSAFormat } from 'msa-parsers';
2
2
  import type { BlastDatabase, MsaAlgorithm, PhmmerDatabase } from '../LaunchMsaView/components/BlastQuery/consts';
3
+ import type { MsaDataPayload } from './msaDataStore';
3
4
  import type { MafRegion, MsaViewInitState } from './types';
4
5
  import type { Feature } from '@jbrowse/core/util';
5
6
  import type { Instance } from '@jbrowse/mobx-state-tree';
@@ -85,7 +86,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
85
86
  id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
86
87
  displayName: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
87
88
  minimized: import("@jbrowse/mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
88
- }, "id" | "type" | "autoTreeAreaWidth" | "drawLabels" | "drawNodeBubbles" | "drawTree" | "labelsAlignRight" | "showBranchLen" | "treeAreaWidth" | "treeWidth" | "bgColor" | "colorSchemeName" | "msaFormat" | "showColumnStats" | "allowedGappyness" | "colWidth" | "collapsed" | "currentAlignment" | "data" | "drawMsaLetters" | "featureFilters" | "gffFilehandle" | "height" | "hideGaps" | "highlightColumns" | "msaFilehandle" | "relativeTo" | "rowHeight" | "scrollX" | "scrollY" | "scrollZoom" | "showDomainLegend" | "showDomains" | "showOnly" | "subFeatureRows" | "treeFilehandle" | "treeMetadataFilehandle" | "turnedOffTracks"> & Omit<Omit<Omit<{}, "autoTreeAreaWidth" | "drawLabels" | "drawNodeBubbles" | "drawTree" | "labelsAlignRight" | "showBranchLen" | "treeAreaWidth" | "treeWidth"> & {
89
+ }, "id" | "type" | "autoTreeAreaWidth" | "drawLabels" | "drawNodeBubbles" | "drawTree" | "labelsAlignRight" | "showBranchLen" | "treeAreaWidth" | "treeWidth" | "bgColor" | "colorSchemeName" | "msaFormat" | "showColumnStats" | "allowedGappyness" | "colWidth" | "collapsed" | "columnTracks" | "currentAlignment" | "data" | "drawMsaLetters" | "featureFilters" | "gffFilehandle" | "height" | "hideGaps" | "highlightColumns" | "highlights" | "msaFilehandle" | "relativeTo" | "rowHeight" | "scrollX" | "scrollY" | "scrollZoom" | "showDomainLegend" | "showDomains" | "showOnly" | "subFeatureRows" | "treeFilehandle" | "treeMetadataFilehandle" | "turnedOffTracks"> & Omit<Omit<Omit<{}, "autoTreeAreaWidth" | "drawLabels" | "drawNodeBubbles" | "drawTree" | "labelsAlignRight" | "showBranchLen" | "treeAreaWidth" | "treeWidth"> & {
89
90
  drawLabels: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
90
91
  labelsAlignRight: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
91
92
  treeAreaWidth: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<number>, [undefined]>;
@@ -99,7 +100,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
99
100
  colorSchemeName: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
100
101
  showColumnStats: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
101
102
  msaFormat: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<import("msa-parsers").MSAFormat>>;
102
- }, "id" | "type" | "allowedGappyness" | "colWidth" | "collapsed" | "currentAlignment" | "data" | "drawMsaLetters" | "featureFilters" | "gffFilehandle" | "height" | "hideGaps" | "highlightColumns" | "msaFilehandle" | "relativeTo" | "rowHeight" | "scrollX" | "scrollY" | "scrollZoom" | "showDomainLegend" | "showDomains" | "showOnly" | "subFeatureRows" | "treeFilehandle" | "treeMetadataFilehandle" | "turnedOffTracks"> & {
103
+ }, "id" | "type" | "allowedGappyness" | "colWidth" | "collapsed" | "columnTracks" | "currentAlignment" | "data" | "drawMsaLetters" | "featureFilters" | "gffFilehandle" | "height" | "hideGaps" | "highlightColumns" | "highlights" | "msaFilehandle" | "relativeTo" | "rowHeight" | "scrollX" | "scrollY" | "scrollZoom" | "showDomainLegend" | "showDomains" | "showOnly" | "subFeatureRows" | "treeFilehandle" | "treeMetadataFilehandle" | "turnedOffTracks"> & {
103
104
  id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
104
105
  showDomains: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
105
106
  showDomainLegend: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
@@ -494,6 +495,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
494
495
  collapsed: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").ISimpleType<string>>, [undefined]>;
495
496
  showOnly: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
496
497
  turnedOffTracks: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IMapType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>>, [undefined]>;
498
+ columnTracks: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IType<import("react-msaview").ColumnTrackSpec, import("react-msaview").ColumnTrackSpec, import("react-msaview").ColumnTrackSpec>>, [undefined]>;
497
499
  data: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IModelType<{
498
500
  tree: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
499
501
  msa: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
@@ -510,6 +512,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
510
512
  featureFilters: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IMapType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>>, [undefined]>;
511
513
  relativeTo: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
512
514
  highlightColumns: import("@jbrowse/mobx-state-tree").IType<number[] | undefined, number[] | undefined, number[] | undefined>;
515
+ highlights: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IType<import("react-msaview").Highlight, import("react-msaview").Highlight, import("react-msaview").Highlight>>, [undefined]>;
513
516
  }, "init" | "querySeqName" | "zoomToBaseLevel" | "connectedViewId" | "connectedFeature" | "blastParams" | "orthologParams" | "uniprotId" | "dataStoreId" | "mafRegion"> & {
514
517
  connectedViewId: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
515
518
  connectedFeature: import("@jbrowse/mobx-state-tree").IType<any, any, any>;
@@ -591,6 +594,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
591
594
  setError(error?: unknown): void;
592
595
  setMousePos(col?: number, row?: number): void;
593
596
  setHighlightedColumns(columns?: number[]): void;
597
+ setHighlights(highlights: import("react-msaview").Highlight[]): void;
594
598
  setShowDomains(arg: boolean): void;
595
599
  setShowDomainLegend(arg: boolean): void;
596
600
  setSubFeatureRows(arg: boolean): void;
@@ -608,6 +612,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
608
612
  }): void;
609
613
  setMSAFilehandle(msaFilehandle?: import("@jbrowse/core/util").FileLocation): void;
610
614
  setTreeFilehandle(treeFilehandle?: import("@jbrowse/core/util").FileLocation): void;
615
+ setTreeMetadataFilehandle(treeMetadataFilehandle?: import("@jbrowse/core/util").FileLocation): void;
611
616
  setGFFFilehandle(gffFilehandle?: import("@jbrowse/core/util").FileLocation): void;
612
617
  setMSA(result: string): void;
613
618
  setTree(result: string): void;
@@ -715,6 +720,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
715
720
  applyGFFText(gffText: string): void;
716
721
  doScrollX(deltaX: number): void;
717
722
  setScrollX(n: number): void;
723
+ setColumnTracks(tracks: import("react-msaview").ColumnTrackSpec[]): void;
718
724
  toggleTrack(id: string): void;
719
725
  setStatus(status?: {
720
726
  msg: string;
@@ -728,6 +734,11 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
728
734
  readonly secondaryStructureConsensus: string | undefined;
729
735
  readonly seqConsensus: string | undefined;
730
736
  readonly adapterTrackModels: import("react-msaview").BasicTrack[];
737
+ readonly columnTrackContent: Map<string, {
738
+ values?: number[];
739
+ data?: string;
740
+ }>;
741
+ readonly columnTrackModels: import("react-msaview").BasicTrack[];
731
742
  readonly tracks: import("react-msaview").BasicTrack[];
732
743
  readonly turnedOnTracks: import("react-msaview").BasicTrack[];
733
744
  readonly showHorizontalScrollbar: boolean;
@@ -767,6 +778,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
767
778
  start: number;
768
779
  end: number;
769
780
  }[];
781
+ readonly resolvedHighlights: import("react-msaview").ResolvedHighlight[];
770
782
  readonly mouseOverColumnStats: {
771
783
  col: number;
772
784
  total: number;
@@ -792,12 +804,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
792
804
  setConservationTrackHeight(arg: number): void;
793
805
  setSequenceLogoTrackHeight(arg: number): void;
794
806
  reset(): void;
795
- exportSVG(opts: {
796
- theme: import("@mui/material").Theme;
797
- includeMinimap?: boolean;
798
- includeTracks?: boolean;
799
- exportType: "entire" | "viewport";
800
- }): Promise<void>;
807
+ exportSVG(opts: import("react-msaview").ExportSvgOptions): Promise<void>;
801
808
  initFilter(arg: string): void;
802
809
  setFilter(arg: string, flag: boolean): void;
803
810
  fit(): void;
@@ -810,6 +817,8 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
810
817
  error: unknown;
811
818
  loadingStoredData: boolean;
812
819
  isStoringData: boolean;
820
+ lastStoredData: MsaDataPayload | undefined;
821
+ launchController: AbortController | undefined;
813
822
  domainsRequested: boolean;
814
823
  } & {
815
824
  /**
@@ -908,6 +917,23 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
908
917
  * #action
909
918
  */
910
919
  setIsStoringData(arg: boolean): void;
920
+ /**
921
+ * #action
922
+ */
923
+ setLastStoredData(arg?: MsaDataPayload): void;
924
+ /**
925
+ * #action
926
+ */
927
+ setLaunchController(arg?: AbortController): void;
928
+ /**
929
+ * #action
930
+ * Abandon the launch in flight. The EBI job keeps running on their side —
931
+ * nothing here can recall it, and its JobLink stays valid — so this only
932
+ * stops the polling and the writes it would make. Dropping the params is
933
+ * what keeps the request from refiring; the view falls back to the import
934
+ * form, which is where react-msaview's own cancel leaves it too.
935
+ */
936
+ cancelLaunch(): void;
911
937
  /**
912
938
  * #action
913
939
  */
@@ -937,7 +963,129 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
937
963
  id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
938
964
  displayName: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
939
965
  minimized: import("@jbrowse/mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
940
- }> & {
966
+ }> & (({
967
+ bgColor: boolean;
968
+ colorSchemeName: string;
969
+ showColumnStats: boolean;
970
+ msaFormat: import("msa-parsers").MSAFormat | undefined;
971
+ drawLabels: boolean;
972
+ labelsAlignRight: boolean;
973
+ treeAreaWidth: number;
974
+ treeWidth: number;
975
+ showBranchLen: boolean;
976
+ drawTree: boolean;
977
+ drawNodeBubbles: boolean;
978
+ autoTreeAreaWidth: boolean;
979
+ columnTracks: import("react-msaview").ColumnTrackSpec[];
980
+ id: string;
981
+ showDomains: boolean;
982
+ showDomainLegend: boolean;
983
+ hideGaps: boolean;
984
+ allowedGappyness: number;
985
+ subFeatureRows: boolean;
986
+ type: "MsaView";
987
+ drawMsaLetters: boolean;
988
+ scrollZoom: boolean;
989
+ height: number;
990
+ rowHeight: number;
991
+ scrollY: number;
992
+ scrollX: number;
993
+ colWidth: number;
994
+ treeFilehandle: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
995
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"LocalPathLocation">;
996
+ localPath: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
997
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
998
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"BlobLocation">;
999
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1000
+ blobId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1001
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1002
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"FileHandleLocation">;
1003
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1004
+ handleId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1005
+ }> | {
1006
+ locationType: "UriLocation";
1007
+ uri: string;
1008
+ internetAccountId: string | undefined;
1009
+ internetAccountPreAuthorization: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1010
+ internetAccountType: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1011
+ authInfo: import("@jbrowse/mobx-state-tree").IType<any, any, any>;
1012
+ }> | undefined;
1013
+ } | undefined;
1014
+ msaFilehandle: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1015
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"LocalPathLocation">;
1016
+ localPath: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1017
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1018
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"BlobLocation">;
1019
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1020
+ blobId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1021
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1022
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"FileHandleLocation">;
1023
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1024
+ handleId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1025
+ }> | {
1026
+ locationType: "UriLocation";
1027
+ uri: string;
1028
+ internetAccountId: string | undefined;
1029
+ internetAccountPreAuthorization: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1030
+ internetAccountType: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1031
+ authInfo: import("@jbrowse/mobx-state-tree").IType<any, any, any>;
1032
+ }> | undefined;
1033
+ } | undefined;
1034
+ treeMetadataFilehandle: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1035
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"LocalPathLocation">;
1036
+ localPath: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1037
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1038
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"BlobLocation">;
1039
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1040
+ blobId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1041
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1042
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"FileHandleLocation">;
1043
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1044
+ handleId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1045
+ }> | {
1046
+ locationType: "UriLocation";
1047
+ uri: string;
1048
+ internetAccountId: string | undefined;
1049
+ internetAccountPreAuthorization: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1050
+ internetAccountType: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1051
+ authInfo: import("@jbrowse/mobx-state-tree").IType<any, any, any>;
1052
+ }> | undefined;
1053
+ } | undefined;
1054
+ gffFilehandle: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1055
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"LocalPathLocation">;
1056
+ localPath: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1057
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1058
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"BlobLocation">;
1059
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1060
+ blobId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1061
+ }> | import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1062
+ locationType: import("@jbrowse/mobx-state-tree").ISimpleType<"FileHandleLocation">;
1063
+ name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1064
+ handleId: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1065
+ }> | {
1066
+ locationType: "UriLocation";
1067
+ uri: string;
1068
+ internetAccountId: string | undefined;
1069
+ internetAccountPreAuthorization: import("@jbrowse/mobx-state-tree").ModelSnapshotType<{
1070
+ internetAccountType: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
1071
+ authInfo: import("@jbrowse/mobx-state-tree").IType<any, any, any>;
1072
+ }> | undefined;
1073
+ } | undefined;
1074
+ currentAlignment: number;
1075
+ collapsed: string[];
1076
+ showOnly: string | undefined;
1077
+ turnedOffTracks: import("mobx").IKeyValueMap<boolean>;
1078
+ featureFilters: import("mobx").IKeyValueMap<boolean>;
1079
+ relativeTo: string | undefined;
1080
+ highlightColumns: number[] | undefined;
1081
+ highlights: import("react-msaview").Highlight[];
1082
+ data: {
1083
+ tree?: string | undefined;
1084
+ msa?: string | undefined;
1085
+ treeMetadata?: string | undefined;
1086
+ gff?: string | undefined;
1087
+ };
1088
+ } & import("@jbrowse/mobx-state-tree")._NotCustomized) | ({
941
1089
  bgColor: boolean;
942
1090
  colorSchemeName: string;
943
1091
  showColumnStats: boolean;
@@ -950,6 +1098,7 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
950
1098
  drawTree: boolean;
951
1099
  drawNodeBubbles: boolean;
952
1100
  autoTreeAreaWidth: boolean;
1101
+ columnTracks?: undefined;
953
1102
  id: string;
954
1103
  showDomains: boolean;
955
1104
  showDomainLegend: boolean;
@@ -1051,13 +1200,14 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
1051
1200
  featureFilters: import("mobx").IKeyValueMap<boolean>;
1052
1201
  relativeTo: string | undefined;
1053
1202
  highlightColumns: number[] | undefined;
1203
+ highlights: import("react-msaview").Highlight[];
1054
1204
  data: {
1055
1205
  tree?: string | undefined;
1056
1206
  msa?: string | undefined;
1057
1207
  treeMetadata?: string | undefined;
1058
1208
  gff?: string | undefined;
1059
1209
  };
1060
- } & import("@jbrowse/mobx-state-tree")._NotCustomized>;
1210
+ } & import("@jbrowse/mobx-state-tree")._NotCustomized))>;
1061
1211
  export type JBrowsePluginMsaViewStateModel = ReturnType<typeof stateModelFactory>;
1062
1212
  export type JBrowsePluginMsaViewModel = Instance<JBrowsePluginMsaViewStateModel>;
1063
1213
  export { type MafRegion, type MsaViewInitState } from './types';
@@ -88,6 +88,21 @@ export default function stateModelFactory() {
88
88
  * #volatile
89
89
  */
90
90
  isStoringData: false,
91
+ /**
92
+ * #volatile
93
+ * what IndexedDB was last known to hold for this view, so an edit made
94
+ * after the first write (react-msaview replacing data.tree with a
95
+ * neighbor-joining tree, say) is recognized as needing a new one
96
+ */
97
+ lastStoredData: undefined,
98
+ /**
99
+ * #volatile
100
+ * aborts the launch currently in flight -- from the Cancel button, and
101
+ * from the disposer afterCreate registers, so a closed view stops
102
+ * polling EBI instead of running to completion and writing to a node
103
+ * that is gone
104
+ */
105
+ launchController: undefined,
91
106
  /**
92
107
  * #volatile
93
108
  * guards the one-shot auto-fetch of protein domains so it doesn't refire
@@ -106,7 +121,7 @@ export default function stateModelFactory() {
106
121
  * #method
107
122
  */
108
123
  getSequenceByRowName(rowName) {
109
- return self.rows.find(r => r[0] === rowName)?.[1];
124
+ return this.getRowByName(rowName)?.[1];
110
125
  },
111
126
  }))
112
127
  .views(self => ({
@@ -241,6 +256,35 @@ export default function stateModelFactory() {
241
256
  setIsStoringData(arg) {
242
257
  self.isStoringData = arg;
243
258
  },
259
+ /**
260
+ * #action
261
+ */
262
+ setLastStoredData(arg) {
263
+ self.lastStoredData = arg;
264
+ },
265
+ /**
266
+ * #action
267
+ */
268
+ setLaunchController(arg) {
269
+ self.launchController = arg;
270
+ },
271
+ /**
272
+ * #action
273
+ * Abandon the launch in flight. The EBI job keeps running on their side —
274
+ * nothing here can recall it, and its JobLink stays valid — so this only
275
+ * stops the polling and the writes it would make. Dropping the params is
276
+ * what keeps the request from refiring; the view falls back to the import
277
+ * form, which is where react-msaview's own cancel leaves it too.
278
+ */
279
+ cancelLaunch() {
280
+ self.launchController?.abort();
281
+ self.launchController = undefined;
282
+ self.blastParams = undefined;
283
+ self.orthologParams = undefined;
284
+ self.progress = '';
285
+ self.rid = undefined;
286
+ self.error = undefined;
287
+ },
244
288
  /**
245
289
  * #action
246
290
  */
@@ -301,6 +345,12 @@ export default function stateModelFactory() {
301
345
  .actions(self => ({
302
346
  afterCreate() {
303
347
  runCleanup();
348
+ // ties the launch to the view: closing one mid-BLAST used to leave the
349
+ // poller running for the job's lifetime and then call actions on a
350
+ // destroyed node, which surfaced as an unhandled rejection
351
+ addDisposer(self, () => {
352
+ self.launchController?.abort();
353
+ });
304
354
  for (const fn of [
305
355
  loadStoredData,
306
356
  storeDataToIndexedDB,
@@ -1,9 +1,11 @@
1
- export declare function generateDataStoreId(): string;
2
- export declare function storeMsaData(id: string, data: {
1
+ /** the documents a view keeps in IndexedDB rather than in its session snapshot */
2
+ export interface MsaDataPayload {
3
3
  msa?: string;
4
4
  tree?: string;
5
5
  treeMetadata?: string;
6
- }): Promise<boolean>;
6
+ }
7
+ export declare function generateDataStoreId(): string;
8
+ export declare function storeMsaData(id: string, data: MsaDataPayload): Promise<boolean>;
7
9
  export declare function retrieveMsaData(id: string): Promise<{
8
10
  msa: string | undefined;
9
11
  tree: string | undefined;
@@ -33,14 +33,23 @@ export async function retrieveMsaData(id) {
33
33
  try {
34
34
  const db = await getDB();
35
35
  const result = await db.get(STORE_NAME, id);
36
- if (result) {
37
- return {
38
- msa: result.msa,
39
- tree: result.tree,
40
- treeMetadata: result.treeMetadata,
41
- };
36
+ if (!result) {
37
+ return undefined;
42
38
  }
43
- return undefined;
39
+ // reading counts as use, which is what makes cleanupOldData's policy
40
+ // "unused for 7 days" rather than "written 7 days ago" -- a session opened
41
+ // every day used to lose its alignment on the eighth
42
+ try {
43
+ await db.put(STORE_NAME, { ...result, timestamp: Date.now() });
44
+ }
45
+ catch (e) {
46
+ console.warn('Failed to refresh MSA data timestamp:', e);
47
+ }
48
+ return {
49
+ msa: result.msa,
50
+ tree: result.tree,
51
+ treeMetadata: result.treeMetadata,
52
+ };
44
53
  }
45
54
  catch (e) {
46
55
  console.warn('Failed to retrieve MSA data:', e);
@@ -0,0 +1 @@
1
+ export {};