@design.estate/wcctools 6.2.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.
Files changed (67) hide show
  1. package/changelog.md +21 -0
  2. package/dist_shell/bundle.js +4 -4
  3. package/dist_shell/bundle.js.map +1 -1
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/capture/classes.capturebrowser.d.ts +65 -0
  6. package/dist_ts/capture/classes.capturebrowser.js +251 -0
  7. package/dist_ts/capture/classes.captureservice.d.ts +39 -0
  8. package/dist_ts/capture/classes.captureservice.js +119 -0
  9. package/dist_ts/capture/classes.screencast.d.ts +47 -0
  10. package/dist_ts/capture/classes.screencast.js +127 -0
  11. package/dist_ts/capture/errors.d.ts +14 -0
  12. package/dist_ts/capture/errors.js +23 -0
  13. package/dist_ts/capture/images.d.ts +3 -0
  14. package/dist_ts/capture/images.js +58 -0
  15. package/dist_ts/capture/index.d.ts +4 -0
  16. package/dist_ts/capture/index.js +5 -0
  17. package/dist_ts/capture/interaction.d.ts +11 -0
  18. package/dist_ts/capture/interaction.js +138 -0
  19. package/dist_ts/capture/navigationguard.d.ts +44 -0
  20. package/dist_ts/capture/navigationguard.js +129 -0
  21. package/dist_ts/capture/previewpage.d.ts +29 -0
  22. package/dist_ts/capture/previewpage.js +96 -0
  23. package/dist_ts/capture/request.d.ts +69 -0
  24. package/dist_ts/capture/request.js +239 -0
  25. package/dist_ts/classes.devserver.d.ts +25 -9
  26. package/dist_ts/classes.devserver.js +45 -14
  27. package/dist_ts/classes.hostpolicy.d.ts +63 -0
  28. package/dist_ts/classes.hostpolicy.js +171 -0
  29. package/dist_ts/cli.js +25 -3
  30. package/dist_ts/cli.screenshot.d.ts +6 -0
  31. package/dist_ts/cli.screenshot.js +89 -0
  32. package/dist_ts/plugins.d.ts +6 -2
  33. package/dist_ts/plugins.js +8 -3
  34. package/dist_ts_interfaces/capture.d.ts +151 -0
  35. package/dist_ts_interfaces/capture.js +2 -0
  36. package/dist_ts_interfaces/index.d.ts +1 -0
  37. package/dist_ts_interfaces/index.js +2 -1
  38. package/dist_ts_interfaces/requests.d.ts +24 -0
  39. package/dist_ts_shell/elements/wcc-recording-panel.d.ts +9 -0
  40. package/dist_ts_shell/elements/wcc-recording-panel.js +12 -1
  41. package/dist_ts_shell/services/framesampler.service.d.ts +23 -0
  42. package/dist_ts_shell/services/framesampler.service.js +101 -0
  43. package/dist_ts_web/00_commitinfo_data.js +1 -1
  44. package/package.json +2 -1
  45. package/readme.md +104 -6
  46. package/ts/00_commitinfo_data.ts +1 -1
  47. package/ts/capture/classes.capturebrowser.ts +290 -0
  48. package/ts/capture/classes.captureservice.ts +152 -0
  49. package/ts/capture/classes.screencast.ts +152 -0
  50. package/ts/capture/errors.ts +25 -0
  51. package/ts/capture/images.ts +61 -0
  52. package/ts/capture/index.ts +4 -0
  53. package/ts/capture/interaction.ts +146 -0
  54. package/ts/capture/navigationguard.ts +147 -0
  55. package/ts/capture/previewpage.ts +130 -0
  56. package/ts/capture/request.ts +304 -0
  57. package/ts/classes.devserver.ts +55 -13
  58. package/ts/classes.hostpolicy.ts +201 -0
  59. package/ts/cli.screenshot.ts +95 -0
  60. package/ts/cli.ts +25 -2
  61. package/ts/plugins.ts +9 -2
  62. package/ts_interfaces/capture.ts +144 -0
  63. package/ts_interfaces/index.ts +1 -0
  64. package/ts_interfaces/requests.ts +39 -0
  65. package/ts_shell/elements/wcc-recording-panel.ts +12 -0
  66. package/ts_shell/services/framesampler.service.ts +112 -0
  67. package/ts_web/00_commitinfo_data.ts +1 -1
