@jbrowse/capture 5.0.0-beta.7 → 5.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -25
- package/esm/args.d.ts +2 -1
- package/esm/args.js +2 -1
- package/esm/bin.js +52 -29
- package/esm/browser.d.ts +0 -1
- package/esm/browser.js +5 -9
- package/esm/capture.d.ts +9 -8
- package/esm/capture.js +41 -57
- package/esm/catalog.d.ts +40 -0
- package/esm/catalog.js +103 -0
- package/esm/hub.d.ts +10 -0
- package/esm/hub.js +31 -16
- package/esm/imagePath.d.ts +1 -0
- package/esm/imagePath.js +8 -0
- package/esm/index.d.ts +8 -8
- package/esm/index.js +7 -7
- package/esm/instanceVersion.d.ts +8 -0
- package/esm/instanceVersion.js +24 -0
- package/esm/poll.d.ts +13 -0
- package/esm/poll.js +29 -0
- package/esm/ready.d.ts +20 -9
- package/esm/ready.js +37 -27
- package/esm/session.d.ts +13 -5
- package/esm/session.js +31 -12
- package/esm/sessionGate.d.ts +52 -43
- package/esm/sessionGate.js +82 -83
- package/esm/sessionOverflow.d.ts +11 -0
- package/esm/sessionOverflow.js +24 -0
- package/esm/url.d.ts +16 -6
- package/esm/url.js +34 -17
- package/esm/waits.d.ts +13 -16
- package/esm/waits.js +49 -81
- package/package.json +3 -3
package/esm/sessionGate.d.ts
CHANGED
|
@@ -9,54 +9,54 @@ export interface SessionExpectations {
|
|
|
9
9
|
* the default set alone when the id you passed does not exist at all.
|
|
10
10
|
*/
|
|
11
11
|
trackIds?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* The fewest open views that count. Default 1; 0 accepts a session that
|
|
14
|
+
* opened none, such as one loaded to show a dialog.
|
|
15
|
+
*/
|
|
16
|
+
views?: number;
|
|
12
17
|
}
|
|
13
|
-
/**
|
|
14
|
-
* The census the app publishes beside its phase: `AppReadyMarker` renders
|
|
15
|
-
* `data-app-views` (a count), and `data-app-assemblies` / `data-app-tracks`
|
|
16
|
-
* (JSON string arrays of what is open).
|
|
17
|
-
*
|
|
18
|
-
* One element answers the whole gate. Each view declares what it holds and
|
|
19
|
-
* `openViews`/`openTracks` reduce over those declarations (ADR-103), so nothing
|
|
20
|
-
* here has to know that a synteny view keeps its tracks on levels and its rows
|
|
21
|
-
* on sub-views — the duck-typed walk of `window.JBrowseSession` that used to
|
|
22
|
-
* live here, and drifted against the copy in `isPageBusyInPage`, is gone with
|
|
23
|
-
* the builds that needed it.
|
|
24
|
-
*
|
|
25
|
-
* `scripts/readinessContract.test.ts` pins the attribute names against the
|
|
26
|
-
* marker, since neither package may import the other.
|
|
27
|
-
*/
|
|
28
|
-
export declare const APP_CENSUS = "[data-app-tracks]";
|
|
29
18
|
export interface SessionSummary {
|
|
30
19
|
views: number;
|
|
31
20
|
assemblies: string[];
|
|
32
21
|
trackIds: string[];
|
|
33
22
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The census the app publishes beside its phase: `AppReadyMarker` renders
|
|
25
|
+
* `data-app-views` (a count), and `data-app-assemblies` / `data-app-tracks`
|
|
26
|
+
* (JSON string arrays of what is open). Each view declares what it holds and
|
|
27
|
+
* `openViews`/`openTracks` reduce over those declarations (ADR-103), so
|
|
28
|
+
* nothing here has to know which property a container view keeps its children
|
|
29
|
+
* on. `scripts/readinessContract.test.ts` pins the attribute names against the
|
|
30
|
+
* marker, since neither package may import the other.
|
|
31
|
+
*
|
|
32
|
+
* Serialized into the page, so it can only call what it declares.
|
|
33
|
+
*/
|
|
36
34
|
export declare function readSessionSummaryInPage(): SessionSummary | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* The message the app shows in place of itself when its config, session or
|
|
37
|
+
* plugins fail to load. Serialized into the page.
|
|
38
|
+
*/
|
|
39
|
+
export declare function readLoadFailureInPage(): string | undefined;
|
|
37
40
|
/**
|
|
38
41
|
* Wait until the census exists and the assembly and tracks that were asked for
|
|
39
42
|
* are actually open.
|
|
40
43
|
*
|
|
41
|
-
*
|
|
42
|
-
* that does not exist in the config, and an
|
|
43
|
-
* the one the config declares
|
|
44
|
-
* at all — each of them otherwise
|
|
45
|
-
* chrome, and photographs beautifully
|
|
44
|
+
* A config, session or plugin that fails to load fails here at once, with the
|
|
45
|
+
* app's own message. A trackId that does not exist in the config, and an
|
|
46
|
+
* assembly name that does not match the one the config declares, fail here on
|
|
47
|
+
* timeout, which is the only place they fail at all — each of them otherwise
|
|
48
|
+
* produces a browser that loads, paints its chrome, and photographs beautifully
|
|
49
|
+
* with nothing in it.
|
|
46
50
|
*/
|
|
47
|
-
export declare function waitForSession(page: Page, { assembly, trackIds, timeout, }?: SessionExpectations & {
|
|
51
|
+
export declare function waitForSession(page: Page, { assembly, trackIds, views, timeout, }?: SessionExpectations & {
|
|
48
52
|
timeout?: number;
|
|
49
53
|
}): Promise<void>;
|
|
50
|
-
/**
|
|
51
|
-
* Displays that were still reporting unpainted at the moment of the call.
|
|
52
|
-
*
|
|
53
|
-
* Distinct from `unsettled`, which says a wait ran out of time. This says what
|
|
54
|
-
* the page looked like when the shutter fired.
|
|
55
|
-
*/
|
|
56
|
-
export declare function pendingDisplays(page: Page): Promise<string[]>;
|
|
57
|
-
/** One unpainted display and what it says about itself. */
|
|
54
|
+
/** One display and what it says about itself. */
|
|
58
55
|
export interface PendingDisplay {
|
|
59
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* the display TYPE, shared by every instance of it, or its id where a banner
|
|
58
|
+
* has replaced the display and no type is on the page
|
|
59
|
+
*/
|
|
60
60
|
name: string;
|
|
61
61
|
/** which instance, where the display publishes `data-display-id` */
|
|
62
62
|
id?: string;
|
|
@@ -68,17 +68,19 @@ export interface PendingDisplay {
|
|
|
68
68
|
phase?: string;
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
71
|
+
* Displays still reporting unpainted, canceled, or failed to render, each with
|
|
72
|
+
* its own account of itself. The phase separates the cases a bare name runs
|
|
73
|
+
* together —
|
|
72
74
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* `
|
|
79
|
-
* `ready`
|
|
80
|
-
*
|
|
81
|
-
*
|
|
75
|
+
* `loading` still fetching. A slow page or a fetch that never returns.
|
|
76
|
+
* `error` finished, badly. Nothing is coming; the picture is a banner.
|
|
77
|
+
* `canceled` stopped by the user, and it stays stopped until Retry. It is
|
|
78
|
+
* in the census painted or not, since the picture is the
|
|
79
|
+
* Retry overlay whatever was drawn before.
|
|
80
|
+
* `renderError` its renderer failed; the picture is a Retry banner.
|
|
81
|
+
* `ready` it says it is done and reports no paint. That is the
|
|
82
|
+
* display's bug, not the wait's, and it is the one a longer
|
|
83
|
+
* timeout will never fix.
|
|
82
84
|
*
|
|
83
85
|
* Serialized into the page, so a test can call the real function. Read fresh
|
|
84
86
|
* from the DOM at report time rather than from handles the waits held. A handle
|
|
@@ -87,6 +89,13 @@ export interface PendingDisplay {
|
|
|
87
89
|
* timeouts into nine opaque puppeteer errors (reverted in 28c6ee6d90).
|
|
88
90
|
*/
|
|
89
91
|
export declare function pendingDisplayStatesInPage(): PendingDisplay[];
|
|
92
|
+
/**
|
|
93
|
+
* Displays showing the "too much data" banner in place of their features: the
|
|
94
|
+
* region is wider than the track fetches without being asked. Not a failure,
|
|
95
|
+
* since that is the app working as designed, but not the picture either.
|
|
96
|
+
* Serialized into the page.
|
|
97
|
+
*/
|
|
98
|
+
export declare function tooLargeDisplaysInPage(): PendingDisplay[];
|
|
90
99
|
export declare function pendingDisplayStates(page: Page): Promise<PendingDisplay[]>;
|
|
91
100
|
/** `pendingDisplayStates` as one line for an error message. */
|
|
92
101
|
export declare function describePendingDisplays(pending: PendingDisplay[]): string;
|
package/esm/sessionGate.js
CHANGED
|
@@ -1,27 +1,15 @@
|
|
|
1
|
+
import { holdTrue } from './poll.js';
|
|
1
2
|
/**
|
|
2
3
|
* The census the app publishes beside its phase: `AppReadyMarker` renders
|
|
3
4
|
* `data-app-views` (a count), and `data-app-assemblies` / `data-app-tracks`
|
|
4
|
-
* (JSON string arrays of what is open).
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
* here has to know that a synteny view keeps its tracks on levels and its rows
|
|
9
|
-
* on sub-views — the duck-typed walk of `window.JBrowseSession` that used to
|
|
10
|
-
* live here, and drifted against the copy in `isPageBusyInPage`, is gone with
|
|
11
|
-
* the builds that needed it.
|
|
12
|
-
*
|
|
13
|
-
* `scripts/readinessContract.test.ts` pins the attribute names against the
|
|
5
|
+
* (JSON string arrays of what is open). Each view declares what it holds and
|
|
6
|
+
* `openViews`/`openTracks` reduce over those declarations (ADR-103), so
|
|
7
|
+
* nothing here has to know which property a container view keeps its children
|
|
8
|
+
* on. `scripts/readinessContract.test.ts` pins the attribute names against the
|
|
14
9
|
* marker, since neither package may import the other.
|
|
10
|
+
*
|
|
11
|
+
* Serialized into the page, so it can only call what it declares.
|
|
15
12
|
*/
|
|
16
|
-
export const APP_CENSUS = '[data-app-tracks]';
|
|
17
|
-
/** What the page currently has open, or undefined if there is no census yet. */
|
|
18
|
-
export function readSessionSummary(page) {
|
|
19
|
-
return page.evaluate(readSessionSummaryInPage);
|
|
20
|
-
}
|
|
21
|
-
// Serialized into the page, so it can only call what it declares. Exported for
|
|
22
|
-
// its test: it reads the document and nothing else, so calling it in node
|
|
23
|
-
// against a staged DOM exercises the very function puppeteer serializes, rather
|
|
24
|
-
// than a copy of it that can drift.
|
|
25
13
|
export function readSessionSummaryInPage() {
|
|
26
14
|
const marker = document.querySelector('[data-app-tracks]');
|
|
27
15
|
if (!marker) {
|
|
@@ -38,50 +26,53 @@ export function readSessionSummaryInPage() {
|
|
|
38
26
|
return undefined;
|
|
39
27
|
}
|
|
40
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* The message the app shows in place of itself when its config, session or
|
|
31
|
+
* plugins fail to load. Serialized into the page.
|
|
32
|
+
*/
|
|
33
|
+
export function readLoadFailureInPage() {
|
|
34
|
+
return document.querySelector('[data-app-error]')?.dataset
|
|
35
|
+
.appError;
|
|
36
|
+
}
|
|
41
37
|
/**
|
|
42
38
|
* Wait until the census exists and the assembly and tracks that were asked for
|
|
43
39
|
* are actually open.
|
|
44
40
|
*
|
|
45
|
-
*
|
|
46
|
-
* that does not exist in the config, and an
|
|
47
|
-
* the one the config declares
|
|
48
|
-
* at all — each of them otherwise
|
|
49
|
-
* chrome, and photographs beautifully
|
|
41
|
+
* A config, session or plugin that fails to load fails here at once, with the
|
|
42
|
+
* app's own message. A trackId that does not exist in the config, and an
|
|
43
|
+
* assembly name that does not match the one the config declares, fail here on
|
|
44
|
+
* timeout, which is the only place they fail at all — each of them otherwise
|
|
45
|
+
* produces a browser that loads, paints its chrome, and photographs beautifully
|
|
46
|
+
* with nothing in it.
|
|
50
47
|
*/
|
|
51
|
-
export async function waitForSession(page, { assembly, trackIds = [], timeout = 60000, } = {}) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
? `${summary.views} view(s), assemblies [${summary.assemblies.join(', ')}], tracks [${summary.trackIds.join(', ')}]`
|
|
80
|
-
: 'no census on the page at all — either this is not a JBrowse app, or ' +
|
|
81
|
-
'it predates the readiness marker, in which case point --instance at a ' +
|
|
82
|
-
'build that publishes one';
|
|
83
|
-
const missing = summary
|
|
84
|
-
? trackIds.filter(id => !summary.trackIds.includes(id))
|
|
48
|
+
export async function waitForSession(page, { assembly, trackIds = [], views = 1, timeout = 60000, } = {}) {
|
|
49
|
+
// a page that navigates or closes under us fails the evaluate, which reads as
|
|
50
|
+
// nothing there yet and leaves the deadline to decide
|
|
51
|
+
let summary;
|
|
52
|
+
const reached = await holdTrue(async () => {
|
|
53
|
+
const failure = await page
|
|
54
|
+
.evaluate(readLoadFailureInPage)
|
|
55
|
+
.catch(() => undefined);
|
|
56
|
+
if (failure) {
|
|
57
|
+
throw new Error(`JBrowse could not load: ${failure}`);
|
|
58
|
+
}
|
|
59
|
+
const now = await page
|
|
60
|
+
.evaluate(readSessionSummaryInPage)
|
|
61
|
+
.catch(() => undefined);
|
|
62
|
+
summary = now;
|
|
63
|
+
return (!!now &&
|
|
64
|
+
now.views >= views &&
|
|
65
|
+
(assembly === undefined || now.assemblies.includes(assembly)) &&
|
|
66
|
+
trackIds.every(id => now.trackIds.includes(id)));
|
|
67
|
+
}, { holdMs: 0, timeout, pollMs: 250 });
|
|
68
|
+
if (!reached) {
|
|
69
|
+
const last = summary;
|
|
70
|
+
const found = last
|
|
71
|
+
? `${last.views} view(s), assemblies [${last.assemblies.join(', ')}], tracks [${last.trackIds.join(', ')}]`
|
|
72
|
+
: 'no census on the page at all, so either this is not a JBrowse app or ' +
|
|
73
|
+
'it is older than v5, the first release to publish one';
|
|
74
|
+
const missing = last
|
|
75
|
+
? trackIds.filter(id => !last.trackIds.includes(id))
|
|
85
76
|
: trackIds;
|
|
86
77
|
const wanted = [
|
|
87
78
|
assembly ? `assembly "${assembly}"` : undefined,
|
|
@@ -90,32 +81,25 @@ export async function waitForSession(page, { assembly, trackIds = [], timeout =
|
|
|
90
81
|
.filter(Boolean)
|
|
91
82
|
.join(' and ');
|
|
92
83
|
throw new Error(`the session never reached the requested state after ${timeout}ms. ` +
|
|
93
|
-
`Wanted ${wanted || 'an open view'}; found ${found}. ` +
|
|
94
|
-
'A
|
|
95
|
-
'
|
|
84
|
+
`Wanted ${wanted || (views > 0 ? 'an open view' : 'a session')}; found ${found}. ` +
|
|
85
|
+
'A trackId the config does not define, or an assembly name that does ' +
|
|
86
|
+
'not match the config, looks like this.');
|
|
96
87
|
}
|
|
97
88
|
}
|
|
98
89
|
/**
|
|
99
|
-
* Displays
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* the page looked like when the shutter fired.
|
|
103
|
-
*/
|
|
104
|
-
export async function pendingDisplays(page) {
|
|
105
|
-
return (await pendingDisplayStates(page)).map(d => d.name);
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* The same census, with each display's own account of itself.
|
|
90
|
+
* Displays still reporting unpainted, canceled, or failed to render, each with
|
|
91
|
+
* its own account of itself. The phase separates the cases a bare name runs
|
|
92
|
+
* together —
|
|
109
93
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* `
|
|
116
|
-
* `ready`
|
|
117
|
-
*
|
|
118
|
-
*
|
|
94
|
+
* `loading` still fetching. A slow page or a fetch that never returns.
|
|
95
|
+
* `error` finished, badly. Nothing is coming; the picture is a banner.
|
|
96
|
+
* `canceled` stopped by the user, and it stays stopped until Retry. It is
|
|
97
|
+
* in the census painted or not, since the picture is the
|
|
98
|
+
* Retry overlay whatever was drawn before.
|
|
99
|
+
* `renderError` its renderer failed; the picture is a Retry banner.
|
|
100
|
+
* `ready` it says it is done and reports no paint. That is the
|
|
101
|
+
* display's bug, not the wait's, and it is the one a longer
|
|
102
|
+
* timeout will never fix.
|
|
119
103
|
*
|
|
120
104
|
* Serialized into the page, so a test can call the real function. Read fresh
|
|
121
105
|
* from the DOM at report time rather than from handles the waits held. A handle
|
|
@@ -125,19 +109,34 @@ export async function pendingDisplays(page) {
|
|
|
125
109
|
*/
|
|
126
110
|
export function pendingDisplayStatesInPage() {
|
|
127
111
|
return [
|
|
128
|
-
...document.querySelectorAll('[data-display-drawn="false"]'),
|
|
112
|
+
...document.querySelectorAll('[data-display-drawn="false"], [data-display-phase="canceled"], [data-display-phase="renderError"]'),
|
|
129
113
|
].map(el => ({
|
|
130
|
-
name: el.dataset.testid ??
|
|
114
|
+
name: el.dataset.testid ?? el.dataset.displayId ?? 'unnamed display',
|
|
131
115
|
id: el.dataset.displayId,
|
|
132
116
|
phase: el.dataset.displayPhase,
|
|
133
117
|
}));
|
|
134
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Displays showing the "too much data" banner in place of their features: the
|
|
121
|
+
* region is wider than the track fetches without being asked. Not a failure,
|
|
122
|
+
* since that is the app working as designed, but not the picture either.
|
|
123
|
+
* Serialized into the page.
|
|
124
|
+
*/
|
|
125
|
+
export function tooLargeDisplaysInPage() {
|
|
126
|
+
return [
|
|
127
|
+
...document.querySelectorAll('[data-display-phase="tooLarge"]'),
|
|
128
|
+
].map(el => ({
|
|
129
|
+
name: el.dataset.displayId ?? 'unnamed display',
|
|
130
|
+
id: el.dataset.displayId,
|
|
131
|
+
phase: 'tooLarge',
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
135
134
|
export function pendingDisplayStates(page) {
|
|
136
135
|
return page.evaluate(pendingDisplayStatesInPage);
|
|
137
136
|
}
|
|
138
137
|
/** `pendingDisplayStates` as one line for an error message. */
|
|
139
138
|
export function describePendingDisplays(pending) {
|
|
140
139
|
return pending
|
|
141
|
-
.map(d => `${d.name}${d.id ? ` (${d.id})` : ''} is ${d.phase ?? 'in an unpublished phase'}`)
|
|
140
|
+
.map(d => `${d.name}${d.id && d.id !== d.name ? ` (${d.id})` : ''} is ${d.phase ?? 'in an unpublished phase'}`)
|
|
142
141
|
.join('; ');
|
|
143
142
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many CSS pixels of the session lie below the window.
|
|
3
|
+
*
|
|
4
|
+
* JBrowse scrolls a column inside the app rather than the document, so the
|
|
5
|
+
* document is always exactly the window's height. The overflow is that column's,
|
|
6
|
+
* found as `overflow` in jbApi finds it, less the trailing room `ViewStack`
|
|
7
|
+
* leaves for scrolling the last view up, which is space rather than content.
|
|
8
|
+
*
|
|
9
|
+
* Serialized into the page, so it declares everything it uses.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sessionOverflowInPage(): number;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many CSS pixels of the session lie below the window.
|
|
3
|
+
*
|
|
4
|
+
* JBrowse scrolls a column inside the app rather than the document, so the
|
|
5
|
+
* document is always exactly the window's height. The overflow is that column's,
|
|
6
|
+
* found as `overflow` in jbApi finds it, less the trailing room `ViewStack`
|
|
7
|
+
* leaves for scrolling the last view up, which is space rather than content.
|
|
8
|
+
*
|
|
9
|
+
* Serialized into the page, so it declares everything it uses.
|
|
10
|
+
*/
|
|
11
|
+
export function sessionOverflowInPage() {
|
|
12
|
+
const container = document.querySelector('[data-testid^="view-container-"]');
|
|
13
|
+
for (let el = container?.parentElement; el; el = el.parentElement) {
|
|
14
|
+
const { overflowY } = getComputedStyle(el);
|
|
15
|
+
if ((overflowY === 'auto' || overflowY === 'scroll') &&
|
|
16
|
+
el.scrollHeight > el.clientHeight) {
|
|
17
|
+
const room = el
|
|
18
|
+
.querySelector('[data-testid="view-stack-overscroll"]')
|
|
19
|
+
?.getBoundingClientRect().height ?? 0;
|
|
20
|
+
return Math.max(0, Math.ceil(el.scrollHeight - room - el.clientHeight));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
|
|
24
|
+
}
|
package/esm/url.d.ts
CHANGED
|
@@ -14,13 +14,23 @@ export interface JBrowseUrlOptions {
|
|
|
14
14
|
loc?: string;
|
|
15
15
|
/** trackIds from the config to open. */
|
|
16
16
|
tracks?: string[];
|
|
17
|
-
/**
|
|
17
|
+
/** A session spec, for anything the three fields above cannot say. */
|
|
18
|
+
spec?: object;
|
|
19
|
+
/**
|
|
20
|
+
* A session saved with "Export session...": the `{ session: {...} }`
|
|
21
|
+
* document, or the snapshot inside it.
|
|
22
|
+
*/
|
|
18
23
|
session?: object;
|
|
19
24
|
/** Name the session carries once opened. */
|
|
20
25
|
sessionName?: string;
|
|
21
26
|
/** JBrowse Web deployment to load. Defaults to the public one. */
|
|
22
27
|
instance?: string;
|
|
23
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* A spec or a saved session says which assembly, locations and tracks to open,
|
|
31
|
+
* and the URL can carry one of the three ways of saying so, never two.
|
|
32
|
+
*/
|
|
33
|
+
export declare function assertSessionStandsAlone({ spec, session, assembly, loc, tracks, }: JBrowseUrlOptions): void;
|
|
24
34
|
/**
|
|
25
35
|
* The URL that opens a JBrowse Web instance onto a described view. Useful on its
|
|
26
36
|
* own: an agent can hand this to a human, or paste it into a report, without
|
|
@@ -29,11 +39,11 @@ export interface JBrowseUrlOptions {
|
|
|
29
39
|
* Two ways to say where to go, and they are not interchangeable. `assembly` /
|
|
30
40
|
* `loc` / `tracks` become the [URL parameters](https://jbrowse.org/jb2/docs/urlparams/),
|
|
31
41
|
* which route `loc` through the config's text-search index, so a **gene name**
|
|
32
|
-
* works there. `
|
|
42
|
+
* works there. `spec` becomes a
|
|
33
43
|
* [session spec](https://jbrowse.org/jb2/docs/urlparams/#session-spec), which
|
|
34
44
|
* can describe several views and per-display settings but whose `init.loc` is
|
|
35
|
-
* parsed as a locstring only and throws on a gene name
|
|
36
|
-
*
|
|
37
|
-
*
|
|
45
|
+
* parsed as a locstring only and throws on a gene name, and `session` opens a
|
|
46
|
+
* saved session as it was exported. Pass one of the three: `&loc=` starts a
|
|
47
|
+
* fresh session, so the URL cannot carry two.
|
|
38
48
|
*/
|
|
39
|
-
export declare function jbrowseUrl({ hub, config, assembly, loc, tracks, session, sessionName, instance, }: JBrowseUrlOptions): string;
|
|
49
|
+
export declare function jbrowseUrl({ hub, config, assembly, loc, tracks, spec, session, sessionName, instance, }: JBrowseUrlOptions): string;
|
package/esm/url.js
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import { hubUrl } from './hub.js';
|
|
2
|
-
import { sessionSpecParam } from './session.js';
|
|
2
|
+
import { savedSessionParam, sessionSpecParam } from './session.js';
|
|
3
3
|
// The public JBrowse Web build the docs' own figure links point at. Any other
|
|
4
4
|
// deployment works the same way, including a local `npx serve` of a build.
|
|
5
5
|
export const PUBLIC_INSTANCE = 'https://jbrowse.org/code/jb2/latest/';
|
|
6
|
+
/**
|
|
7
|
+
* A spec or a saved session says which assembly, locations and tracks to open,
|
|
8
|
+
* and the URL can carry one of the three ways of saying so, never two.
|
|
9
|
+
*/
|
|
10
|
+
export function assertSessionStandsAlone({ spec, session, assembly, loc, tracks, }) {
|
|
11
|
+
if (spec && session) {
|
|
12
|
+
throw new Error('pass a session spec or a saved session, not both');
|
|
13
|
+
}
|
|
14
|
+
if ((spec || session) && (assembly || loc || tracks?.length)) {
|
|
15
|
+
throw new Error('a session spec or saved session says which assembly, locations and ' +
|
|
16
|
+
'tracks to open, so it cannot be combined with assembly, loc or tracks ' +
|
|
17
|
+
'(--assembly, --loc, --track): put them in the session');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
6
20
|
/**
|
|
7
21
|
* The URL that opens a JBrowse Web instance onto a described view. Useful on its
|
|
8
22
|
* own: an agent can hand this to a human, or paste it into a report, without
|
|
@@ -11,34 +25,37 @@ export const PUBLIC_INSTANCE = 'https://jbrowse.org/code/jb2/latest/';
|
|
|
11
25
|
* Two ways to say where to go, and they are not interchangeable. `assembly` /
|
|
12
26
|
* `loc` / `tracks` become the [URL parameters](https://jbrowse.org/jb2/docs/urlparams/),
|
|
13
27
|
* which route `loc` through the config's text-search index, so a **gene name**
|
|
14
|
-
* works there. `
|
|
28
|
+
* works there. `spec` becomes a
|
|
15
29
|
* [session spec](https://jbrowse.org/jb2/docs/urlparams/#session-spec), which
|
|
16
30
|
* can describe several views and per-display settings but whose `init.loc` is
|
|
17
|
-
* parsed as a locstring only and throws on a gene name
|
|
18
|
-
*
|
|
19
|
-
*
|
|
31
|
+
* parsed as a locstring only and throws on a gene name, and `session` opens a
|
|
32
|
+
* saved session as it was exported. Pass one of the three: `&loc=` starts a
|
|
33
|
+
* fresh session, so the URL cannot carry two.
|
|
20
34
|
*/
|
|
21
|
-
export function jbrowseUrl({ hub, config, assembly
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
// either raw produces a link that loads a truncated config and reports
|
|
27
|
-
// nothing.
|
|
35
|
+
export function jbrowseUrl({ hub, config, assembly, loc, tracks, spec, session, sessionName, instance = PUBLIC_INSTANCE, }) {
|
|
36
|
+
assertSessionStandsAlone({ spec, session, assembly, loc, tracks });
|
|
37
|
+
// In the fragment, which never reaches a server: a whole session spec in the
|
|
38
|
+
// query string can exceed the request-line limit and come back HTTP 414.
|
|
39
|
+
const params = new URLSearchParams();
|
|
28
40
|
const set = (key, value) => {
|
|
29
41
|
if (value) {
|
|
30
|
-
|
|
42
|
+
params.set(key, value);
|
|
31
43
|
}
|
|
32
44
|
};
|
|
33
|
-
set('config',
|
|
34
|
-
if (
|
|
35
|
-
set('session', sessionSpecParam(
|
|
45
|
+
set('config', config ?? (hub ? hubUrl(hub) : undefined));
|
|
46
|
+
if (spec) {
|
|
47
|
+
set('session', sessionSpecParam(spec));
|
|
48
|
+
}
|
|
49
|
+
else if (session) {
|
|
50
|
+
set('session', savedSessionParam(session));
|
|
36
51
|
}
|
|
37
52
|
else {
|
|
38
|
-
set('assembly', assembly);
|
|
53
|
+
set('assembly', assembly ?? hub);
|
|
39
54
|
set('loc', loc);
|
|
40
55
|
set('tracks', tracks?.join(','));
|
|
41
56
|
}
|
|
42
57
|
set('sessionName', sessionName);
|
|
58
|
+
const url = new URL(instance);
|
|
59
|
+
url.hash = params.toString();
|
|
43
60
|
return url.href;
|
|
44
61
|
}
|
package/esm/waits.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ElementHandle, Page } from 'puppeteer';
|
|
2
|
-
export declare const delay: (ms: number) => Promise<void>;
|
|
3
2
|
export declare const LOADING_OVERLAY = "[data-testid=\"loading-overlay\"]";
|
|
4
3
|
/**
|
|
5
4
|
* The whole readiness contract, in one selector.
|
|
@@ -17,6 +16,14 @@ export declare const LOADING_OVERLAY = "[data-testid=\"loading-overlay\"]";
|
|
|
17
16
|
* capture taken in it is a picture of an empty browser.
|
|
18
17
|
*/
|
|
19
18
|
export declare const APP_READY = "[data-app-phase=\"ready\"]";
|
|
19
|
+
/**
|
|
20
|
+
* A display drawing a transition between two settled pictures: canvas's row
|
|
21
|
+
* morph, a multi-way lane re-aligning. The app is finished working and every
|
|
22
|
+
* display reads `ready`, so neither the marker nor the phase can say so; the
|
|
23
|
+
* display publishes it itself, and a frame taken while it reads `true` is a
|
|
24
|
+
* picture no settled state ever shows.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ANIMATING_DISPLAYS = "[data-display-animating=\"true\"]";
|
|
20
27
|
/**
|
|
21
28
|
* Wait until the loading overlay is gone, and with `waitForDownloads` until
|
|
22
29
|
* nothing else reports itself busy either — a remote fetch can outlive the
|
|
@@ -72,11 +79,8 @@ export declare const PENDING_DISPLAYS = "[data-display-drawn=\"false\"]";
|
|
|
72
79
|
* finished badly and is showing a banner, and `ready` is a display claiming it
|
|
73
80
|
* finished without drawing — a bug in the display rather than in the wait.
|
|
74
81
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* publish neither attribute and appear here as nothing at all: a wait on such a
|
|
78
|
-
* display can never resolve and can never be reported. Nothing pending means
|
|
79
|
-
* either that, or a selector that was never a display's.
|
|
82
|
+
* A display showing "too much data" is not pending, and a wait on its body
|
|
83
|
+
* can never resolve, so the census names those too.
|
|
80
84
|
*/
|
|
81
85
|
export declare function waitForSelectorAttributed(page: Page, selector: string, timeoutMs: number): Promise<ElementHandle>;
|
|
82
86
|
/**
|
|
@@ -91,8 +95,6 @@ export declare const displayPainted: (testid: string) => string;
|
|
|
91
95
|
* on the phase instead.
|
|
92
96
|
*/
|
|
93
97
|
export declare const displaySettled: (testid: string) => string;
|
|
94
|
-
/** One display, by its config's `displayId` rather than by type. */
|
|
95
|
-
export declare const displayById: (displayId: string) => string;
|
|
96
98
|
export declare function waitForDisplaysDone(page: Page, timeoutMs: number): Promise<boolean>;
|
|
97
99
|
export declare function waitForDisplayPhases(page: Page, timeoutMs: number): Promise<boolean>;
|
|
98
100
|
export declare function waitForViewPhases(page: Page, timeoutMs: number): Promise<import("puppeteer").JSHandle<false> | import("puppeteer").JSHandle<true>>;
|
|
@@ -109,6 +111,7 @@ export declare function waitForViewPhases(page: Page, timeoutMs: number): Promis
|
|
|
109
111
|
* wherever it tells a user it is working
|
|
110
112
|
* `data-display-phase` one display's own fetch, newer builds
|
|
111
113
|
* `data-view-phase` a view still resolving its assembly, newer builds
|
|
114
|
+
* `data-display-animating` a display drawing a morph, newer builds
|
|
112
115
|
*/
|
|
113
116
|
export declare const BUSY_SELECTOR: string;
|
|
114
117
|
/**
|
|
@@ -141,9 +144,6 @@ export declare function isPageBusyInPage(busySelector: string): boolean;
|
|
|
141
144
|
* "nothing is in flight right now", which is what a probe or a benchmark wants
|
|
142
145
|
* between two measured actions.
|
|
143
146
|
*
|
|
144
|
-
* Polled from node rather than in-page: chrome throttles in-page timers and rAF
|
|
145
|
-
* once the tab is not visible, which is the state a headless capture sits in.
|
|
146
|
-
*
|
|
147
147
|
* Returns false on timeout rather than throwing, like its neighbours.
|
|
148
148
|
*/
|
|
149
149
|
export declare function waitForQuietPeriod(page: Page, { quietMs, timeout, pollMs, }?: {
|
|
@@ -165,8 +165,6 @@ export declare function waitForQuietPeriod(page: Page, { quietMs, timeout, pollM
|
|
|
165
165
|
export declare function waitForAppReady(page: Page, { timeout }?: {
|
|
166
166
|
timeout?: number;
|
|
167
167
|
}): Promise<boolean>;
|
|
168
|
-
/** Whether this build publishes the app-level readiness marker at all. */
|
|
169
|
-
export declare function hasAppReadyMarker(page: Page): Promise<boolean>;
|
|
170
168
|
/**
|
|
171
169
|
* Wait out work an INTERACTION started: `ready`, and still ready a beat later.
|
|
172
170
|
*
|
|
@@ -191,9 +189,8 @@ export declare function hasAppReadyMarker(page: Page): Promise<boolean>;
|
|
|
191
189
|
* replaced. Requiring the idle to HOLD costs the hold and no more, and catches
|
|
192
190
|
* the same late-starting work.
|
|
193
191
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* is not visible, which is exactly the state a headless capture sits in.
|
|
192
|
+
* The hold also requires no view body to be waiting on its lazy component and
|
|
193
|
+
* no display to be animating, neither of which the marker can see.
|
|
197
194
|
*
|
|
198
195
|
* Throws on a build too old for the marker rather than falling back — see the
|
|
199
196
|
* body.
|