@design.estate/wcctools 6.1.0 → 7.0.0
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/changelog.md +41 -0
- package/dist_shell/bundle.js +405 -368
- package/dist_shell/bundle.js.map +1 -1
- package/dist_shell/bundle.js.third-party-notices.json +52 -0
- package/dist_shell/bundle.js.third-party-notices.txt +107 -1
- package/dist_shell/index.html +1 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/capture/classes.capturebrowser.d.ts +65 -0
- package/dist_ts/capture/classes.capturebrowser.js +251 -0
- package/dist_ts/capture/classes.captureservice.d.ts +39 -0
- package/dist_ts/capture/classes.captureservice.js +119 -0
- package/dist_ts/capture/classes.screencast.d.ts +47 -0
- package/dist_ts/capture/classes.screencast.js +127 -0
- package/dist_ts/capture/errors.d.ts +14 -0
- package/dist_ts/capture/errors.js +23 -0
- package/dist_ts/capture/images.d.ts +3 -0
- package/dist_ts/capture/images.js +58 -0
- package/dist_ts/capture/index.d.ts +4 -0
- package/dist_ts/capture/index.js +5 -0
- package/dist_ts/capture/interaction.d.ts +11 -0
- package/dist_ts/capture/interaction.js +138 -0
- package/dist_ts/capture/navigationguard.d.ts +44 -0
- package/dist_ts/capture/navigationguard.js +129 -0
- package/dist_ts/capture/previewpage.d.ts +29 -0
- package/dist_ts/capture/previewpage.js +96 -0
- package/dist_ts/capture/request.d.ts +69 -0
- package/dist_ts/capture/request.js +239 -0
- package/dist_ts/classes.bundlestatus.d.ts +36 -0
- package/dist_ts/classes.bundlestatus.js +110 -0
- package/dist_ts/classes.devapi.d.ts +3 -0
- package/dist_ts/classes.devapi.js +8 -1
- package/dist_ts/classes.devserver.d.ts +65 -14
- package/dist_ts/classes.devserver.js +157 -52
- package/dist_ts/classes.hostpolicy.d.ts +63 -0
- package/dist_ts/classes.hostpolicy.js +171 -0
- package/dist_ts/cli.js +28 -3
- package/dist_ts/cli.screenshot.d.ts +6 -0
- package/dist_ts/cli.screenshot.js +89 -0
- package/dist_ts/index.d.ts +0 -2
- package/dist_ts/index.js +1 -3
- package/dist_ts/plugins.d.ts +6 -2
- package/dist_ts/plugins.js +8 -3
- package/dist_ts_interfaces/capture.d.ts +151 -0
- package/dist_ts_interfaces/capture.js +2 -0
- package/dist_ts_interfaces/index.d.ts +1 -0
- package/dist_ts_interfaces/index.js +2 -1
- package/dist_ts_interfaces/requests.d.ts +59 -0
- package/dist_ts_shell/bundlestatus.d.ts +15 -0
- package/dist_ts_shell/bundlestatus.js +64 -0
- package/dist_ts_shell/elements/wcc-contextmenu.d.ts +3 -0
- package/dist_ts_shell/elements/wcc-contextmenu.js +18 -3
- package/dist_ts_shell/elements/wcc-preview-frame.d.ts +6 -0
- package/dist_ts_shell/elements/wcc-preview-frame.js +50 -2
- package/dist_ts_shell/elements/wcc-recording-panel.d.ts +9 -0
- package/dist_ts_shell/elements/wcc-recording-panel.js +12 -1
- package/dist_ts_shell/elements/wcc-shell.d.ts +15 -0
- package/dist_ts_shell/elements/wcc-shell.js +45 -3
- package/dist_ts_shell/plugins.d.ts +2 -1
- package/dist_ts_shell/plugins.js +3 -2
- package/dist_ts_shell/services/framesampler.service.d.ts +23 -0
- package/dist_ts_shell/services/framesampler.service.js +101 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/package.json +6 -2
- package/readme.md +108 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/capture/classes.capturebrowser.ts +290 -0
- package/ts/capture/classes.captureservice.ts +152 -0
- package/ts/capture/classes.screencast.ts +152 -0
- package/ts/capture/errors.ts +25 -0
- package/ts/capture/images.ts +61 -0
- package/ts/capture/index.ts +4 -0
- package/ts/capture/interaction.ts +146 -0
- package/ts/capture/navigationguard.ts +147 -0
- package/ts/capture/previewpage.ts +130 -0
- package/ts/capture/request.ts +304 -0
- package/ts/classes.bundlestatus.ts +123 -0
- package/ts/classes.devapi.ts +9 -0
- package/ts/classes.devserver.ts +172 -50
- package/ts/classes.hostpolicy.ts +201 -0
- package/ts/cli.screenshot.ts +95 -0
- package/ts/cli.ts +28 -2
- package/ts/index.ts +0 -2
- package/ts/plugins.ts +9 -2
- package/ts_interfaces/bridge.ts +95 -0
- package/ts_interfaces/capture.ts +144 -0
- package/ts_interfaces/catalog.ts +37 -0
- package/ts_interfaces/index.ts +5 -0
- package/ts_interfaces/plugins.ts +3 -0
- package/ts_interfaces/requests.ts +104 -0
- package/ts_interfaces/standard.ts +103 -0
- package/ts_shared/index.ts +1 -0
- package/ts_shared/plugins.ts +3 -0
- package/ts_shared/previewroute.ts +58 -0
- package/ts_shell/bundlestatus.ts +68 -0
- package/ts_shell/elements/wcc-contextmenu.ts +306 -0
- package/ts_shell/elements/wcc-preview-frame.ts +167 -0
- package/ts_shell/elements/wcc-properties.ts +998 -0
- package/ts_shell/elements/wcc-record-button.ts +108 -0
- package/ts_shell/elements/wcc-recording-panel.ts +1017 -0
- package/ts_shell/elements/wcc-shell.ts +485 -0
- package/ts_shell/elements/wcc-sidebar.ts +1419 -0
- package/ts_shell/index.html +21 -0
- package/ts_shell/index.ts +4 -0
- package/ts_shell/plugins.ts +12 -0
- package/ts_shell/previewconnection.ts +103 -0
- package/ts_shell/services/framesampler.service.ts +112 -0
- package/ts_shell/services/recorder.service.ts +451 -0
- package/ts_shell/types/dom-mediacapture-stub/index.d.ts +12 -0
- package/ts_shell/types/dom-mediacapture-stub/package.json +6 -0
- package/ts_shell/types/dom-webcodecs-stub/index.d.ts +2 -0
- package/ts_shell/types/dom-webcodecs-stub/package.json +6 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { WccCaptureError, getErrorMessage } from './errors.js';
|
|
3
|
+
/** The message of a navigation the capture blocked. */
|
|
4
|
+
export const formatBlockedNavigation = (urlArg) => {
|
|
5
|
+
return `navigation to ${urlArg} blocked: a capture stays on the rendered preview`;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Runs in the main frame before the preview's scripts, so its `navigate` listener is the first:
|
|
9
|
+
* cancels each navigation that would replace the document (a link, a form submission, a script
|
|
10
|
+
* assigning `location`, a reload, also to the same URL, to `about:blank` or a `blob:` URL) as it
|
|
11
|
+
* starts, and records its URL. Same-document navigations (`history.replaceState`, fragments) and
|
|
12
|
+
* downloads pass. Link clicks and scripts start their navigation while the step that caused it
|
|
13
|
+
* runs; form submissions in a task right after.
|
|
14
|
+
*/
|
|
15
|
+
const cancelLeavingNavigations = () => {
|
|
16
|
+
if (window !== window.top) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const cancelled = [];
|
|
20
|
+
window.wccCancelledNavigations = cancelled;
|
|
21
|
+
navigation.addEventListener('navigate', (eventArg) => {
|
|
22
|
+
if (eventArg.destination.sameDocument || eventArg.downloadRequest !== null || !eventArg.cancelable) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
eventArg.preventDefault();
|
|
26
|
+
cancelled.push(eventArg.destination.url);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Keeps a capture page on the preview. Its main frame loads the preview document once; after that,
|
|
31
|
+
* the page script above cancels every navigation that would replace the document before it starts.
|
|
32
|
+
* Request interception backs it up: a main-frame navigation request the page could not cancel is
|
|
33
|
+
* aborted before it leaves. The preview's history holds the preview alone, so there is no earlier
|
|
34
|
+
* document to go back to. Subframes and all other requests are left alone.
|
|
35
|
+
*
|
|
36
|
+
* `takeBlockedNavigation()` answers the navigation blocked since its last call, so each step of an
|
|
37
|
+
* interaction learns about the navigation it started.
|
|
38
|
+
*/
|
|
39
|
+
export class WccNavigationGuard {
|
|
40
|
+
page;
|
|
41
|
+
/** Navigation requests aborted by the backstop since the last `takeBlockedNavigation()`. */
|
|
42
|
+
abortedUrls = [];
|
|
43
|
+
interceptionFailure = null;
|
|
44
|
+
previewUrl;
|
|
45
|
+
constructor(page, previewUrlArg) {
|
|
46
|
+
this.page = page;
|
|
47
|
+
this.previewUrl = new URL(previewUrlArg).href;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Installs the page script and intercepts the page's requests; the page's first main-frame
|
|
51
|
+
* navigation may load `previewUrlArg` only. Install before loading the preview.
|
|
52
|
+
*/
|
|
53
|
+
static async install(pageArg, previewUrlArg) {
|
|
54
|
+
const guard = new WccNavigationGuard(pageArg, previewUrlArg);
|
|
55
|
+
pageArg.on('request', (requestArg) => guard.decide(requestArg));
|
|
56
|
+
await pageArg.setRequestInterception(true);
|
|
57
|
+
await pageArg.evaluateOnNewDocument(cancelLeavingNavigations);
|
|
58
|
+
return guard;
|
|
59
|
+
}
|
|
60
|
+
/** Drops the history entries before the loaded preview (the blank page it opened as), so going back leaves nothing. */
|
|
61
|
+
async forgetEarlierHistory() {
|
|
62
|
+
const session = await this.page.createCDPSession();
|
|
63
|
+
try {
|
|
64
|
+
await session.send('Page.resetNavigationHistory');
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await session.detach();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Lets the page run the tasks queued so far (an animation frame, then a task), then answers the
|
|
72
|
+
* URL of the first navigation blocked since the last call, by the page script or the backstop,
|
|
73
|
+
* or null when none was.
|
|
74
|
+
*/
|
|
75
|
+
async takeBlockedNavigation() {
|
|
76
|
+
const cancelledUrl = await this.page.evaluate(() => {
|
|
77
|
+
return new Promise((resolve) => {
|
|
78
|
+
requestAnimationFrame(() => {
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
const cancelled = window.wccCancelledNavigations ?? [];
|
|
81
|
+
resolve(cancelled[0] ?? null);
|
|
82
|
+
cancelled.length = 0;
|
|
83
|
+
}, 0);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
if (this.interceptionFailure !== null) {
|
|
88
|
+
throw new WccCaptureError(`wcctools: the capture failed to handle a request of the preview: ${getErrorMessage(this.interceptionFailure)}`);
|
|
89
|
+
}
|
|
90
|
+
const abortedUrl = this.abortedUrls[0] ?? null;
|
|
91
|
+
this.abortedUrls = [];
|
|
92
|
+
return cancelledUrl ?? abortedUrl;
|
|
93
|
+
}
|
|
94
|
+
/** Throws when the page tried to leave the preview while `whileArg` (e.g. "rendering"). */
|
|
95
|
+
async assertStayed(whileArg) {
|
|
96
|
+
const blockedUrl = await this.takeBlockedNavigation();
|
|
97
|
+
if (blockedUrl !== null) {
|
|
98
|
+
throw new WccCaptureError(`wcctools: the demo tried to navigate away while ${whileArg}; ${formatBlockedNavigation(blockedUrl)}.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
decide(requestArg) {
|
|
102
|
+
if (requestArg.isInterceptResolutionHandled()) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!requestArg.isNavigationRequest() || requestArg.frame() !== this.page.mainFrame()) {
|
|
106
|
+
this.settle(requestArg.continue());
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (this.previewUrl !== null && requestArg.url() === this.previewUrl) {
|
|
110
|
+
// The preview document's own load, once
|
|
111
|
+
this.previewUrl = null;
|
|
112
|
+
this.settle(requestArg.continue());
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.abortedUrls.push(requestArg.url());
|
|
116
|
+
// `aborted` drops the navigation without committing an error page, so the preview stays
|
|
117
|
+
this.settle(requestArg.abort('aborted'));
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Keeps the first failure of an interception decision. Puppeteer itself tolerates a request
|
|
121
|
+
* that ended or a page that closed meanwhile; what reaches here fails the capture.
|
|
122
|
+
*/
|
|
123
|
+
settle(decisionArg) {
|
|
124
|
+
decisionArg.catch((errorArg) => {
|
|
125
|
+
this.interceptionFailure ??= errorArg;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmF2aWdhdGlvbmd1YXJkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvY2FwdHVyZS9uYXZpZ2F0aW9uZ3VhcmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFLL0QsdURBQXVEO0FBQ3ZELE1BQU0sQ0FBQyxNQUFNLHVCQUF1QixHQUFHLENBQUMsTUFBYyxFQUFVLEVBQUU7SUFDaEUsT0FBTyxpQkFBaUIsTUFBTSxtREFBbUQsQ0FBQztBQUNwRixDQUFDLENBQUM7QUFRRjs7Ozs7OztHQU9HO0FBQ0gsTUFBTSx3QkFBd0IsR0FBRyxHQUFHLEVBQUU7SUFDcEMsSUFBSSxNQUFNLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQzFCLE9BQU87SUFDVCxDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQWEsRUFBRSxDQUFDO0lBQzlCLE1BQStDLENBQUMsdUJBQXVCLEdBQUcsU0FBUyxDQUFDO0lBQ3JGLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsQ0FBQyxRQUFRLEVBQUUsRUFBRTtRQUNuRCxJQUFJLFFBQVEsQ0FBQyxXQUFXLENBQUMsWUFBWSxJQUFJLFFBQVEsQ0FBQyxlQUFlLEtBQUssSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ25HLE9BQU87UUFDVCxDQUFDO1FBQ0QsUUFBUSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQzFCLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUMzQyxDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQztBQUVGOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sT0FBTyxrQkFBa0I7SUFPVjtJQU5uQiw0RkFBNEY7SUFDcEYsV0FBVyxHQUFh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
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { WccNavigationGuard } from './navigationguard.js';
|
|
3
|
+
import { type IWccResolvedImageOptions, type IWccResolvedView } from './request.js';
|
|
4
|
+
type TBrowserContext = plugins.smartbrowser.puppeteer.BrowserContext;
|
|
5
|
+
type TPage = plugins.smartbrowser.puppeteer.Page;
|
|
6
|
+
/** A preview document in the capture browser that rendered one demo. */
|
|
7
|
+
export interface IWccRenderedPreview {
|
|
8
|
+
page: TPage;
|
|
9
|
+
/** Keeps the page on the preview and knows the navigation it blocked. */
|
|
10
|
+
navigationGuard: WccNavigationGuard;
|
|
11
|
+
selection: plugins.interfaces.IWccSelection;
|
|
12
|
+
/** What the demo rendered: an element demo or a page. */
|
|
13
|
+
type: 'element' | 'page';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Opens the preview route of the dev server at `originArg` in the context, waits for the preview
|
|
17
|
+
* runtime to install its bridge (it does once it rendered the route), resolves the subject against
|
|
18
|
+
* the catalog manifest and renders it. Waits only on the preview's own signals: the bridge, the
|
|
19
|
+
* render's completed updates and the document's fonts. The page stays on the preview: every
|
|
20
|
+
* later navigation of its main frame is blocked, and one blocked while rendering fails the capture.
|
|
21
|
+
*/
|
|
22
|
+
export declare const renderPreview: (contextArg: TBrowserContext, originArg: string, viewArg: IWccResolvedView, subjectArg: plugins.interfaces.IWccCaptureSubject) => Promise<IWccRenderedPreview>;
|
|
23
|
+
/** Screenshots a rendered preview as framed. */
|
|
24
|
+
export declare const screenshotPreview: (previewArg: IWccRenderedPreview, viewArg: IWccResolvedView, optionsArg: IWccResolvedImageOptions) => Promise<{
|
|
25
|
+
framing: plugins.interfaces.TWccCaptureFraming;
|
|
26
|
+
image: plugins.interfaces.IWccImage;
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { WccCaptureError, WccCaptureRequestError, getErrorMessage } from './errors.js';
|
|
3
|
+
import { toWccImage } from './images.js';
|
|
4
|
+
import { WccNavigationGuard } from './navigationguard.js';
|
|
5
|
+
import { captureLimits, resolveSelection } from './request.js';
|
|
6
|
+
/** How long the preview document may take to load and render its demo. */
|
|
7
|
+
const previewStartTimeoutMs = 20_000;
|
|
8
|
+
const formatPageErrors = (errorsArg) => {
|
|
9
|
+
return errorsArg.length > 0 ? ` The preview reported: ${errorsArg.slice(0, 5).join(' | ')}` : '';
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Opens the preview route of the dev server at `originArg` in the context, waits for the preview
|
|
13
|
+
* runtime to install its bridge (it does once it rendered the route), resolves the subject against
|
|
14
|
+
* the catalog manifest and renders it. Waits only on the preview's own signals: the bridge, the
|
|
15
|
+
* render's completed updates and the document's fonts. The page stays on the preview: every
|
|
16
|
+
* later navigation of its main frame is blocked, and one blocked while rendering fails the capture.
|
|
17
|
+
*/
|
|
18
|
+
export const renderPreview = async (contextArg, originArg, viewArg, subjectArg) => {
|
|
19
|
+
const page = await contextArg.newPage();
|
|
20
|
+
const pageErrors = [];
|
|
21
|
+
page.on('pageerror', (errorArg) => {
|
|
22
|
+
pageErrors.push(getErrorMessage(errorArg));
|
|
23
|
+
});
|
|
24
|
+
page.on('console', (messageArg) => {
|
|
25
|
+
if (messageArg.type() === 'error') {
|
|
26
|
+
pageErrors.push(messageArg.text());
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
await page.setViewport({ width: viewArg.width, height: viewArg.height, deviceScaleFactor: viewArg.scale });
|
|
30
|
+
await page.emulateMediaFeatures([
|
|
31
|
+
{ name: 'prefers-color-scheme', value: viewArg.theme === 'bright' ? 'light' : 'dark' },
|
|
32
|
+
]);
|
|
33
|
+
const previewUrl = `${originArg}${plugins.shared.previewRoutePath}${plugins.shared.formatPreviewQuery({ selection: null, theme: viewArg.theme })}`;
|
|
34
|
+
const navigationGuard = await WccNavigationGuard.install(page, previewUrl);
|
|
35
|
+
const response = await page.goto(previewUrl, { waitUntil: 'load', timeout: previewStartTimeoutMs });
|
|
36
|
+
if (!response?.ok()) {
|
|
37
|
+
throw new WccCaptureError(`wcctools: the preview document answered ${response?.status() ?? 'nothing'}.${formatPageErrors(pageErrors)}`);
|
|
38
|
+
}
|
|
39
|
+
await navigationGuard.forgetEarlierHistory();
|
|
40
|
+
try {
|
|
41
|
+
await page.waitForFunction(() => window.wccPreview !== undefined, { polling: 'raf', timeout: previewStartTimeoutMs });
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw new WccCaptureError(`wcctools: the preview did not start within ${previewStartTimeoutMs / 1000} s (${getErrorMessage(error)}).${formatPageErrors(pageErrors)}`);
|
|
45
|
+
}
|
|
46
|
+
const manifest = await page.evaluate(() => window.wccPreview.getManifest());
|
|
47
|
+
const selection = resolveSelection(manifest, subjectArg);
|
|
48
|
+
const outcome = await page.evaluate((selectionArg) => window.wccPreview.render(selectionArg), selection);
|
|
49
|
+
if (outcome.kind === 'empty') {
|
|
50
|
+
throw new WccCaptureError(`wcctools: ${selection.sectionName}/${selection.itemName} demo ${selection.demoIndex} did not render: ${outcome.emptyState.title}. ${outcome.emptyState.detail}${formatPageErrors(pageErrors)}`);
|
|
51
|
+
}
|
|
52
|
+
await page.evaluate(async () => {
|
|
53
|
+
await document.fonts.ready;
|
|
54
|
+
});
|
|
55
|
+
await navigationGuard.assertStayed('rendering');
|
|
56
|
+
return { page, navigationGuard, selection, type: outcome.type };
|
|
57
|
+
};
|
|
58
|
+
/** Screenshots a rendered preview as framed. */
|
|
59
|
+
export const screenshotPreview = async (previewArg, viewArg, optionsArg) => {
|
|
60
|
+
const { page } = previewArg;
|
|
61
|
+
const framing = optionsArg.framing ?? (previewArg.type === 'page' ? 'fullpage' : 'element');
|
|
62
|
+
const encoding = { type: optionsArg.format, ...(optionsArg.quality === undefined ? {} : { quality: optionsArg.quality }) };
|
|
63
|
+
if (framing === 'element') {
|
|
64
|
+
if (previewArg.type === 'page') {
|
|
65
|
+
throw new WccCaptureRequestError('wcctools: a page has no element to frame; use the viewport or fullpage framing.');
|
|
66
|
+
}
|
|
67
|
+
const handle = await page.evaluateHandle(() => window.wccPreview.findRenderedInstance());
|
|
68
|
+
try {
|
|
69
|
+
const element = handle.asElement();
|
|
70
|
+
if (!element) {
|
|
71
|
+
throw new WccCaptureError(`wcctools: the demo of ${previewArg.selection.itemName} renders no instance of its element; use the viewport framing.`);
|
|
72
|
+
}
|
|
73
|
+
const bytes = await element.screenshot(encoding);
|
|
74
|
+
return { framing, image: toWccImage(bytes, optionsArg.format), truncated: false };
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
await handle.dispose();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (framing === 'viewport') {
|
|
81
|
+
const bytes = await page.screenshot(encoding);
|
|
82
|
+
return { framing, image: toWccImage(bytes, optionsArg.format), truncated: false };
|
|
83
|
+
}
|
|
84
|
+
const documentHeight = await page.evaluate(() => document.documentElement.scrollHeight);
|
|
85
|
+
if (documentHeight > captureLimits.maxFullPageHeight) {
|
|
86
|
+
const bytes = await page.screenshot({
|
|
87
|
+
...encoding,
|
|
88
|
+
clip: { x: 0, y: 0, width: viewArg.width, height: captureLimits.maxFullPageHeight },
|
|
89
|
+
captureBeyondViewport: true,
|
|
90
|
+
});
|
|
91
|
+
return { framing, image: toWccImage(bytes, optionsArg.format), truncated: true };
|
|
92
|
+
}
|
|
93
|
+
const bytes = await page.screenshot({ ...encoding, fullPage: true });
|
|
94
|
+
return { framing, image: toWccImage(bytes, optionsArg.format), truncated: false };
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJldmlld3BhZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jYXB0dXJlL3ByZXZpZXdwYWdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxlQUFlLEVBQUUsc0JBQXNCLEVBQUUsZUFBZSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3ZGLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDekMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDMUQsT0FBTyxFQUFFLGFBQWEsRUFBRSxnQkFBZ0IsRUFBd0QsTUFBTSxjQUFjLENBQUM7QUFLckgsMEVBQTBFO0FBQzFFLE1BQU0scUJBQXFCLEdBQUcsTUFBTSxDQUFDO0FBWXJDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxTQUFtQixFQUFVLEVBQUU7SUFDdkQsT0FBTyxTQUFTLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsMEJBQTBCLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7QUFDbkcsQ0FBQyxDQUFDO0FBRUY7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sYUFBYSxHQUFHLEtBQUssRUFDaEMsVUFBMkIsRUFDM0IsU0FBaUIsRUFDakIsT0FBeUIsRUFDekIsVUFBaUQsRUFDbkIsRUFBRTtJQUNoQyxNQUFNLElBQUksR0FBRyxNQUFNLFVBQVUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUN4QyxNQUFNLFVBQVUsR0FBYSxFQUFFLENBQUM7SUFDaEMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLEVBQUUsRUFBRTtRQUNoQyxVQUFVLENBQUMsSUFBSSxDQUFDLGVBQWUs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
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
type IWccCaptureSubject = plugins.interfaces.IWccCaptureSubject;
|
|
3
|
+
type TWccCaptureFraming = plugins.interfaces.TWccCaptureFraming;
|
|
4
|
+
type TWccInteractionStep = plugins.interfaces.TWccInteractionStep;
|
|
5
|
+
/** The limits every capture request is held to. */
|
|
6
|
+
export declare const captureLimits: {
|
|
7
|
+
readonly minWidth: 200;
|
|
8
|
+
readonly maxWidth: 3840;
|
|
9
|
+
readonly minHeight: 200;
|
|
10
|
+
readonly maxHeight: 4320;
|
|
11
|
+
readonly defaultHeight: 800;
|
|
12
|
+
/** Full-page captures are cut at this height in CSS pixels. */
|
|
13
|
+
readonly maxFullPageHeight: 10000;
|
|
14
|
+
readonly defaultJpegQuality: 80;
|
|
15
|
+
readonly maxSteps: 20;
|
|
16
|
+
readonly stepTimeoutMs: 5000;
|
|
17
|
+
readonly maxWaitMs: 3000;
|
|
18
|
+
readonly defaultObserveMs: 500;
|
|
19
|
+
readonly maxObserveMs: 5000;
|
|
20
|
+
readonly defaultMaxFrames: 12;
|
|
21
|
+
readonly maxMaxFrames: 40;
|
|
22
|
+
readonly defaultFrameQuality: 70;
|
|
23
|
+
readonly maxSelectorLength: 500;
|
|
24
|
+
readonly maxTextLength: 2000;
|
|
25
|
+
readonly maxKeyLength: 32;
|
|
26
|
+
readonly maxSubjectLength: 200;
|
|
27
|
+
};
|
|
28
|
+
/** The capture widths of the named viewports: the dees-domtools breakpoints. */
|
|
29
|
+
export declare const captureViewportWidths: Readonly<Record<plugins.interfaces.TWccCaptureViewportPreset, number>>;
|
|
30
|
+
/** The preview set-up of a capture, validated and with its defaults applied. */
|
|
31
|
+
export interface IWccResolvedView {
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
theme: plugins.interfaces.TWccTheme;
|
|
35
|
+
scale: 1 | 2;
|
|
36
|
+
}
|
|
37
|
+
export interface IWccResolvedImageOptions {
|
|
38
|
+
/** Undefined: the default framing of what the demo rendered. */
|
|
39
|
+
framing?: TWccCaptureFraming;
|
|
40
|
+
format: 'png' | 'jpeg';
|
|
41
|
+
/** JPEG quality; undefined for PNG. */
|
|
42
|
+
quality?: number;
|
|
43
|
+
}
|
|
44
|
+
export interface IWccResolvedScreenshotRequest {
|
|
45
|
+
subject: IWccCaptureSubject;
|
|
46
|
+
view: IWccResolvedView;
|
|
47
|
+
image: IWccResolvedImageOptions;
|
|
48
|
+
}
|
|
49
|
+
export interface IWccResolvedInteractionRequest {
|
|
50
|
+
subject: IWccCaptureSubject;
|
|
51
|
+
view: IWccResolvedView;
|
|
52
|
+
steps: TWccInteractionStep[];
|
|
53
|
+
observeMs: number;
|
|
54
|
+
maxFrames: number;
|
|
55
|
+
frameQuality: number;
|
|
56
|
+
finalScreenshot: IWccResolvedImageOptions | null;
|
|
57
|
+
}
|
|
58
|
+
/** Reads a viewport: a named one, or a width in CSS pixels. */
|
|
59
|
+
export declare const resolveViewportWidth: (viewportArg: unknown) => number;
|
|
60
|
+
/** Validates a screenshot request and applies its defaults. */
|
|
61
|
+
export declare const resolveScreenshotRequest: (requestArg: plugins.interfaces.IWccScreenshotRequest) => IWccResolvedScreenshotRequest;
|
|
62
|
+
/** Validates an interaction request and applies its defaults. */
|
|
63
|
+
export declare const resolveInteractionRequest: (requestArg: plugins.interfaces.IWccInteractionRequest) => IWccResolvedInteractionRequest;
|
|
64
|
+
/**
|
|
65
|
+
* Resolves a subject to one demo of the catalog: an entry named by its name or its element's tag,
|
|
66
|
+
* in the named section or, without one, in exactly one section.
|
|
67
|
+
*/
|
|
68
|
+
export declare const resolveSelection: (manifestArg: plugins.interfaces.IWccCatalogManifest, subjectArg: IWccCaptureSubject) => plugins.interfaces.IWccSelection;
|
|
69
|
+
export {};
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { WccCaptureRequestError } from './errors.js';
|
|
3
|
+
/** The limits every capture request is held to. */
|
|
4
|
+
export const captureLimits = {
|
|
5
|
+
minWidth: 200,
|
|
6
|
+
maxWidth: 3840,
|
|
7
|
+
minHeight: 200,
|
|
8
|
+
maxHeight: 4320,
|
|
9
|
+
defaultHeight: 800,
|
|
10
|
+
/** Full-page captures are cut at this height in CSS pixels. */
|
|
11
|
+
maxFullPageHeight: 10_000,
|
|
12
|
+
defaultJpegQuality: 80,
|
|
13
|
+
maxSteps: 20,
|
|
14
|
+
stepTimeoutMs: 5_000,
|
|
15
|
+
maxWaitMs: 3_000,
|
|
16
|
+
defaultObserveMs: 500,
|
|
17
|
+
maxObserveMs: 5_000,
|
|
18
|
+
defaultMaxFrames: 12,
|
|
19
|
+
maxMaxFrames: 40,
|
|
20
|
+
defaultFrameQuality: 70,
|
|
21
|
+
maxSelectorLength: 500,
|
|
22
|
+
maxTextLength: 2_000,
|
|
23
|
+
maxKeyLength: 32,
|
|
24
|
+
maxSubjectLength: 200,
|
|
25
|
+
};
|
|
26
|
+
/** The capture widths of the named viewports: the dees-domtools breakpoints. */
|
|
27
|
+
export const captureViewportWidths = {
|
|
28
|
+
phone: plugins.deesDomtools.breakpoints.phone,
|
|
29
|
+
phablet: plugins.deesDomtools.breakpoints.phablet,
|
|
30
|
+
tablet: plugins.deesDomtools.breakpoints.tablet,
|
|
31
|
+
desktop: plugins.deesDomtools.breakpoints.desktop,
|
|
32
|
+
};
|
|
33
|
+
const isViewportPreset = (valueArg) => {
|
|
34
|
+
return typeof valueArg === 'string' && Object.hasOwn(captureViewportWidths, valueArg);
|
|
35
|
+
};
|
|
36
|
+
const framings = ['element', 'viewport', 'fullpage'];
|
|
37
|
+
const isInteger = (valueArg) => typeof valueArg === 'number' && Number.isInteger(valueArg);
|
|
38
|
+
const requireIntegerIn = (valueArg, minArg, maxArg, nameArg) => {
|
|
39
|
+
if (!isInteger(valueArg) || valueArg < minArg || valueArg > maxArg) {
|
|
40
|
+
throw new WccCaptureRequestError(`wcctools: ${nameArg} must be a whole number from ${minArg} to ${maxArg}.`);
|
|
41
|
+
}
|
|
42
|
+
return valueArg;
|
|
43
|
+
};
|
|
44
|
+
const requireText = (valueArg, maxLengthArg, nameArg, allowEmptyArg = false) => {
|
|
45
|
+
if (typeof valueArg !== 'string' || (!allowEmptyArg && valueArg.trim() === '') || valueArg.length > maxLengthArg) {
|
|
46
|
+
throw new WccCaptureRequestError(`wcctools: ${nameArg} must be ${allowEmptyArg ? 'a' : 'a non-empty'} string of at most ${maxLengthArg} characters.`);
|
|
47
|
+
}
|
|
48
|
+
return valueArg;
|
|
49
|
+
};
|
|
50
|
+
const resolveSubject = (subjectArg) => {
|
|
51
|
+
if (typeof subjectArg !== 'object' || subjectArg === null) {
|
|
52
|
+
throw new WccCaptureRequestError('wcctools: a capture needs a subject: the catalog entry to render.');
|
|
53
|
+
}
|
|
54
|
+
const subject = subjectArg;
|
|
55
|
+
const resolved = {
|
|
56
|
+
itemName: requireText(subject.itemName, captureLimits.maxSubjectLength, 'the subject\'s itemName'),
|
|
57
|
+
demoIndex: subject.demoIndex === undefined ? 0 : requireIntegerIn(subject.demoIndex, 0, 1_000, 'the demo index'),
|
|
58
|
+
};
|
|
59
|
+
if (subject.sectionName !== undefined) {
|
|
60
|
+
resolved.sectionName = requireText(subject.sectionName, captureLimits.maxSubjectLength, 'the subject\'s sectionName');
|
|
61
|
+
}
|
|
62
|
+
return resolved;
|
|
63
|
+
};
|
|
64
|
+
/** Reads a viewport: a named one, or a width in CSS pixels. */
|
|
65
|
+
export const resolveViewportWidth = (viewportArg) => {
|
|
66
|
+
if (viewportArg === undefined) {
|
|
67
|
+
return captureViewportWidths.desktop;
|
|
68
|
+
}
|
|
69
|
+
if (isViewportPreset(viewportArg)) {
|
|
70
|
+
return captureViewportWidths[viewportArg];
|
|
71
|
+
}
|
|
72
|
+
if (isInteger(viewportArg)) {
|
|
73
|
+
return requireIntegerIn(viewportArg, captureLimits.minWidth, captureLimits.maxWidth, 'the viewport width');
|
|
74
|
+
}
|
|
75
|
+
throw new WccCaptureRequestError(`wcctools: the viewport must be one of ${Object.keys(captureViewportWidths).join(', ')} or a width from ${captureLimits.minWidth} to ${captureLimits.maxWidth}.`);
|
|
76
|
+
};
|
|
77
|
+
const resolveView = (requestArg) => {
|
|
78
|
+
const theme = requestArg.theme ?? 'dark';
|
|
79
|
+
if (theme !== 'dark' && theme !== 'bright') {
|
|
80
|
+
throw new WccCaptureRequestError('wcctools: the theme must be dark or bright.');
|
|
81
|
+
}
|
|
82
|
+
const scale = requestArg.scale ?? 1;
|
|
83
|
+
if (scale !== 1 && scale !== 2) {
|
|
84
|
+
throw new WccCaptureRequestError('wcctools: the scale must be 1 or 2.');
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
width: resolveViewportWidth(requestArg.viewport),
|
|
88
|
+
height: requestArg.height === undefined
|
|
89
|
+
? captureLimits.defaultHeight
|
|
90
|
+
: requireIntegerIn(requestArg.height, captureLimits.minHeight, captureLimits.maxHeight, 'the height'),
|
|
91
|
+
theme,
|
|
92
|
+
scale,
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
const resolveImageOptions = (optionsArg) => {
|
|
96
|
+
const framing = optionsArg.framing;
|
|
97
|
+
if (framing !== undefined && !framings.includes(framing)) {
|
|
98
|
+
throw new WccCaptureRequestError(`wcctools: the framing must be one of ${framings.join(', ')}.`);
|
|
99
|
+
}
|
|
100
|
+
const format = optionsArg.format ?? 'png';
|
|
101
|
+
if (format !== 'png' && format !== 'jpeg') {
|
|
102
|
+
throw new WccCaptureRequestError('wcctools: the format must be png or jpeg.');
|
|
103
|
+
}
|
|
104
|
+
if (format === 'png' && optionsArg.quality !== undefined) {
|
|
105
|
+
throw new WccCaptureRequestError('wcctools: a quality applies to jpeg only.');
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
framing: framing,
|
|
109
|
+
format,
|
|
110
|
+
quality: format === 'jpeg'
|
|
111
|
+
? (optionsArg.quality === undefined ? captureLimits.defaultJpegQuality : requireIntegerIn(optionsArg.quality, 1, 100, 'the quality'))
|
|
112
|
+
: undefined,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
/** Validates a screenshot request and applies its defaults. */
|
|
116
|
+
export const resolveScreenshotRequest = (requestArg) => {
|
|
117
|
+
if (typeof requestArg !== 'object' || requestArg === null) {
|
|
118
|
+
throw new WccCaptureRequestError('wcctools: the screenshot request is missing.');
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
subject: resolveSubject(requestArg.subject),
|
|
122
|
+
view: resolveView(requestArg),
|
|
123
|
+
image: resolveImageOptions(requestArg),
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
const resolveStep = (stepArg, indexArg) => {
|
|
127
|
+
const name = `step ${indexArg}`;
|
|
128
|
+
if (typeof stepArg !== 'object' || stepArg === null) {
|
|
129
|
+
throw new WccCaptureRequestError(`wcctools: ${name} must be an object with an action.`);
|
|
130
|
+
}
|
|
131
|
+
const step = stepArg;
|
|
132
|
+
const selector = (requiredArg) => {
|
|
133
|
+
if (!requiredArg && step.selector === undefined) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
return requireText(step.selector, captureLimits.maxSelectorLength, `the selector of ${name}`);
|
|
137
|
+
};
|
|
138
|
+
switch (step.action) {
|
|
139
|
+
case 'click':
|
|
140
|
+
case 'hover':
|
|
141
|
+
return { action: step.action, selector: selector(true) };
|
|
142
|
+
case 'fill':
|
|
143
|
+
return {
|
|
144
|
+
action: 'fill',
|
|
145
|
+
selector: selector(true),
|
|
146
|
+
text: requireText(step.text, captureLimits.maxTextLength, `the text of ${name}`, true),
|
|
147
|
+
};
|
|
148
|
+
case 'press': {
|
|
149
|
+
const pressStep = {
|
|
150
|
+
action: 'press',
|
|
151
|
+
key: requireText(step.key, captureLimits.maxKeyLength, `the key of ${name}`),
|
|
152
|
+
};
|
|
153
|
+
const pressSelector = selector(false);
|
|
154
|
+
return pressSelector === undefined ? pressStep : { ...pressStep, selector: pressSelector };
|
|
155
|
+
}
|
|
156
|
+
case 'waitFor': {
|
|
157
|
+
const state = step.state ?? 'visible';
|
|
158
|
+
if (state !== 'visible' && state !== 'hidden') {
|
|
159
|
+
throw new WccCaptureRequestError(`wcctools: the state of ${name} must be visible or hidden.`);
|
|
160
|
+
}
|
|
161
|
+
return { action: 'waitFor', selector: selector(true), state };
|
|
162
|
+
}
|
|
163
|
+
case 'wait':
|
|
164
|
+
return { action: 'wait', ms: requireIntegerIn(step.ms, 0, captureLimits.maxWaitMs, `the time of ${name}`) };
|
|
165
|
+
default:
|
|
166
|
+
throw new WccCaptureRequestError(`wcctools: the action of ${name} must be one of click, hover, fill, press, waitFor, wait.`);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/** Validates an interaction request and applies its defaults. */
|
|
170
|
+
export const resolveInteractionRequest = (requestArg) => {
|
|
171
|
+
if (typeof requestArg !== 'object' || requestArg === null) {
|
|
172
|
+
throw new WccCaptureRequestError('wcctools: the interaction request is missing.');
|
|
173
|
+
}
|
|
174
|
+
if (!Array.isArray(requestArg.steps) || requestArg.steps.length > captureLimits.maxSteps) {
|
|
175
|
+
throw new WccCaptureRequestError(`wcctools: an interaction has a list of at most ${captureLimits.maxSteps} steps.`);
|
|
176
|
+
}
|
|
177
|
+
if (requestArg.finalScreenshot !== undefined && (typeof requestArg.finalScreenshot !== 'object' || requestArg.finalScreenshot === null)) {
|
|
178
|
+
throw new WccCaptureRequestError('wcctools: finalScreenshot must be an object.');
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
subject: resolveSubject(requestArg.subject),
|
|
182
|
+
view: resolveView(requestArg),
|
|
183
|
+
steps: requestArg.steps.map((stepArg, indexArg) => resolveStep(stepArg, indexArg)),
|
|
184
|
+
observeMs: requestArg.observeMs === undefined
|
|
185
|
+
? captureLimits.defaultObserveMs
|
|
186
|
+
: requireIntegerIn(requestArg.observeMs, 0, captureLimits.maxObserveMs, 'observeMs'),
|
|
187
|
+
maxFrames: requestArg.maxFrames === undefined
|
|
188
|
+
? captureLimits.defaultMaxFrames
|
|
189
|
+
: requireIntegerIn(requestArg.maxFrames, 1, captureLimits.maxMaxFrames, 'maxFrames'),
|
|
190
|
+
frameQuality: requestArg.frameQuality === undefined
|
|
191
|
+
? captureLimits.defaultFrameQuality
|
|
192
|
+
: requireIntegerIn(requestArg.frameQuality, 1, 100, 'frameQuality'),
|
|
193
|
+
finalScreenshot: requestArg.finalScreenshot ? resolveImageOptions(requestArg.finalScreenshot) : null,
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
const describeEntry = (sectionNameArg, entryNameArg) => `${sectionNameArg}/${entryNameArg}`;
|
|
197
|
+
const listSome = (namesArg) => {
|
|
198
|
+
const shown = namesArg.slice(0, 20).join(', ');
|
|
199
|
+
return namesArg.length > 20 ? `${shown}, … (${namesArg.length} in all)` : shown;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Resolves a subject to one demo of the catalog: an entry named by its name or its element's tag,
|
|
203
|
+
* in the named section or, without one, in exactly one section.
|
|
204
|
+
*/
|
|
205
|
+
export const resolveSelection = (manifestArg, subjectArg) => {
|
|
206
|
+
const sections = subjectArg.sectionName === undefined
|
|
207
|
+
? manifestArg.sections
|
|
208
|
+
: manifestArg.sections.filter((sectionArg) => sectionArg.name === subjectArg.sectionName);
|
|
209
|
+
if (sections.length === 0) {
|
|
210
|
+
throw new WccCaptureRequestError(`wcctools: the catalog has no section "${subjectArg.sectionName}". Sections: ${listSome(manifestArg.sections.map((sectionArg) => sectionArg.name))}.`);
|
|
211
|
+
}
|
|
212
|
+
const wanted = subjectArg.itemName;
|
|
213
|
+
const matchesName = (entryArg) => {
|
|
214
|
+
return entryArg.name === wanted || (entryArg.tagName !== null && entryArg.tagName === wanted.toLowerCase());
|
|
215
|
+
};
|
|
216
|
+
const matches = sections.flatMap((sectionArg) => sectionArg.entries
|
|
217
|
+
.filter(matchesName)
|
|
218
|
+
.map((entryArg) => ({ section: sectionArg, entry: entryArg })));
|
|
219
|
+
if (matches.length === 0) {
|
|
220
|
+
const withoutDemo = sections.flatMap((sectionArg) => sectionArg.entriesWithoutDemo
|
|
221
|
+
.filter(matchesName)
|
|
222
|
+
.map((entryArg) => describeEntry(sectionArg.name, entryArg.name)));
|
|
223
|
+
if (withoutDemo.length > 0) {
|
|
224
|
+
throw new WccCaptureRequestError(`wcctools: ${listSome(withoutDemo)} has no demo to capture.`);
|
|
225
|
+
}
|
|
226
|
+
const known = sections.flatMap((sectionArg) => sectionArg.entries.map((entryArg) => describeEntry(sectionArg.name, entryArg.name)));
|
|
227
|
+
throw new WccCaptureRequestError(`wcctools: the catalog has no entry "${wanted}". Entries: ${listSome(known)}.`);
|
|
228
|
+
}
|
|
229
|
+
if (matches.length > 1) {
|
|
230
|
+
throw new WccCaptureRequestError(`wcctools: "${wanted}" names ${matches.length} entries (${listSome(matches.map((matchArg) => describeEntry(matchArg.section.name, matchArg.entry.name)))}); name its section.`);
|
|
231
|
+
}
|
|
232
|
+
const [{ section, entry }] = matches;
|
|
233
|
+
const demoIndex = subjectArg.demoIndex ?? 0;
|
|
234
|
+
if (demoIndex >= entry.demoCount) {
|
|
235
|
+
throw new WccCaptureRequestError(`wcctools: ${describeEntry(section.name, entry.name)} has ${entry.demoCount} demo${entry.demoCount === 1 ? '' : 's'}; demo ${demoIndex} does not exist (demos count from 0).`);
|
|
236
|
+
}
|
|
237
|
+
return { sectionName: section.name, itemName: entry.name, demoIndex };
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVxdWVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL2NhcHR1cmUvcmVxdWVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFNckQsbURBQW1EO0FBQ25ELE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRztJQUMzQixRQUFRLEVBQUUsR0FBRztJQUNiLFFBQVEsRUFBRSxJQUFJO0lBQ2QsU0FBUyxFQUFFLEdBQUc7SUFDZCxTQUFTLEVBQUUsSUFBSTtJQUNmLGFBQWEsRUFBRSxHQUFHO0lBQ2xCLCtEQUErRDtJQUMvRCxpQkFBaUIsRUFBRSxNQUFNO0lBQ3pCLGtCQUFrQixFQUFFLEVBQUU7SUFDdEIsUUFBUSxFQUFFLEVBQUU7SUFDWixhQUFhLEVBQUUsS0FBSztJQUNwQixTQUFTLEVBQUUsS0FBSztJQUNoQixnQkFBZ0IsRUFBRSxHQUFHO0lBQ3JCLFlBQVksRUFBRSxLQUFLO0lBQ25CLGdCQUFnQixFQUFFLEVBQUU7SUFDcEIsWUFBWSxFQUFFLEVBQUU7SUFDaEIsbUJBQW1CLEVBQUUsRUFBRTtJQUN2QixpQkFBaUIsRUFBRSxHQUFHO0lBQ3RCLGFBQWEsRUFBRSxLQUFLO0lBQ3BCLFlBQVksRUFBRSxFQUFFO0lBQ2hCLGdCQUFnQixFQUFFLEdBQUc7Q0FDYixDQUFDO0FBRVgsZ0ZBQWdGO0FBQ2hGLE1BQU0sQ0FBQyxNQUFNLHFCQUFxQixHQUEyRTtJQUMzRyxLQUFLLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsS0FBSztJQUM3QyxPQUFPLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsT0FBTztJQUNqRCxNQUFNLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsTUFBTTtJQUMvQyxPQUFPLEVBQUUsT0FBTyxDQUFDLFlBQVks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
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
/** The longest time a status request waits for a change before it answers the unchanged status. */
|
|
3
|
+
export declare const bundleStatusWaitLimitMs = 25000;
|
|
4
|
+
/** What a status request answers while the board is closed: the server is not serving. */
|
|
5
|
+
export declare const bundleStatusClosedMessage = "wcctools: the dev server is not serving the bundle status.";
|
|
6
|
+
/**
|
|
7
|
+
* The status of the catalog bundles, as tswatch's bundle events report it, for the shell's
|
|
8
|
+
* `getBundleStatus` requests. Every event raises the revision and answers the requests waiting for
|
|
9
|
+
* a change. While closed (the server is not serving), requests are refused with a typed error, so
|
|
10
|
+
* a client backs off as it does for any failed request.
|
|
11
|
+
*/
|
|
12
|
+
export declare class WccBundleStatusBoard {
|
|
13
|
+
private readonly bundles;
|
|
14
|
+
private revision;
|
|
15
|
+
private isOpen;
|
|
16
|
+
/** Settles one waiting request: answers the current snapshot, or refuses; each removes itself. */
|
|
17
|
+
private readonly waiters;
|
|
18
|
+
/** Starts a run of the server: the bundles of an earlier run are forgotten. */
|
|
19
|
+
open(): void;
|
|
20
|
+
/** Ends the run: every waiting request is refused now, later ones at once. */
|
|
21
|
+
close(): void;
|
|
22
|
+
/** The number of requests waiting for a change. */
|
|
23
|
+
getWaiterCount(): number;
|
|
24
|
+
record(eventArg: plugins.tswatch.TBundleEvent): void;
|
|
25
|
+
getSnapshot(): plugins.interfaces.IWccBundleStatusSnapshot;
|
|
26
|
+
/**
|
|
27
|
+
* The snapshot once it differs from `afterRevisionArg`, or after the wait limit; at once without
|
|
28
|
+
* a revision or for a revision that is no longer current. Refused while closed. When
|
|
29
|
+
* `abortSignalArg` aborts (the client went away), the wait ends at once and releases its timer;
|
|
30
|
+
* it answers the current snapshot, which the transport drops.
|
|
31
|
+
*/
|
|
32
|
+
waitForChange(afterRevisionArg: number | undefined, abortSignalArg?: AbortSignal): Promise<plugins.interfaces.IWccBundleStatusSnapshot>;
|
|
33
|
+
private changed;
|
|
34
|
+
private answerWaiters;
|
|
35
|
+
private createClosedError;
|
|
36
|
+
}
|