@@ -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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
@@ -1,4 +1,6 @@
1
1
  import { WccDevApi } from './classes.devapi.js';
2
+ import { WccHostPolicy } from './classes.hostpolicy.js';
3
+ import { WccCaptureService } from './capture/index.js';
2
4
  export interface IWccDevServerOptions {
3
5
  /** Port to listen on; overrides the tswatch configuration. 0 picks a free port. */
4
6
  port?: number;
@@ -7,11 +9,22 @@ export interface IWccDevServerOptions {
7
9
  * tswatch bundles and watches in it (default: `process.cwd()` when the server is constructed).
8
10
  */
9
11
  cwd?: string;
12
+ /**
13
+ * The interface address to listen on (default `127.0.0.1`, this machine only). `0.0.0.0` or
14
+ * `::` listen on every interface; the machine's interface addresses are then allowed hosts.
15
+ */
16
+ host?: string;
17
+ /** Further hostnames or addresses the server answers, besides localhost and the listen address. */
18
+ allowedHosts?: readonly string[];
10
19
  }
11
20
  export interface IWccDevServerAddress {
12
21
  port: number;
13
22
  /** The shell's address. */
14
23
  url: string;
24
+ /** The interface address the server listens on. */
25
+ host: string;
26
+ /** The hostnames the server answers; requests for any other Host are refused. */
27
+ allowedHosts: string[];
15
28
  }
16
29
  /**
17
30
  * `wcctools dev`: bundles the catalog of a project with tswatch, as its `@git.zone/tswatch`
@@ -20,7 +33,12 @@ export interface IWccDevServerAddress {
20
33
  * - the shell on `/wcctools/` (`/` redirects there) and its routes `/wcctools-route/...`,
21
34
  * - the typed API on `/wcctools/typedrequest`, including the bundles' status,
22
35
  * - the catalog's preview document and bundle from the tswatch serve directory on every other
23
- * path, with live reload that reloads only the preview document.
36
+ * path, with live reload that reloads only the preview document,
37
+ * - captures of single demos in a headless browser (`capture`, also on the typed API).
38
+ *
39
+ * It listens on loopback unless told otherwise and admits requests as its `WccHostPolicy`
40
+ * describes: only the hosts it serves, and state-changing requests and WebSockets only from its
41
+ * own origin.
24
42
  *
25
43
  * The server owns no process lifecycle: while it runs, a process shutdown through smartexit's
26
44
  * `ProcessLifecycle` stops it. The entry point installs that lifecycle with
@@ -30,6 +48,10 @@ export declare class WccDevServer {
30
48
  private readonly options;
31
49
  /** The typed API on `/wcctools/typedrequest`; register further handlers before `start()`. */
32
50
  readonly api: WccDevApi;
51
+ /** Screenshots and interaction captures of the served catalog; available while the server runs. */
52
+ readonly capture: WccCaptureService;
53
+ /** Who may talk to the server: the hosts it answers and the origin of its requests. */
54
+ readonly hostPolicy: WccHostPolicy;
33
55
  /** The project directory everything resolves against. */
34
56
  readonly cwd: string;
35
57
  /** The project's tswatch configuration, or the `element` preset; read once, on construction. */
@@ -64,14 +86,8 @@ export declare class WccDevServer {
64
86
  private startServing;
65
87
  private getElementPreset;
66
88
  /**
67
- * The shell: the prebuilt shell bundle and the typed API.
68
- *
69
- * Seam for side-effecting APIs (assistant, capture, standards fixes): before any handler with
70
- * side effects is registered here, the server binds to localhost by default (another interface
71
- * only on explicit request), validates the Host header against the addresses it serves instead
72
- * of accepting any hostname, and admits typedsocket connections and typed requests only from the
73
- * shell's own origin (a `websocketAdmission`/`requestAdmission`), keeping surface mode's strict
74
- * authority validation. Until then every handler here is read-only.
89
+ * The shell: the prebuilt shell bundle and the typed API, whose requests (captures among them)
90
+ * are admitted only from the server's own origin.
75
91
  */
76
92
  private createShellSurface;
77
93
  /**