@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,171 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
/** The interface address `wcctools dev` listens on unless told otherwise. */
|
|
3
|
+
export const defaultListenHost = '127.0.0.1';
|
|
4
|
+
/** The hostnames every dev server answers: the loopback names. */
|
|
5
|
+
const loopbackHostnames = ['localhost', '127.0.0.1', '::1'];
|
|
6
|
+
const wildcardHosts = new Set(['0.0.0.0', '::']);
|
|
7
|
+
/** Methods that never change state, which the Origin check leaves to the Host check alone. */
|
|
8
|
+
const safeMethods = new Set(['GET', 'HEAD', 'OPTIONS']);
|
|
9
|
+
/** Canonical form of a hostname: lower case, IPv6 without brackets, no trailing dot. */
|
|
10
|
+
export const normalizeHostname = (hostnameArg) => {
|
|
11
|
+
let hostname = hostnameArg.trim().toLowerCase();
|
|
12
|
+
if (hostname.startsWith('[') && hostname.endsWith(']')) {
|
|
13
|
+
hostname = hostname.slice(1, -1);
|
|
14
|
+
}
|
|
15
|
+
if (hostname.endsWith('.') && hostname.length > 1) {
|
|
16
|
+
hostname = hostname.slice(0, -1);
|
|
17
|
+
}
|
|
18
|
+
return hostname;
|
|
19
|
+
};
|
|
20
|
+
/** Whether the address is the wildcard address of all interfaces. */
|
|
21
|
+
export const isWildcardHost = (hostArg) => wildcardHosts.has(normalizeHostname(hostArg));
|
|
22
|
+
/** Whether a peer address is a loopback address (IPv4, IPv6 or IPv4-mapped IPv6). */
|
|
23
|
+
export const isLoopbackAddress = (addressArg) => {
|
|
24
|
+
const address = normalizeHostname(addressArg);
|
|
25
|
+
return address === '::1' || address.startsWith('127.') || address.startsWith('::ffff:127.');
|
|
26
|
+
};
|
|
27
|
+
/** A hostname as it appears in a URL: IPv6 addresses in brackets. */
|
|
28
|
+
export const formatUrlHost = (hostnameArg) => {
|
|
29
|
+
return hostnameArg.includes(':') ? `[${hostnameArg}]` : hostnameArg;
|
|
30
|
+
};
|
|
31
|
+
/** An invalid `--host` or `--allowed-host` value. */
|
|
32
|
+
export class WccHostPolicyError extends Error {
|
|
33
|
+
constructor(messageArg) {
|
|
34
|
+
super(messageArg);
|
|
35
|
+
this.name = 'WccHostPolicyError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Who may talk to a `wcctools dev` server. The server listens on loopback unless told otherwise
|
|
40
|
+
* and answers only the hostnames it serves: the loopback names, the address it listens on, the
|
|
41
|
+
* machine's interface addresses when it listens on all of them, and the hosts allowed explicitly.
|
|
42
|
+
* Every other Host is refused, which defeats DNS rebinding. Requests that can change state
|
|
43
|
+
* (any method but GET, HEAD and OPTIONS) and WebSocket connections are further admitted only from
|
|
44
|
+
* the server's own origin, so other web pages cannot drive the API; a request without an Origin
|
|
45
|
+
* is admitted only from a loopback peer (local tools, such as the CLI and tests).
|
|
46
|
+
*/
|
|
47
|
+
export class WccHostPolicy {
|
|
48
|
+
/** The canonical hostnames the server answers. */
|
|
49
|
+
allowedHostnames;
|
|
50
|
+
listenHost;
|
|
51
|
+
port = null;
|
|
52
|
+
constructor(optionsArg) {
|
|
53
|
+
this.listenHost = WccHostPolicy.parseHost(optionsArg.listenHost);
|
|
54
|
+
const hostnames = new Set(loopbackHostnames);
|
|
55
|
+
if (isWildcardHost(this.listenHost)) {
|
|
56
|
+
for (const address of WccHostPolicy.getInterfaceAddresses()) {
|
|
57
|
+
hostnames.add(address);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
hostnames.add(this.listenHost);
|
|
62
|
+
}
|
|
63
|
+
for (const host of optionsArg.allowedHosts ?? []) {
|
|
64
|
+
hostnames.add(WccHostPolicy.parseHost(host));
|
|
65
|
+
}
|
|
66
|
+
this.allowedHostnames = hostnames;
|
|
67
|
+
}
|
|
68
|
+
/** A hostname or address, as a URL would carry it; anything else is refused. */
|
|
69
|
+
static parseHost(hostArg) {
|
|
70
|
+
const hostname = normalizeHostname(hostArg);
|
|
71
|
+
let parsed = null;
|
|
72
|
+
try {
|
|
73
|
+
parsed = normalizeHostname(new URL(`http://${formatUrlHost(hostname)}/`).hostname);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
parsed = null;
|
|
77
|
+
}
|
|
78
|
+
if (!hostname || parsed !== hostname) {
|
|
79
|
+
throw new WccHostPolicyError(`wcctools: "${hostArg}" is not a hostname or address.`);
|
|
80
|
+
}
|
|
81
|
+
return hostname;
|
|
82
|
+
}
|
|
83
|
+
/** Every address of the machine's network interfaces. */
|
|
84
|
+
static getInterfaceAddresses() {
|
|
85
|
+
const addresses = [];
|
|
86
|
+
for (const interfaceAddresses of Object.values(plugins.os.networkInterfaces())) {
|
|
87
|
+
for (const interfaceAddress of interfaceAddresses ?? []) {
|
|
88
|
+
addresses.push(normalizeHostname(interfaceAddress.address));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return addresses;
|
|
92
|
+
}
|
|
93
|
+
/** The port the server listens on, once it does: origins are compared against it. */
|
|
94
|
+
setPort(portArg) {
|
|
95
|
+
this.port = portArg;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The host the server's own tools reach it on: the loopback address when it listens on all
|
|
99
|
+
* interfaces or on loopback, otherwise the address it listens on.
|
|
100
|
+
*/
|
|
101
|
+
getLocalHost() {
|
|
102
|
+
if (isWildcardHost(this.listenHost) || isLoopbackAddress(this.listenHost)) {
|
|
103
|
+
return '127.0.0.1';
|
|
104
|
+
}
|
|
105
|
+
return this.listenHost;
|
|
106
|
+
}
|
|
107
|
+
/** The host shown to the user: `localhost` for a loopback or all-interface server. */
|
|
108
|
+
getDisplayHost() {
|
|
109
|
+
if (isWildcardHost(this.listenHost) || isLoopbackAddress(this.listenHost) || this.listenHost === 'localhost') {
|
|
110
|
+
return 'localhost';
|
|
111
|
+
}
|
|
112
|
+
return this.listenHost;
|
|
113
|
+
}
|
|
114
|
+
/** Server-wide HTTP and WebSocket admission: the Host must be one the server answers. */
|
|
115
|
+
admitHost = (contextArg) => {
|
|
116
|
+
const hostname = normalizeHostname(contextArg.url.hostname);
|
|
117
|
+
if (this.allowedHostnames.has(hostname)) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return this.refuse(`wcctools: the dev server does not answer the host "${hostname}". Open it on localhost, or restart it with --allowed-host ${hostname}.`);
|
|
121
|
+
};
|
|
122
|
+
/** Surface HTTP admission: a request that can change state must come from the server's origin. */
|
|
123
|
+
admitRequestOrigin = (contextArg) => {
|
|
124
|
+
if (safeMethods.has(contextArg.method)) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return this.admitOrigin(contextArg);
|
|
128
|
+
};
|
|
129
|
+
/** Surface WebSocket admission: a connection must come from the server's origin. */
|
|
130
|
+
admitWebsocketOrigin = (contextArg) => {
|
|
131
|
+
return this.admitOrigin(contextArg);
|
|
132
|
+
};
|
|
133
|
+
/** Whether an Origin header names this server: http, an allowed host and the server's port. */
|
|
134
|
+
isOwnOrigin(originArg) {
|
|
135
|
+
if (this.port === null) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
let origin;
|
|
139
|
+
try {
|
|
140
|
+
origin = new URL(originArg);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
const port = origin.port === '' ? 80 : Number(origin.port);
|
|
146
|
+
return origin.protocol === 'http:'
|
|
147
|
+
&& port === this.port
|
|
148
|
+
&& this.allowedHostnames.has(normalizeHostname(origin.hostname));
|
|
149
|
+
}
|
|
150
|
+
admitOrigin(contextArg) {
|
|
151
|
+
const origin = contextArg.headers.get('origin');
|
|
152
|
+
if (origin === null) {
|
|
153
|
+
const peer = contextArg.connectionInfo?.remoteAddr ?? 'unknown';
|
|
154
|
+
if (isLoopbackAddress(peer)) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
return this.refuse('wcctools: requests without an Origin are admitted only from this machine.');
|
|
158
|
+
}
|
|
159
|
+
if (this.isOwnOrigin(origin)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return this.refuse(`wcctools: requests from the origin "${origin}" are not admitted.`);
|
|
163
|
+
}
|
|
164
|
+
refuse(messageArg) {
|
|
165
|
+
return new Response(`${messageArg}\n`, {
|
|
166
|
+
status: 403,
|
|
167
|
+
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ob3N0cG9saWN5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvY2xhc3Nlcy5ob3N0cG9saWN5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBS3hDLDZFQUE2RTtBQUM3RSxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxXQUFXLENBQUM7QUFFN0Msa0VBQWtFO0FBQ2xFLE1BQU0saUJBQWlCLEdBQUcsQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFLEtBQUssQ0FBQyxDQUFDO0FBRTVELE1BQU0sYUFBYSxHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7QUFFakQsOEZBQThGO0FBQzlGLE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLE1BQU0sRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDO0FBRXhELHdGQUF3RjtBQUN4RixNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxDQUFDLFdBQW1CLEVBQVUsRUFBRTtJQUMvRCxJQUFJLFFBQVEsR0FBRyxXQUFXLENBQUMsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDaEQsSUFBSSxRQUFRLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN2RCxRQUFRLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBQ0QsSUFBSSxRQUFRLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDbEQsUUFBUSxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFD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
|
package/dist_ts/cli.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import { commitinfo } from './00_commitinfo_data.js';
|
|
3
3
|
import { WccDevServer } from './classes.devserver.js';
|
|
4
|
+
import { WccHostPolicyError } from './classes.hostpolicy.js';
|
|
5
|
+
import { WccCaptureRequestError } from './capture/index.js';
|
|
6
|
+
import { registerScreenshotCommand } from './cli.screenshot.js';
|
|
4
7
|
import { StandardConfigError, StandardUsageError, registerStandardCommands } from './standards/index.js';
|
|
5
8
|
/**
|
|
6
9
|
* Registers the wcctools commands on the given console.
|
|
@@ -19,12 +22,32 @@ export const registerCommands = (out) => {
|
|
|
19
22
|
type: 'number',
|
|
20
23
|
description: 'Port to listen on (0 picks a free port); overrides the tswatch configuration',
|
|
21
24
|
},
|
|
25
|
+
host: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Interface address to listen on (default 127.0.0.1, this machine only; 0.0.0.0 for every interface)',
|
|
28
|
+
},
|
|
29
|
+
allowedHost: {
|
|
30
|
+
type: 'strings',
|
|
31
|
+
description: 'A further hostname or address the server answers, besides localhost and the listen address (repeatable)',
|
|
32
|
+
},
|
|
22
33
|
},
|
|
23
34
|
}, async ({ options }) => {
|
|
24
|
-
const devServer = new WccDevServer({
|
|
35
|
+
const devServer = new WccDevServer({
|
|
36
|
+
port: options.port,
|
|
37
|
+
cwd: process.cwd(),
|
|
38
|
+
host: options.host,
|
|
39
|
+
allowedHosts: options.allowedHost,
|
|
40
|
+
});
|
|
41
|
+
// The entry point owns process signals: SIGINT and SIGTERM run every registered cleanup, the
|
|
42
|
+
// dev server's stop among them, once within tswatch's shutdown deadline, then exit.
|
|
43
|
+
plugins.smartexit.ProcessLifecycle.install({ shutdownTimeoutMs: devServer.getShutdownTimeoutMs() });
|
|
25
44
|
const address = await devServer.start();
|
|
26
45
|
await out.log(`wcctools dev: ${address.url}`);
|
|
46
|
+
if (address.host !== '127.0.0.1' && address.host !== '::1' && address.host !== 'localhost') {
|
|
47
|
+
await out.log(`wcctools dev: listening on ${address.host}, answering the hosts ${address.allowedHosts.join(', ')}`);
|
|
48
|
+
}
|
|
27
49
|
});
|
|
50
|
+
registerScreenshotCommand(out);
|
|
28
51
|
registerStandardCommands(out);
|
|
29
52
|
out.cli.default({}, async () => {
|
|
30
53
|
await out.log(out.cli.renderHelp());
|
|
@@ -41,11 +64,13 @@ export const runCli = async () => {
|
|
|
41
64
|
// Configuration and usage errors exit with 2, every other failure with 1.
|
|
42
65
|
const usageError = error instanceof plugins.smartconsole.CliUsageError
|
|
43
66
|
|| error instanceof StandardConfigError
|
|
44
|
-
|| error instanceof StandardUsageError
|
|
67
|
+
|| error instanceof StandardUsageError
|
|
68
|
+
|| error instanceof WccCaptureRequestError
|
|
69
|
+
|| error instanceof WccHostPolicyError;
|
|
45
70
|
process.exitCode = usageError ? 2 : 1;
|
|
46
71
|
}
|
|
47
72
|
finally {
|
|
48
73
|
await out.dispose();
|
|
49
74
|
}
|
|
50
75
|
};
|
|
51
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
76
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvY2xpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDN0QsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUQsT0FBTyxFQUFFLHlCQUF5QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDaEUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLGtCQUFrQixFQUFFLHdCQUF3QixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFekc7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLEdBQXNDLEVBQVEsRUFBRTtJQUMvRSxHQUFHLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQztRQUNoQixJQUFJLEVBQUUsVUFBVTtRQUNoQixPQUFPLEVBQUUsVUFBVSxDQUFDLE9BQU87UUFDM0IsV0FBVyxFQUFFLHVEQUF1RDtLQUNyRSxDQUFDLENBQUM7SUFFSCxHQUFHLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQztRQUNkLElBQUksRUFBRSxLQUFLO1FBQ1gsV0FBVyxFQUFFLHdFQUF3RTtRQUNyRixPQUFPLEVBQUU7WUFDUCxJQUFJLEVBQUU7Z0JBQ0osSUFBSSxFQUFFLFFBQVE7Z0JBQ2QsV0FBVyxFQUFFLDhFQUE4RTthQUM1RjtZQUNELElBQUksRUFBRTtnQkFDSixJQUFJLEVBQUUsUUFBUTtnQkFDZCxXQUFXLEVBQUUsb0dBQW9HO2FBQ2xIO1lBQ0QsV0FBVyxFQUFFO2dCQUNYLElBQUksRUFBRSxTQUFTO2dCQUNmLFdBQVcsRUFBRSx5R0FBeUc7YUFD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
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* `wcctools screenshot`: bundles the catalog of the working directory, serves it on a free
|
|
4
|
+
* loopback port, screenshots one demo in a headless browser, writes the image and stops.
|
|
5
|
+
*/
|
|
6
|
+
export declare const registerScreenshotCommand: (out: plugins.smartconsole.SmartConsole) => void;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { WccDevServer } from './classes.devserver.js';
|
|
3
|
+
import { WccCaptureRequestError, captureViewportWidths, resolveScreenshotRequest } from './capture/index.js';
|
|
4
|
+
/** Reads `<item>` or `<section>/<item>`. */
|
|
5
|
+
const parseSubject = (argsArg, demoArg) => {
|
|
6
|
+
if (argsArg.length !== 1 || argsArg[0].trim() === '') {
|
|
7
|
+
throw new WccCaptureRequestError('wcctools screenshot: name one catalog entry, as <item> or <section>/<item>.');
|
|
8
|
+
}
|
|
9
|
+
const [subject] = argsArg;
|
|
10
|
+
const slash = subject.indexOf('/');
|
|
11
|
+
const named = slash === -1
|
|
12
|
+
? { itemName: subject }
|
|
13
|
+
: { sectionName: subject.slice(0, slash), itemName: subject.slice(slash + 1) };
|
|
14
|
+
return demoArg === undefined ? named : { ...named, demoIndex: demoArg };
|
|
15
|
+
};
|
|
16
|
+
/** Reads a named viewport or a width in pixels. */
|
|
17
|
+
const parseViewport = (valueArg) => {
|
|
18
|
+
if (valueArg === undefined) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (Object.hasOwn(captureViewportWidths, valueArg)) {
|
|
22
|
+
return valueArg;
|
|
23
|
+
}
|
|
24
|
+
if (/^\d+$/.test(valueArg)) {
|
|
25
|
+
return Number(valueArg);
|
|
26
|
+
}
|
|
27
|
+
throw new WccCaptureRequestError(`wcctools screenshot: --viewport must be one of ${Object.keys(captureViewportWidths).join(', ')} or a width in pixels.`);
|
|
28
|
+
};
|
|
29
|
+
/** The image format the output file's extension names. */
|
|
30
|
+
const formatForFile = (fileArg) => {
|
|
31
|
+
const extension = plugins.path.extname(fileArg).toLowerCase();
|
|
32
|
+
if (extension === '.png') {
|
|
33
|
+
return 'png';
|
|
34
|
+
}
|
|
35
|
+
if (extension === '.jpg' || extension === '.jpeg') {
|
|
36
|
+
return 'jpeg';
|
|
37
|
+
}
|
|
38
|
+
throw new WccCaptureRequestError('wcctools screenshot: --out must name a .png, .jpg or .jpeg file.');
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* `wcctools screenshot`: bundles the catalog of the working directory, serves it on a free
|
|
42
|
+
* loopback port, screenshots one demo in a headless browser, writes the image and stops.
|
|
43
|
+
*/
|
|
44
|
+
export const registerScreenshotCommand = (out) => {
|
|
45
|
+
out.cli.command({
|
|
46
|
+
name: 'screenshot',
|
|
47
|
+
description: 'Screenshot one demo of the catalog in a headless browser: wcctools screenshot <item | section/item> --out <file.png>',
|
|
48
|
+
options: {
|
|
49
|
+
out: { type: 'string', required: true, description: 'The image file to write; .png, .jpg or .jpeg' },
|
|
50
|
+
demo: { type: 'number', description: 'The demo to render, counted from 0 as in the shell URL (default 0)' },
|
|
51
|
+
viewport: { type: 'string', description: `The width: ${Object.entries(captureViewportWidths).map(([nameArg, widthArg]) => `${nameArg} (${widthArg})`).join(', ')} or pixels (default desktop)` },
|
|
52
|
+
height: { type: 'number', description: 'The height of the preview window in pixels (default 800)' },
|
|
53
|
+
theme: { type: 'string', description: 'dark or bright (default dark)' },
|
|
54
|
+
framing: { type: 'string', description: 'element, viewport or fullpage (default element; fullpage for pages)' },
|
|
55
|
+
scale: { type: 'number', description: 'Device pixel ratio, 1 or 2 (default 1)' },
|
|
56
|
+
quality: { type: 'number', description: 'JPEG quality from 1 to 100 (default 80)' },
|
|
57
|
+
},
|
|
58
|
+
}, async ({ args, options }) => {
|
|
59
|
+
const format = formatForFile(options.out);
|
|
60
|
+
const request = {
|
|
61
|
+
subject: parseSubject(args, options.demo),
|
|
62
|
+
viewport: parseViewport(options.viewport),
|
|
63
|
+
height: options.height,
|
|
64
|
+
theme: options.theme,
|
|
65
|
+
framing: options.framing,
|
|
66
|
+
scale: options.scale,
|
|
67
|
+
format,
|
|
68
|
+
quality: options.quality,
|
|
69
|
+
};
|
|
70
|
+
// Invalid options are refused before anything is bundled
|
|
71
|
+
resolveScreenshotRequest(request);
|
|
72
|
+
const outFile = plugins.path.resolve(options.out);
|
|
73
|
+
const devServer = new WccDevServer({ port: 0, cwd: process.cwd() });
|
|
74
|
+
// As for `wcctools dev`: SIGINT and SIGTERM stop the server and the capture browser once
|
|
75
|
+
plugins.smartexit.ProcessLifecycle.install({ shutdownTimeoutMs: devServer.getShutdownTimeoutMs() });
|
|
76
|
+
let result;
|
|
77
|
+
try {
|
|
78
|
+
await devServer.start();
|
|
79
|
+
result = await devServer.capture.captureScreenshot(request);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
await devServer.stop();
|
|
83
|
+
}
|
|
84
|
+
await plugins.fs.writeFile(outFile, Buffer.from(result.image.dataBase64, 'base64'));
|
|
85
|
+
const { selection, image } = result;
|
|
86
|
+
await out.log(`wcctools screenshot: wrote ${outFile} (${selection.sectionName}/${selection.itemName} demo ${selection.demoIndex}, ${result.framing}, ${image.width}×${image.height}${result.truncated ? ', cut at the maximum height' : ''})`);
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLnNjcmVlbnNob3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jbGkuc2NyZWVuc2hvdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdEQsT0FBTyxFQUFFLHNCQUFzQixFQUFFLHFCQUFxQixFQUFFLHdCQUF3QixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFN0csNENBQTRDO0FBQzVDLE1BQU0sWUFBWSxHQUFHLENBQUMsT0FBMEIsRUFBRSxPQUEyQixFQUF5QyxFQUFFO0lBQ3RILElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ3JELE1BQU0sSUFBSSxzQkFBc0IsQ0FBQyw2RUFBNkUsQ0FBQyxDQUFDO0lBQ2xILENBQUM7SUFDRCxNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsT0FBTyxDQUFDO0lBQzFCLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDbkMsTUFBTSxLQUFLLEdBQTBDLEtBQUssS0FBSyxDQUFDLENBQUM7UUFDL0QsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRTtRQUN2QixDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDakYsT0FBTyxPQUFPLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxLQUFLLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFB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
|
package/dist_ts/index.d.ts
CHANGED
package/dist_ts/index.js
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
1
|
export { runCli } from './cli.js';
|
|
2
|
-
|
|
3
|
-
export { WccDevApi } from './classes.devapi.js';
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxZQUFZLEVBQXdELE1BQU0sd0JBQXdCLENBQUM7QUFDNUcsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDIn0=
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sVUFBVSxDQUFDIn0=
|
package/dist_ts/plugins.d.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import * as childProcess from 'node:child_process';
|
|
2
2
|
import * as fs from 'node:fs/promises';
|
|
3
3
|
import * as fsSync from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
4
5
|
import * as path from 'node:path';
|
|
5
6
|
import * as url from 'node:url';
|
|
6
|
-
export { childProcess, fs, fsSync, path, url };
|
|
7
|
+
export { childProcess, fs, fsSync, os, path, url };
|
|
7
8
|
import * as typedrequest from '@api.global/typedrequest';
|
|
8
9
|
import * as typedserver from '@api.global/typedserver';
|
|
9
10
|
export { typedrequest, typedserver };
|
|
11
|
+
import * as deesDomtools from '@design.estate/dees-domtools';
|
|
12
|
+
export { deesDomtools };
|
|
10
13
|
import * as tswatch from '@git.zone/tswatch';
|
|
11
14
|
export { tswatch };
|
|
15
|
+
import * as smartbrowser from '@push.rocks/smartbrowser/automation';
|
|
12
16
|
import * as smartconfig from '@push.rocks/smartconfig';
|
|
13
17
|
import * as smartconsole from '@push.rocks/smartconsole';
|
|
14
18
|
import * as smartexit from '@push.rocks/smartexit';
|
|
15
|
-
export { smartconfig, smartconsole, smartexit };
|
|
19
|
+
export { smartbrowser, smartconfig, smartconsole, smartexit };
|
|
16
20
|
import typescript from 'typescript';
|
|
17
21
|
export { typescript };
|
|
18
22
|
import * as interfaces from '../dist_ts_interfaces/index.js';
|
package/dist_ts/plugins.js
CHANGED
|
@@ -2,21 +2,26 @@
|
|
|
2
2
|
import * as childProcess from 'node:child_process';
|
|
3
3
|
import * as fs from 'node:fs/promises';
|
|
4
4
|
import * as fsSync from 'node:fs';
|
|
5
|
+
import * as os from 'node:os';
|
|
5
6
|
import * as path from 'node:path';
|
|
6
7
|
import * as url from 'node:url';
|
|
7
|
-
export { childProcess, fs, fsSync, path, url };
|
|
8
|
+
export { childProcess, fs, fsSync, os, path, url };
|
|
8
9
|
// @api.global scope
|
|
9
10
|
import * as typedrequest from '@api.global/typedrequest';
|
|
10
11
|
import * as typedserver from '@api.global/typedserver';
|
|
11
12
|
export { typedrequest, typedserver };
|
|
13
|
+
// @design.estate scope
|
|
14
|
+
import * as deesDomtools from '@design.estate/dees-domtools';
|
|
15
|
+
export { deesDomtools };
|
|
12
16
|
// @git.zone scope
|
|
13
17
|
import * as tswatch from '@git.zone/tswatch';
|
|
14
18
|
export { tswatch };
|
|
15
19
|
// @push.rocks scope
|
|
20
|
+
import * as smartbrowser from '@push.rocks/smartbrowser/automation';
|
|
16
21
|
import * as smartconfig from '@push.rocks/smartconfig';
|
|
17
22
|
import * as smartconsole from '@push.rocks/smartconsole';
|
|
18
23
|
import * as smartexit from '@push.rocks/smartexit';
|
|
19
|
-
export { smartconfig, smartconsole, smartexit };
|
|
24
|
+
export { smartbrowser, smartconfig, smartconsole, smartexit };
|
|
20
25
|
// third party scope
|
|
21
26
|
import typescript from 'typescript';
|
|
22
27
|
export { typescript };
|
|
@@ -24,4 +29,4 @@ export { typescript };
|
|
|
24
29
|
import * as interfaces from '../dist_ts_interfaces/index.js';
|
|
25
30
|
import * as shared from '../dist_ts_shared/index.js';
|
|
26
31
|
export { interfaces, shared };
|
|
27
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
32
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxZQUFZLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUN2QyxPQUFPLEtBQUssTUFBTSxNQUFNLFNBQVMsQ0FBQztBQUNsQyxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEtBQUssR0FBRyxNQUFNLFVBQVUsQ0FBQztBQUVoQyxPQUFPLEVBQUUsWUFBWSxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUVuRCxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBRXZELE9BQU8sRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLENBQUM7QUFFckMsdUJBQXVCO0FBQ3ZCLE9BQU8sS0FBSyxZQUFZLE1BQU0sOEJBQThCLENBQUM7QUFFN0QsT0FBTyxFQUFFLFlBQVksRUFBRSxDQUFDO0FBRXhCLGtCQUFrQjtBQUNsQixPQUFPLEtBQUssT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBRTdDLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQztBQUVuQixvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFlBQVksTUFBTSxxQ0FBcUMsQ0FBQztBQUNwRSxPQUFPLEtBQUssV0FBVyxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxLQUFLLFNBQVMsTUFBTSx1QkFBdUIsQ0FBQztBQUVuRCxPQUFPLEVBQUUsWUFBWSxFQUFFLFdBQVcsRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFFOUQsb0JBQW9CO0FBQ3BCLE9BQU8sVUFBVSxNQUFNLFlBQVksQ0FBQztBQUVwQyxPQUFPLEVBQUUsVUFBVSxFQUFFLENBQUM7QUFFdEIsZ0JBQWdCO0FBQ2hCLE9BQU8sS0FBSyxVQUFVLE1BQU0sMkJBQTJCLENBQUM7QUFDeEQsT0FBTyxLQUFLLE1BQU0sTUFBTSx1QkFBdUIsQ0FBQztBQUVoRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxDQUFDIn0=
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { IWccSelection, TWccTheme } from './bridge.js';
|
|
2
|
+
/**
|
|
3
|
+
* Captures of the catalog's demos: screenshots and interaction frames the `wcctools dev` server
|
|
4
|
+
* takes in a headless browser, and frames the shell samples from a recording.
|
|
5
|
+
*/
|
|
6
|
+
/** The named capture widths: the dees-domtools breakpoints. */
|
|
7
|
+
export type TWccCaptureViewportPreset = 'phone' | 'phablet' | 'tablet' | 'desktop';
|
|
8
|
+
/** A named capture width, or a width in CSS pixels. */
|
|
9
|
+
export type TWccCaptureViewport = TWccCaptureViewportPreset | number;
|
|
10
|
+
/**
|
|
11
|
+
* What a capture shows:
|
|
12
|
+
* - `element`: the rendered instance of the selected element (the first one its demo renders),
|
|
13
|
+
* - `viewport`: the visible preview at the capture's width and height,
|
|
14
|
+
* - `fullpage`: the whole preview document.
|
|
15
|
+
*/
|
|
16
|
+
export type TWccCaptureFraming = 'element' | 'viewport' | 'fullpage';
|
|
17
|
+
export type TWccImageMimeType = 'image/png' | 'image/jpeg';
|
|
18
|
+
/** An encoded image. */
|
|
19
|
+
export interface IWccImage {
|
|
20
|
+
mimeType: TWccImageMimeType;
|
|
21
|
+
/** Width in device pixels. */
|
|
22
|
+
width: number;
|
|
23
|
+
/** Height in device pixels. */
|
|
24
|
+
height: number;
|
|
25
|
+
dataBase64: string;
|
|
26
|
+
}
|
|
27
|
+
/** A frame of a recording or of an observed interaction. */
|
|
28
|
+
export interface IWccTimedFrame extends IWccImage {
|
|
29
|
+
/** Milliseconds since the start of the recording or observation. */
|
|
30
|
+
timestampMs: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The catalog entry a capture renders. `itemName` is an entry's name or its element's tag;
|
|
34
|
+
* without `sectionName` it must name exactly one entry across all sections.
|
|
35
|
+
*/
|
|
36
|
+
export interface IWccCaptureSubject {
|
|
37
|
+
sectionName?: string;
|
|
38
|
+
itemName: string;
|
|
39
|
+
/** 0-based demo index; default 0. */
|
|
40
|
+
demoIndex?: number;
|
|
41
|
+
}
|
|
42
|
+
/** How the headless preview is set up for a capture. */
|
|
43
|
+
export interface IWccCaptureView {
|
|
44
|
+
/** Default `desktop`. */
|
|
45
|
+
viewport?: TWccCaptureViewport;
|
|
46
|
+
/** Height of the preview window in CSS pixels; default 800. */
|
|
47
|
+
height?: number;
|
|
48
|
+
/** Default `dark`. */
|
|
49
|
+
theme?: TWccTheme;
|
|
50
|
+
/** Device pixel ratio, 1 or 2; default 1. */
|
|
51
|
+
scale?: 1 | 2;
|
|
52
|
+
}
|
|
53
|
+
export interface IWccScreenshotRequest extends IWccCaptureView {
|
|
54
|
+
subject: IWccCaptureSubject;
|
|
55
|
+
/** Default `element` for element demos and `fullpage` for pages. */
|
|
56
|
+
framing?: TWccCaptureFraming;
|
|
57
|
+
/** Default `png`. */
|
|
58
|
+
format?: 'png' | 'jpeg';
|
|
59
|
+
/** JPEG quality from 1 to 100; default 80. */
|
|
60
|
+
quality?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface IWccScreenshotResult {
|
|
63
|
+
/** The demo the subject resolved to. */
|
|
64
|
+
selection: IWccSelection;
|
|
65
|
+
framing: TWccCaptureFraming;
|
|
66
|
+
image: IWccImage;
|
|
67
|
+
/** True when a full-page capture was cut at the maximum height. */
|
|
68
|
+
truncated: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* One step of a scripted interaction. A selector is a CSS selector matched in the preview
|
|
72
|
+
* document and in every open shadow root below it; the first match in document order is used,
|
|
73
|
+
* once it is visible and enabled.
|
|
74
|
+
*/
|
|
75
|
+
export type TWccInteractionStep = {
|
|
76
|
+
action: 'click';
|
|
77
|
+
selector: string;
|
|
78
|
+
} | {
|
|
79
|
+
action: 'hover';
|
|
80
|
+
selector: string;
|
|
81
|
+
}
|
|
82
|
+
/** Replaces the value of an input, textarea, select or contenteditable element. */
|
|
83
|
+
| {
|
|
84
|
+
action: 'fill';
|
|
85
|
+
selector: string;
|
|
86
|
+
text: string;
|
|
87
|
+
}
|
|
88
|
+
/** Presses a key (`Enter`, `Tab`, `a`, …), on the matched element when a selector is given. */
|
|
89
|
+
| {
|
|
90
|
+
action: 'press';
|
|
91
|
+
key: string;
|
|
92
|
+
selector?: string;
|
|
93
|
+
}
|
|
94
|
+
/** Waits until a match is visible, or until none is (`hidden`). */
|
|
95
|
+
| {
|
|
96
|
+
action: 'waitFor';
|
|
97
|
+
selector: string;
|
|
98
|
+
state?: 'visible' | 'hidden';
|
|
99
|
+
}
|
|
100
|
+
/** Observes for a fixed time, for example an animation. */
|
|
101
|
+
| {
|
|
102
|
+
action: 'wait';
|
|
103
|
+
ms: number;
|
|
104
|
+
};
|
|
105
|
+
export interface IWccInteractionRequest extends IWccCaptureView {
|
|
106
|
+
subject: IWccCaptureSubject;
|
|
107
|
+
steps: TWccInteractionStep[];
|
|
108
|
+
/** How long to keep observing after the last step, in milliseconds; default 500. */
|
|
109
|
+
observeMs?: number;
|
|
110
|
+
/** The most frames to return, spread over the observation; default 12. */
|
|
111
|
+
maxFrames?: number;
|
|
112
|
+
/** JPEG quality of the frames from 1 to 100; default 70. */
|
|
113
|
+
frameQuality?: number;
|
|
114
|
+
/** Also take a screenshot once the steps ran, framed as a screenshot request would be. */
|
|
115
|
+
finalScreenshot?: {
|
|
116
|
+
framing?: TWccCaptureFraming;
|
|
117
|
+
format?: 'png' | 'jpeg';
|
|
118
|
+
quality?: number;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export interface IWccInteractionStepResult {
|
|
122
|
+
index: number;
|
|
123
|
+
ok: boolean;
|
|
124
|
+
/** Why the step failed; the steps after a failed one do not run. */
|
|
125
|
+
error?: string;
|
|
126
|
+
/** Milliseconds since the start of the observation. */
|
|
127
|
+
startMs: number;
|
|
128
|
+
endMs: number;
|
|
129
|
+
}
|
|
130
|
+
export interface IWccInteractionResult {
|
|
131
|
+
selection: IWccSelection;
|
|
132
|
+
/** Viewport frames in time order, timestamped from the start of the observation. */
|
|
133
|
+
frames: IWccTimedFrame[];
|
|
134
|
+
steps: IWccInteractionStepResult[];
|
|
135
|
+
/** True when every step ran. */
|
|
136
|
+
completed: boolean;
|
|
137
|
+
finalImage?: IWccImage;
|
|
138
|
+
}
|
|
139
|
+
/** How frames are sampled from a recording. */
|
|
140
|
+
export interface IWccFrameSamplingOptions {
|
|
141
|
+
/** Number of frames, spread evenly over the range; default 8, at most 32. */
|
|
142
|
+
count?: number;
|
|
143
|
+
/** Start of the range in seconds; default the start of the recording. */
|
|
144
|
+
startS?: number;
|
|
145
|
+
/** End of the range in seconds; default the end of the recording. */
|
|
146
|
+
endS?: number;
|
|
147
|
+
/** Frames wider than this are scaled down; default 960. */
|
|
148
|
+
maxWidth?: number;
|
|
149
|
+
/** JPEG quality from 0 to 1; default 0.8. */
|
|
150
|
+
quality?: number;
|
|
151
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './catalog.js';
|
|
2
2
|
export * from './bridge.js';
|
|
3
|
+
export * from './capture.js';
|
|
3
4
|
export * from './requests.js';
|
|
4
5
|
export * from './standard.js';
|
|
5
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19pbnRlcmZhY2VzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsY0FBYyxDQUFDO0FBQzdCLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsY0FBYyxDQUFDO0FBQzdCLGNBQWMsZUFBZSxDQUFDO0FBQzlCLGNBQWMsZUFBZSxDQUFDIn0=
|