@godscene/web 1.7.11

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 (129) hide show
  1. package/README.md +7 -0
  2. package/bin/midscene-playground +3 -0
  3. package/bin/midscene-web +2 -0
  4. package/dist/es/bin.mjs +14 -0
  5. package/dist/es/bridge-mode/agent-cli-side.mjs +135 -0
  6. package/dist/es/bridge-mode/browser.mjs +2 -0
  7. package/dist/es/bridge-mode/common.mjs +41 -0
  8. package/dist/es/bridge-mode/index.mjs +4 -0
  9. package/dist/es/bridge-mode/io-client.mjs +99 -0
  10. package/dist/es/bridge-mode/io-server.mjs +218 -0
  11. package/dist/es/bridge-mode/page-browser-side.mjs +119 -0
  12. package/dist/es/cdp-proxy-constants.mjs +7 -0
  13. package/dist/es/cdp-proxy-manager.mjs +217 -0
  14. package/dist/es/cdp-proxy.mjs +151 -0
  15. package/dist/es/cdp-target-store.mjs +26 -0
  16. package/dist/es/chrome-extension/agent.mjs +8 -0
  17. package/dist/es/chrome-extension/cdpInput.mjs +172 -0
  18. package/dist/es/chrome-extension/cdpInput.mjs.LICENSE.txt +5 -0
  19. package/dist/es/chrome-extension/dynamic-scripts.mjs +36 -0
  20. package/dist/es/chrome-extension/index.mjs +5 -0
  21. package/dist/es/chrome-extension/page.mjs +733 -0
  22. package/dist/es/cli-options.mjs +97 -0
  23. package/dist/es/cli.mjs +26 -0
  24. package/dist/es/common/cache-helper.mjs +26 -0
  25. package/dist/es/common/viewport.mjs +36 -0
  26. package/dist/es/index.mjs +8 -0
  27. package/dist/es/mcp-server.mjs +33 -0
  28. package/dist/es/mcp-tools-cdp.mjs +164 -0
  29. package/dist/es/mcp-tools-puppeteer.mjs +246 -0
  30. package/dist/es/mcp-tools.mjs +81 -0
  31. package/dist/es/platform.mjs +37 -0
  32. package/dist/es/playwright/ai-fixture.mjs +364 -0
  33. package/dist/es/playwright/index.mjs +36 -0
  34. package/dist/es/playwright/page.mjs +42 -0
  35. package/dist/es/playwright/reporter/index.mjs +178 -0
  36. package/dist/es/puppeteer/agent-launcher.mjs +172 -0
  37. package/dist/es/puppeteer/base-page.mjs +830 -0
  38. package/dist/es/puppeteer/index.mjs +35 -0
  39. package/dist/es/puppeteer/page.mjs +7 -0
  40. package/dist/es/static/index.mjs +3 -0
  41. package/dist/es/static/static-agent.mjs +10 -0
  42. package/dist/es/static/static-page.mjs +123 -0
  43. package/dist/es/utils.mjs +6 -0
  44. package/dist/es/web-element.mjs +57 -0
  45. package/dist/es/web-page.mjs +272 -0
  46. package/dist/lib/bin.js +20 -0
  47. package/dist/lib/bridge-mode/agent-cli-side.js +172 -0
  48. package/dist/lib/bridge-mode/browser.js +36 -0
  49. package/dist/lib/bridge-mode/common.js +105 -0
  50. package/dist/lib/bridge-mode/index.js +44 -0
  51. package/dist/lib/bridge-mode/io-client.js +133 -0
  52. package/dist/lib/bridge-mode/io-server.js +255 -0
  53. package/dist/lib/bridge-mode/page-browser-side.js +163 -0
  54. package/dist/lib/cdp-proxy-constants.js +50 -0
  55. package/dist/lib/cdp-proxy-manager.js +273 -0
  56. package/dist/lib/cdp-proxy.js +179 -0
  57. package/dist/lib/cdp-target-store.js +66 -0
  58. package/dist/lib/chrome-extension/agent.js +42 -0
  59. package/dist/lib/chrome-extension/cdpInput.js +206 -0
  60. package/dist/lib/chrome-extension/cdpInput.js.LICENSE.txt +5 -0
  61. package/dist/lib/chrome-extension/dynamic-scripts.js +86 -0
  62. package/dist/lib/chrome-extension/index.js +58 -0
  63. package/dist/lib/chrome-extension/page.js +767 -0
  64. package/dist/lib/cli-options.js +131 -0
  65. package/dist/lib/cli.js +54 -0
  66. package/dist/lib/common/cache-helper.js +66 -0
  67. package/dist/lib/common/viewport.js +88 -0
  68. package/dist/lib/index.js +66 -0
  69. package/dist/lib/mcp-server.js +73 -0
  70. package/dist/lib/mcp-tools-cdp.js +208 -0
  71. package/dist/lib/mcp-tools-puppeteer.js +296 -0
  72. package/dist/lib/mcp-tools.js +115 -0
  73. package/dist/lib/platform.js +71 -0
  74. package/dist/lib/playwright/ai-fixture.js +401 -0
  75. package/dist/lib/playwright/index.js +89 -0
  76. package/dist/lib/playwright/page.js +76 -0
  77. package/dist/lib/playwright/reporter/index.js +212 -0
  78. package/dist/lib/puppeteer/agent-launcher.js +240 -0
  79. package/dist/lib/puppeteer/base-page.js +876 -0
  80. package/dist/lib/puppeteer/index.js +85 -0
  81. package/dist/lib/puppeteer/page.js +41 -0
  82. package/dist/lib/static/index.js +50 -0
  83. package/dist/lib/static/static-agent.js +44 -0
  84. package/dist/lib/static/static-page.js +157 -0
  85. package/dist/lib/utils.js +38 -0
  86. package/dist/lib/web-element.js +94 -0
  87. package/dist/lib/web-page.js +322 -0
  88. package/dist/types/bin.d.ts +1 -0
  89. package/dist/types/bridge-mode/agent-cli-side.d.ts +49 -0
  90. package/dist/types/bridge-mode/browser.d.ts +2 -0
  91. package/dist/types/bridge-mode/common.d.ts +74 -0
  92. package/dist/types/bridge-mode/index.d.ts +4 -0
  93. package/dist/types/bridge-mode/io-client.d.ts +10 -0
  94. package/dist/types/bridge-mode/io-server.d.ts +27 -0
  95. package/dist/types/bridge-mode/page-browser-side.d.ts +21 -0
  96. package/dist/types/cdp-proxy-constants.d.ts +4 -0
  97. package/dist/types/cdp-proxy-manager.d.ts +53 -0
  98. package/dist/types/cdp-proxy.d.ts +37 -0
  99. package/dist/types/cdp-target-store.d.ts +26 -0
  100. package/dist/types/chrome-extension/agent.d.ts +4 -0
  101. package/dist/types/chrome-extension/cdpInput.d.ts +52 -0
  102. package/dist/types/chrome-extension/dynamic-scripts.d.ts +3 -0
  103. package/dist/types/chrome-extension/index.d.ts +5 -0
  104. package/dist/types/chrome-extension/page.d.ts +120 -0
  105. package/dist/types/cli-options.d.ts +8 -0
  106. package/dist/types/cli.d.ts +1 -0
  107. package/dist/types/common/cache-helper.d.ts +20 -0
  108. package/dist/types/common/viewport.d.ts +17 -0
  109. package/dist/types/index.d.ts +9 -0
  110. package/dist/types/mcp-server.d.ts +26 -0
  111. package/dist/types/mcp-tools-cdp.d.ts +23 -0
  112. package/dist/types/mcp-tools-puppeteer.d.ts +23 -0
  113. package/dist/types/mcp-tools.d.ts +14 -0
  114. package/dist/types/platform.d.ts +10 -0
  115. package/dist/types/playwright/ai-fixture.d.ts +133 -0
  116. package/dist/types/playwright/index.d.ts +13 -0
  117. package/dist/types/playwright/page.d.ts +11 -0
  118. package/dist/types/playwright/reporter/index.d.ts +28 -0
  119. package/dist/types/puppeteer/agent-launcher.d.ts +59 -0
  120. package/dist/types/puppeteer/base-page.d.ts +123 -0
  121. package/dist/types/puppeteer/index.d.ts +11 -0
  122. package/dist/types/puppeteer/page.d.ts +6 -0
  123. package/dist/types/static/index.d.ts +2 -0
  124. package/dist/types/static/static-agent.d.ts +5 -0
  125. package/dist/types/static/static-page.d.ts +46 -0
  126. package/dist/types/utils.d.ts +6 -0
  127. package/dist/types/web-element.d.ts +48 -0
  128. package/dist/types/web-page.d.ts +69 -0
  129. package/package.json +173 -0
@@ -0,0 +1,876 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ forceClosePopup: ()=>forceClosePopup,
28
+ Page: ()=>Page,
29
+ forceChromeSelectRendering: ()=>forceChromeSelectRendering,
30
+ BROWSER_NAVIGATION_ERROR_PATTERN: ()=>BROWSER_NAVIGATION_ERROR_PATTERN,
31
+ debugPage: ()=>debugPage
32
+ });
33
+ const utils_namespaceObject = require("@godscene/core/utils");
34
+ const constants_namespaceObject = require("@godscene/shared/constants");
35
+ const extractor_namespaceObject = require("@godscene/shared/extractor");
36
+ const img_namespaceObject = require("@godscene/shared/img");
37
+ const logger_namespaceObject = require("@godscene/shared/logger");
38
+ const node_namespaceObject = require("@godscene/shared/node");
39
+ const shared_utils_namespaceObject = require("@godscene/shared/utils");
40
+ const cache_helper_js_namespaceObject = require("../common/cache-helper.js");
41
+ const external_web_page_js_namespaceObject = require("../web-page.js");
42
+ function _define_property(obj, key, value) {
43
+ if (key in obj) Object.defineProperty(obj, key, {
44
+ value: value,
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true
48
+ });
49
+ else obj[key] = value;
50
+ return obj;
51
+ }
52
+ const debugPage = (0, logger_namespaceObject.getDebug)('web:page');
53
+ const warnPage = (0, logger_namespaceObject.getDebug)('web:page', {
54
+ console: true
55
+ });
56
+ const BROWSER_NAVIGATION_ERROR_PATTERN = /execution context was destroyed|frame was detached|target closed|page has been closed|context was destroyed|net::ERR_ABORTED/i;
57
+ const CDP_SCREENCAST_QUALITY = 70;
58
+ const CDP_SCREENCAST_EVERY_NTH_FRAME = 1;
59
+ const FLUSH_VISUAL_UPDATE_TIMEOUT_MS = 50;
60
+ const DATA_URL_BASE64_PREFIX = /^data:image\/\w+;base64,/;
61
+ function isClosedPageError(error) {
62
+ if (!(error instanceof Error)) return false;
63
+ return /target page, context or browser has been closed|page has been closed|target closed|browser has been closed/i.test(error.message);
64
+ }
65
+ class Page {
66
+ actionSpace() {
67
+ const defaultActions = (0, external_web_page_js_namespaceObject.commonWebActionsForWebPage)(this, this.enableTouchEventsInActionSpace);
68
+ const customActions = this.customActions || [];
69
+ return [
70
+ ...defaultActions,
71
+ ...customActions
72
+ ];
73
+ }
74
+ async evaluate(pageFunction, arg) {
75
+ let result;
76
+ debugPage('evaluate function begin');
77
+ this.interfaceType, result = await this.underlyingPage.evaluate(pageFunction, arg);
78
+ debugPage('evaluate function end');
79
+ return result;
80
+ }
81
+ async evaluateJavaScript(script) {
82
+ return this.evaluate(script);
83
+ }
84
+ async waitForNavigation(moment, actionName) {
85
+ if (0 === this.waitForNavigationTimeout) return void debugPage('waitForNavigation timeout is 0, skip waiting');
86
+ if ('puppeteer' === this.interfaceType || 'playwright' === this.interfaceType) {
87
+ debugPage(`waitForNavigation begin at moment ${moment} with timeout: ${this.waitForNavigationTimeout} and actionName: ${actionName}`);
88
+ try {
89
+ await this.underlyingPage.waitForSelector('html', {
90
+ timeout: this.waitForNavigationTimeout
91
+ });
92
+ } catch (error) {
93
+ console.warn('[midscene:warning] Waiting for the "navigation" has timed out, but Midscene will continue execution. Please check https://midscenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout');
94
+ }
95
+ debugPage('waitForNavigation end');
96
+ }
97
+ }
98
+ async waitForNetworkIdle(moment, actionName) {
99
+ if ('puppeteer' === this.interfaceType) {
100
+ if (0 === this.waitForNetworkIdleTimeout) return void debugPage('waitForNetworkIdle timeout is 0, skip waiting');
101
+ debugPage(`waitForNetworkIdle begin at moment ${moment} with timeout: ${this.waitForNetworkIdleTimeout} and concurrency: ${constants_namespaceObject.DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY} and actionName: ${actionName}`);
102
+ try {
103
+ await this.underlyingPage.waitForNetworkIdle({
104
+ idleTime: 200,
105
+ concurrency: constants_namespaceObject.DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY,
106
+ timeout: this.waitForNetworkIdleTimeout
107
+ });
108
+ } catch (error) {
109
+ console.warn('[midscene:warning] Waiting for the "network idle" has timed out, but Midscene will continue execution. Please check https://midscenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout');
110
+ }
111
+ debugPage('waitForNetworkIdle end');
112
+ } else if (!this.playwrightNetworkIdleWarningShown) {
113
+ this.playwrightNetworkIdleWarningShown = true;
114
+ warnPage('[midscene:warning] waitForNetworkIdle is skipped for Playwright. Playwright does not provide an equivalent underlying capability for the intended post-action network idle behavior here.');
115
+ }
116
+ }
117
+ async getElementsInfo() {
118
+ await this.waitForNavigation('getElementsInfo');
119
+ debugPage('getElementsInfo begin');
120
+ const tree = await this.getElementsNodeTree();
121
+ debugPage('getElementsInfo end');
122
+ return (0, extractor_namespaceObject.treeToList)(tree);
123
+ }
124
+ async getXpathsByPoint(point, isOrderSensitive) {
125
+ const elementInfosScriptContent = (0, node_namespaceObject.getElementInfosScriptContent)();
126
+ return this.evaluateJavaScript(`${elementInfosScriptContent}midscene_element_inspector.getXpathsByPoint({left: ${point.left}, top: ${point.top}}, ${isOrderSensitive})`);
127
+ }
128
+ async getElementFromPoint(args) {
129
+ const data = await this.evaluate(`window.midscene_element_inspector.webGetElementFromPoint({x:${args.x}, y:${args.y} })`);
130
+ return data;
131
+ }
132
+ async getElementInfoByXpath(xpath) {
133
+ const elementInfosScriptContent = (0, node_namespaceObject.getElementInfosScriptContent)();
134
+ return this.evaluateJavaScript(`${elementInfosScriptContent}midscene_element_inspector.getElementInfoByXpath(${JSON.stringify(xpath)})`);
135
+ }
136
+ async cacheFeatureForPoint(center, options) {
137
+ const point = {
138
+ left: center[0],
139
+ top: center[1]
140
+ };
141
+ try {
142
+ const isOrderSensitive = await (0, cache_helper_js_namespaceObject.judgeOrderSensitive)(options, debugPage);
143
+ const xpaths = await this.getXpathsByPoint(point, isOrderSensitive);
144
+ const sanitized = (0, cache_helper_js_namespaceObject.sanitizeXpaths)(xpaths);
145
+ if (!sanitized.length) debugPage('cacheFeatureForPoint: no xpath found at point %o', center);
146
+ return {
147
+ xpaths: sanitized
148
+ };
149
+ } catch (error) {
150
+ debugPage('cacheFeatureForPoint failed: %s', error);
151
+ return {
152
+ xpaths: []
153
+ };
154
+ }
155
+ }
156
+ async rectMatchesCacheFeature(feature) {
157
+ const xpaths = (0, cache_helper_js_namespaceObject.sanitizeXpaths)(feature.xpaths);
158
+ debugPage('rectMatchesCacheFeature: trying %d xpath(s)', xpaths.length);
159
+ for (const xpath of xpaths)try {
160
+ debugPage('rectMatchesCacheFeature: evaluating xpath: %s', xpath);
161
+ const elementInfo = await this.getElementInfoByXpath(xpath);
162
+ if (elementInfo?.rect) {
163
+ debugPage('rectMatchesCacheFeature: found element, rect: %o', elementInfo.rect);
164
+ return (0, cache_helper_js_namespaceObject.buildRectFromElementInfo)(elementInfo);
165
+ }
166
+ debugPage('rectMatchesCacheFeature: element found but no rect (elementInfo: %o)', elementInfo);
167
+ } catch (error) {
168
+ debugPage('rectMatchesCacheFeature failed for xpath %s: %s', xpath, error);
169
+ }
170
+ throw new Error(`No matching element rect found for the provided cache feature (tried ${xpaths.length} xpath(s): ${xpaths.join(', ')})`);
171
+ }
172
+ async getElementsNodeTree() {
173
+ await this.waitForNavigation('getElementsNodeTree');
174
+ const scripts = await (0, node_namespaceObject.getExtraReturnLogic)(true);
175
+ (0, shared_utils_namespaceObject.assert)(scripts, "scripts should be set before writing report in browser");
176
+ const startTime = Date.now();
177
+ const captureElementSnapshot = await this.evaluate(scripts);
178
+ const sizeInfo = await this.evaluate(()=>({
179
+ width: document.documentElement.clientWidth,
180
+ height: document.documentElement.clientHeight,
181
+ dpr: window.devicePixelRatio
182
+ }));
183
+ this.viewportSize = sizeInfo;
184
+ const endTime = Date.now();
185
+ debugPage(`getElementsNodeTree end, cost: ${endTime - startTime}ms`);
186
+ return captureElementSnapshot;
187
+ }
188
+ async size() {
189
+ const sizeInfo = await this.evaluate(()=>({
190
+ width: window.innerWidth,
191
+ height: window.innerHeight
192
+ }));
193
+ this.viewportSize = sizeInfo;
194
+ return sizeInfo;
195
+ }
196
+ async screenshotBase64() {
197
+ const imgType = 'jpeg';
198
+ const quality = 90;
199
+ const startTime = Date.now();
200
+ debugPage('screenshotBase64 begin');
201
+ let base64;
202
+ if ('puppeteer' === this.interfaceType) {
203
+ const result = await this.underlyingPage.screenshot({
204
+ type: imgType,
205
+ quality,
206
+ encoding: 'base64'
207
+ });
208
+ base64 = (0, img_namespaceObject.createImgBase64ByFormat)(imgType, result);
209
+ } else if ('playwright' === this.interfaceType) {
210
+ const page = this.underlyingPage;
211
+ try {
212
+ const buffer = await page.screenshot({
213
+ type: imgType,
214
+ quality,
215
+ timeout: 10000
216
+ });
217
+ base64 = (0, img_namespaceObject.createImgBase64ByFormat)(imgType, buffer.toString('base64'));
218
+ } catch (error) {
219
+ if (isClosedPageError(error) || page.isClosed()) throw error;
220
+ const errorMsg = error instanceof Error ? error.message : String(error);
221
+ console.warn(`[Midscene] Playwright screenshot failed: ${errorMsg}. Falling back to CDP screenshot.`);
222
+ debugPage('playwright screenshot failed, trying CDP fallback: %s', error);
223
+ base64 = await this.screenshotBase64ByPlaywrightCdp(page, imgType, quality);
224
+ }
225
+ } else throw new Error('Unsupported page type for screenshot');
226
+ const endTime = Date.now();
227
+ debugPage(`screenshotBase64 end, cost: ${endTime - startTime}ms`);
228
+ return base64;
229
+ }
230
+ async screenshotBase64ByPlaywrightCdp(page, imgType, quality) {
231
+ const browserName = page.context().browser()?.browserType().name();
232
+ if (browserName && 'chromium' !== browserName) throw new Error(`CDP screenshot fallback requires Chromium-based browser, but current browser is "${browserName}".`);
233
+ const client = await page.context().newCDPSession(page);
234
+ try {
235
+ const result = await client.send('Page.captureScreenshot', {
236
+ format: imgType,
237
+ ...quality ? {
238
+ quality
239
+ } : {}
240
+ });
241
+ return (0, img_namespaceObject.createImgBase64ByFormat)(imgType, result.data);
242
+ } finally{
243
+ await client.detach().catch(()=>{});
244
+ }
245
+ }
246
+ async createScreencastCdpSession() {
247
+ if ('puppeteer' === this.interfaceType) {
248
+ const page = this.underlyingPage;
249
+ return await page.target().createCDPSession();
250
+ }
251
+ const page = this.underlyingPage;
252
+ const browserName = page.context().browser()?.browserType().name();
253
+ if (browserName && 'chromium' !== browserName) throw new Error(`CDP screencast requires Chromium-based browser, but current browser is "${browserName}".`);
254
+ return await page.context().newCDPSession(page);
255
+ }
256
+ async flushPendingVisualUpdate() {
257
+ const activeStream = this.activeMjpegStream;
258
+ if (!activeStream) return;
259
+ try {
260
+ await this.evaluate((timeoutMs)=>new Promise((resolve)=>{
261
+ let done = false;
262
+ const finish = ()=>{
263
+ if (done) return;
264
+ done = true;
265
+ resolve();
266
+ };
267
+ setTimeout(finish, timeoutMs);
268
+ requestAnimationFrame(()=>requestAnimationFrame(finish));
269
+ }), FLUSH_VISUAL_UPDATE_TIMEOUT_MS);
270
+ const dataUrl = await this.screenshotBase64();
271
+ if (this.activeMjpegStream?.token !== activeStream.token) return;
272
+ activeStream.onFrame({
273
+ data: dataUrl.replace(DATA_URL_BASE64_PREFIX, ''),
274
+ contentType: 'image/jpeg'
275
+ });
276
+ } catch (error) {
277
+ debugPage('screencast visual refresh failed: %s', error);
278
+ activeStream.onError?.(error);
279
+ }
280
+ }
281
+ async startMjpegStream(options) {
282
+ const { signal, onFrame, onError } = options;
283
+ if ('function' == typeof this.underlyingPage.bringToFront) await this.underlyingPage.bringToFront();
284
+ const client = await this.createScreencastCdpSession();
285
+ let stopped = false;
286
+ const streamToken = Symbol('mjpeg-stream');
287
+ const reportStreamError = (error)=>{
288
+ try {
289
+ onError?.(error);
290
+ } catch (callbackError) {
291
+ debugPage('mjpeg onError callback threw: %s', callbackError);
292
+ }
293
+ };
294
+ const handleFrame = (event)=>{
295
+ (async ()=>{
296
+ if (stopped) return;
297
+ try {
298
+ onFrame({
299
+ data: event.data,
300
+ contentType: 'image/jpeg'
301
+ });
302
+ } catch (error) {
303
+ reportStreamError(error);
304
+ }
305
+ try {
306
+ await client.send('Page.screencastFrameAck', {
307
+ sessionId: event.sessionId
308
+ });
309
+ } catch (error) {
310
+ if (!stopped) reportStreamError(error);
311
+ }
312
+ })();
313
+ };
314
+ const removeFrameListener = ()=>{
315
+ if (client.off) client.off('Page.screencastFrame', handleFrame);
316
+ else if (client.removeListener) client.removeListener('Page.screencastFrame', handleFrame);
317
+ };
318
+ const stop = async ()=>{
319
+ if (stopped) return;
320
+ stopped = true;
321
+ if (this.activeMjpegStream?.token === streamToken) this.activeMjpegStream = void 0;
322
+ signal?.removeEventListener('abort', abortHandler);
323
+ removeFrameListener();
324
+ await client.send('Page.stopScreencast').catch((error)=>{
325
+ debugPage('Page.stopScreencast failed: %s', error);
326
+ });
327
+ await client.detach().catch((error)=>{
328
+ debugPage('CDP screencast session detach failed: %s', error);
329
+ });
330
+ };
331
+ const abortHandler = ()=>{
332
+ stop();
333
+ };
334
+ try {
335
+ client.on('Page.screencastFrame', handleFrame);
336
+ this.activeMjpegStream = {
337
+ token: streamToken,
338
+ onFrame,
339
+ onError
340
+ };
341
+ signal?.addEventListener('abort', abortHandler, {
342
+ once: true
343
+ });
344
+ if (signal?.aborted) {
345
+ await stop();
346
+ return {
347
+ stop
348
+ };
349
+ }
350
+ await client.send('Page.enable');
351
+ try {
352
+ const { width, height } = await this.size();
353
+ await client.send('Emulation.setVisibleSize', {
354
+ width,
355
+ height
356
+ });
357
+ } catch (error) {
358
+ debugPage('CDP screencast visible size sync failed: %s', error);
359
+ }
360
+ await client.send('Page.startScreencast', {
361
+ format: 'jpeg',
362
+ quality: CDP_SCREENCAST_QUALITY,
363
+ everyNthFrame: CDP_SCREENCAST_EVERY_NTH_FRAME
364
+ });
365
+ return {
366
+ stop
367
+ };
368
+ } catch (error) {
369
+ await stop();
370
+ throw error;
371
+ }
372
+ }
373
+ async url() {
374
+ return this.underlyingPage.url();
375
+ }
376
+ describe() {
377
+ const url = this.underlyingPage.url();
378
+ return url || '';
379
+ }
380
+ get mouse() {
381
+ return {
382
+ click: async (x, y, options)=>{
383
+ await this.mouse.move(x, y);
384
+ const { button = 'left', count = 1 } = options || {};
385
+ debugPage(`mouse click ${x}, ${y}, ${button}, ${count}`);
386
+ if (2 === count && 'playwright' === this.interfaceType) await this.underlyingPage.mouse.dblclick(x, y, {
387
+ button
388
+ });
389
+ else if ('puppeteer' === this.interfaceType) {
390
+ const page = this.underlyingPage;
391
+ if ('left' === button && 1 === count) await page.mouse.click(x, y);
392
+ else await page.mouse.click(x, y, {
393
+ button,
394
+ count
395
+ });
396
+ } else if ('playwright' === this.interfaceType) await this.underlyingPage.mouse.click(x, y, {
397
+ button,
398
+ clickCount: count
399
+ });
400
+ },
401
+ wheel: async (deltaX, deltaY)=>{
402
+ debugPage(`mouse wheel ${deltaX}, ${deltaY}`);
403
+ if ('puppeteer' === this.interfaceType) await this.underlyingPage.mouse.wheel({
404
+ deltaX,
405
+ deltaY
406
+ });
407
+ else if ('playwright' === this.interfaceType) await this.underlyingPage.mouse.wheel(deltaX, deltaY);
408
+ },
409
+ move: async (x, y)=>{
410
+ this.everMoved = true;
411
+ debugPage(`mouse move to ${x}, ${y}`);
412
+ return this.underlyingPage.mouse.move(x, y);
413
+ },
414
+ drag: async (from, to)=>{
415
+ debugPage(`begin mouse drag from ${from.x}, ${from.y} to ${to.x}, ${to.y}`);
416
+ await this.underlyingPage.mouse.move(from.x, from.y);
417
+ await (0, utils_namespaceObject.sleep)(200);
418
+ await this.underlyingPage.mouse.down();
419
+ await (0, utils_namespaceObject.sleep)(300);
420
+ await this.underlyingPage.mouse.move(to.x, to.y, {
421
+ steps: 20
422
+ });
423
+ await (0, utils_namespaceObject.sleep)(500);
424
+ await this.underlyingPage.mouse.up();
425
+ await (0, utils_namespaceObject.sleep)(200);
426
+ debugPage(`end mouse drag from ${from.x}, ${from.y} to ${to.x}, ${to.y}`);
427
+ }
428
+ };
429
+ }
430
+ get keyboard() {
431
+ return {
432
+ type: async (text)=>{
433
+ debugPage(`keyboard type ${text}`);
434
+ return this.underlyingPage.keyboard.type(text, {
435
+ delay: 80
436
+ });
437
+ },
438
+ press: async (action)=>{
439
+ const keys = Array.isArray(action) ? action : [
440
+ action
441
+ ];
442
+ debugPage('keyboard press', keys);
443
+ for (const k of keys){
444
+ const commands = k.command ? [
445
+ k.command
446
+ ] : [];
447
+ await this.underlyingPage.keyboard.down(k.key, {
448
+ commands
449
+ });
450
+ }
451
+ for (const k of [
452
+ ...keys
453
+ ].reverse())await this.underlyingPage.keyboard.up(k.key);
454
+ },
455
+ down: async (key)=>{
456
+ debugPage(`keyboard down ${key}`);
457
+ return this.underlyingPage.keyboard.down(key);
458
+ },
459
+ up: async (key)=>{
460
+ debugPage(`keyboard up ${key}`);
461
+ return this.underlyingPage.keyboard.up(key);
462
+ }
463
+ };
464
+ }
465
+ async clearInput(element) {
466
+ const backspace = async ()=>{
467
+ await (0, utils_namespaceObject.sleep)(100);
468
+ await this.keyboard.press([
469
+ {
470
+ key: 'Backspace'
471
+ }
472
+ ]);
473
+ };
474
+ const isMac = 'darwin' === process.platform;
475
+ debugPage('clearInput begin');
476
+ if (isMac) {
477
+ if ('puppeteer' === this.interfaceType) {
478
+ element && await this.mouse.click(element.center[0], element.center[1], {
479
+ count: 3
480
+ });
481
+ await backspace();
482
+ }
483
+ element && await this.mouse.click(element.center[0], element.center[1]);
484
+ await this.underlyingPage.keyboard.down('Meta');
485
+ await this.underlyingPage.keyboard.press('a');
486
+ await this.underlyingPage.keyboard.up('Meta');
487
+ await backspace();
488
+ } else {
489
+ element && await this.mouse.click(element.center[0], element.center[1]);
490
+ await this.underlyingPage.keyboard.down('Control');
491
+ await this.underlyingPage.keyboard.press('a');
492
+ await this.underlyingPage.keyboard.up('Control');
493
+ await backspace();
494
+ }
495
+ debugPage('clearInput end');
496
+ }
497
+ async moveToPointBeforeScroll(point) {
498
+ if (point) await this.mouse.move(point.left, point.top);
499
+ else if (!this.everMoved) {
500
+ const size = await this.size();
501
+ const targetX = Math.floor(size.width / 2);
502
+ const targetY = Math.floor(size.height / 2);
503
+ await this.mouse.move(targetX, targetY);
504
+ }
505
+ }
506
+ async scrollUntilTop(startingPoint) {
507
+ await this.moveToPointBeforeScroll(startingPoint);
508
+ return this.mouse.wheel(0, -9999999);
509
+ }
510
+ async scrollUntilBottom(startingPoint) {
511
+ await this.moveToPointBeforeScroll(startingPoint);
512
+ return this.mouse.wheel(0, 9999999);
513
+ }
514
+ async scrollUntilLeft(startingPoint) {
515
+ await this.moveToPointBeforeScroll(startingPoint);
516
+ return this.mouse.wheel(-9999999, 0);
517
+ }
518
+ async scrollUntilRight(startingPoint) {
519
+ await this.moveToPointBeforeScroll(startingPoint);
520
+ return this.mouse.wheel(9999999, 0);
521
+ }
522
+ async scrollUp(distance, startingPoint) {
523
+ const innerHeight = await this.evaluate(()=>window.innerHeight);
524
+ const scrollDistance = distance || 0.7 * innerHeight;
525
+ await this.moveToPointBeforeScroll(startingPoint);
526
+ return this.mouse.wheel(0, -scrollDistance);
527
+ }
528
+ async scrollDown(distance, startingPoint) {
529
+ const innerHeight = await this.evaluate(()=>window.innerHeight);
530
+ const scrollDistance = distance || 0.7 * innerHeight;
531
+ await this.moveToPointBeforeScroll(startingPoint);
532
+ return this.mouse.wheel(0, scrollDistance);
533
+ }
534
+ async scrollLeft(distance, startingPoint) {
535
+ const innerWidth = await this.evaluate(()=>window.innerWidth);
536
+ const scrollDistance = distance || 0.7 * innerWidth;
537
+ await this.moveToPointBeforeScroll(startingPoint);
538
+ return this.mouse.wheel(-scrollDistance, 0);
539
+ }
540
+ async scrollRight(distance, startingPoint) {
541
+ const innerWidth = await this.evaluate(()=>window.innerWidth);
542
+ const scrollDistance = distance || 0.7 * innerWidth;
543
+ await this.moveToPointBeforeScroll(startingPoint);
544
+ return this.mouse.wheel(scrollDistance, 0);
545
+ }
546
+ async navigate(url) {
547
+ debugPage(`navigate to ${url}`);
548
+ if ('puppeteer' === this.interfaceType) await this.underlyingPage.goto(url);
549
+ else if ('playwright' === this.interfaceType) await this.underlyingPage.goto(url);
550
+ else throw new Error('Unsupported page type for navigate');
551
+ }
552
+ async reload() {
553
+ debugPage('reload page');
554
+ if ('puppeteer' === this.interfaceType) await this.underlyingPage.reload();
555
+ else if ('playwright' === this.interfaceType) await this.underlyingPage.reload();
556
+ else throw new Error('Unsupported page type for reload');
557
+ }
558
+ async goBack() {
559
+ debugPage('go back');
560
+ if ('puppeteer' === this.interfaceType) await this.underlyingPage.goBack();
561
+ else if ('playwright' === this.interfaceType) await this.underlyingPage.goBack();
562
+ else throw new Error('Unsupported page type for go back');
563
+ }
564
+ async goForward() {
565
+ debugPage('go forward');
566
+ if ('puppeteer' === this.interfaceType) await this.underlyingPage.goForward();
567
+ else if ('playwright' === this.interfaceType) await this.underlyingPage.goForward();
568
+ else throw new Error('Unsupported page type for go forward');
569
+ }
570
+ async stopLoading() {
571
+ debugPage('stop loading');
572
+ if ('puppeteer' === this.interfaceType) {
573
+ const client = await this.underlyingPage.target().createCDPSession();
574
+ try {
575
+ await client.send('Page.stopLoading');
576
+ } finally{
577
+ await client.detach();
578
+ }
579
+ } else if ('playwright' === this.interfaceType) await this.underlyingPage.evaluate(()=>window.stop());
580
+ else throw new Error('Unsupported page type for stop loading');
581
+ }
582
+ async navigationState() {
583
+ try {
584
+ const readyState = await this.evaluate(()=>document.readyState);
585
+ return {
586
+ isLoading: 'complete' !== readyState
587
+ };
588
+ } catch (error) {
589
+ debugPage('failed to query navigation state: %s', error);
590
+ return {
591
+ isLoading: false
592
+ };
593
+ }
594
+ }
595
+ async beforeInvokeAction(name, param) {
596
+ if (this.onBeforeInvokeAction) await this.onBeforeInvokeAction(name, param);
597
+ }
598
+ async afterInvokeAction(name, param) {
599
+ await Promise.all([
600
+ this.waitForNavigation('afterInvokeAction', name),
601
+ this.waitForNetworkIdle('afterInvokeAction', name)
602
+ ]);
603
+ if (this.onAfterInvokeAction) await this.onAfterInvokeAction(name, param);
604
+ }
605
+ async destroy() {}
606
+ async swipe(from, to, duration) {
607
+ const LONG_PRESS_THRESHOLD = 500;
608
+ const MIN_PRESS_THRESHOLD = 150;
609
+ duration = duration || 100;
610
+ if (duration < MIN_PRESS_THRESHOLD) duration = MIN_PRESS_THRESHOLD;
611
+ if (duration > LONG_PRESS_THRESHOLD) duration = LONG_PRESS_THRESHOLD;
612
+ debugPage(`mouse swipe from ${from.x}, ${from.y} to ${to.x}, ${to.y} with duration ${duration}ms`);
613
+ if ('puppeteer' === this.interfaceType) {
614
+ const page = this.underlyingPage;
615
+ await page.mouse.move(from.x, from.y);
616
+ await page.mouse.down({
617
+ button: 'left'
618
+ });
619
+ const steps = 30;
620
+ const delay = duration / steps;
621
+ for(let i = 1; i <= steps; i++){
622
+ const x = from.x + (to.x - from.x) * (i / steps);
623
+ const y = from.y + (to.y - from.y) * (i / steps);
624
+ await page.mouse.move(x, y);
625
+ await new Promise((resolve)=>setTimeout(resolve, delay));
626
+ }
627
+ await page.mouse.up({
628
+ button: 'left'
629
+ });
630
+ } else if ('playwright' === this.interfaceType) {
631
+ const page = this.underlyingPage;
632
+ await page.mouse.move(from.x, from.y);
633
+ await page.mouse.down();
634
+ const steps = 30;
635
+ const delay = duration / steps;
636
+ for(let i = 1; i <= steps; i++){
637
+ const x = from.x + (to.x - from.x) * (i / steps);
638
+ const y = from.y + (to.y - from.y) * (i / steps);
639
+ await page.mouse.move(x, y);
640
+ await page.waitForTimeout(delay);
641
+ }
642
+ await page.mouse.up({
643
+ button: 'left'
644
+ });
645
+ }
646
+ }
647
+ async longPress(x, y, duration) {
648
+ duration = duration || 500;
649
+ const LONG_PRESS_THRESHOLD = 600;
650
+ const MIN_PRESS_THRESHOLD = 300;
651
+ if (duration > LONG_PRESS_THRESHOLD) duration = LONG_PRESS_THRESHOLD;
652
+ if (duration < MIN_PRESS_THRESHOLD) duration = MIN_PRESS_THRESHOLD;
653
+ debugPage(`mouse longPress at ${x}, ${y} for ${duration}ms`);
654
+ if ('puppeteer' === this.interfaceType) {
655
+ const page = this.underlyingPage;
656
+ await page.mouse.move(x, y);
657
+ await page.mouse.down({
658
+ button: 'left'
659
+ });
660
+ await new Promise((res)=>setTimeout(res, duration));
661
+ await page.mouse.up({
662
+ button: 'left'
663
+ });
664
+ } else if ('playwright' === this.interfaceType) {
665
+ const page = this.underlyingPage;
666
+ await page.mouse.move(x, y);
667
+ await page.mouse.down({
668
+ button: 'left'
669
+ });
670
+ await page.waitForTimeout(duration);
671
+ await page.mouse.up({
672
+ button: 'left'
673
+ });
674
+ }
675
+ }
676
+ async pinch(centerX, centerY, startDistance, endDistance, duration = 500) {
677
+ const steps = 30;
678
+ const delay = duration / steps;
679
+ const halfStart = startDistance / 2;
680
+ const halfEnd = endDistance / 2;
681
+ let client;
682
+ if ('puppeteer' === this.interfaceType) {
683
+ const page = this.underlyingPage;
684
+ client = await page.target().createCDPSession();
685
+ } else {
686
+ if ('playwright' !== this.interfaceType) return;
687
+ const page = this.underlyingPage;
688
+ const browserName = page.context().browser()?.browserType().name();
689
+ if (browserName && 'chromium' !== browserName) throw new Error(`Pinch gesture requires Chromium-based browser, but current browser is "${browserName}". CDP touch events are not supported in Firefox/WebKit.`);
690
+ client = await page.context().newCDPSession(page);
691
+ }
692
+ try {
693
+ await client.send('Input.dispatchTouchEvent', {
694
+ type: 'touchStart',
695
+ touchPoints: [
696
+ {
697
+ x: Math.round(centerX),
698
+ y: Math.round(centerY - halfStart),
699
+ id: 0
700
+ },
701
+ {
702
+ x: Math.round(centerX),
703
+ y: Math.round(centerY + halfStart),
704
+ id: 1
705
+ }
706
+ ]
707
+ });
708
+ for(let i = 1; i <= steps; i++){
709
+ const currentHalf = halfStart + i / steps * (halfEnd - halfStart);
710
+ await client.send('Input.dispatchTouchEvent', {
711
+ type: 'touchMove',
712
+ touchPoints: [
713
+ {
714
+ x: Math.round(centerX),
715
+ y: Math.round(centerY - currentHalf),
716
+ id: 0
717
+ },
718
+ {
719
+ x: Math.round(centerX),
720
+ y: Math.round(centerY + currentHalf),
721
+ id: 1
722
+ }
723
+ ]
724
+ });
725
+ await new Promise((res)=>setTimeout(res, delay));
726
+ }
727
+ await client.send('Input.dispatchTouchEvent', {
728
+ type: 'touchEnd',
729
+ touchPoints: []
730
+ });
731
+ } finally{
732
+ await client.detach();
733
+ }
734
+ }
735
+ async ensurePuppeteerFileChooserSession(page) {
736
+ if (this.puppeteerFileChooserSession) return this.puppeteerFileChooserSession;
737
+ const session = await page.target().createCDPSession();
738
+ await session.send('Page.enable');
739
+ await session.send('DOM.enable');
740
+ await session.send('Page.setInterceptFileChooserDialog', {
741
+ enabled: true
742
+ });
743
+ this.puppeteerFileChooserSession = session;
744
+ return session;
745
+ }
746
+ async registerFileChooserListener(handler) {
747
+ if ('puppeteer' !== this.interfaceType) throw new Error('registerFileChooserListener is only supported in Puppeteer');
748
+ const page = this.underlyingPage;
749
+ const session = await this.ensurePuppeteerFileChooserSession(page);
750
+ if (this.puppeteerFileChooserHandler) session.off('Page.fileChooserOpened', this.puppeteerFileChooserHandler);
751
+ let capturedError;
752
+ this.puppeteerFileChooserHandler = async (event)=>{
753
+ if (void 0 === event.backendNodeId) return void debugPage('puppeteer file chooser opened without backendNodeId, skip');
754
+ try {
755
+ await handler({
756
+ accept: async (files)=>{
757
+ const { node } = await session.send('DOM.describeNode', {
758
+ backendNodeId: event.backendNodeId
759
+ });
760
+ const hasWebkitDirectory = node.attributes?.includes('webkitdirectory') || node.attributes?.includes('directory');
761
+ if (hasWebkitDirectory) throw new Error('Directory upload (webkitdirectory) is not supported in Puppeteer. Please use Playwright instead, which supports directory upload since version 1.45.');
762
+ if (files.length > 1) {
763
+ const hasMultiple = node.attributes?.includes('multiple');
764
+ if (!hasMultiple) throw new Error('Non-multiple file input can only accept single file');
765
+ }
766
+ await session.send('DOM.setFileInputFiles', {
767
+ files,
768
+ backendNodeId: event.backendNodeId
769
+ });
770
+ }
771
+ });
772
+ } catch (error) {
773
+ capturedError = error;
774
+ }
775
+ };
776
+ session.on('Page.fileChooserOpened', this.puppeteerFileChooserHandler);
777
+ return {
778
+ dispose: ()=>{
779
+ if (this.puppeteerFileChooserHandler) session.off('Page.fileChooserOpened', this.puppeteerFileChooserHandler);
780
+ session.detach();
781
+ this.puppeteerFileChooserHandler = void 0;
782
+ if (this.puppeteerFileChooserSession === session) this.puppeteerFileChooserSession = void 0;
783
+ },
784
+ getError: ()=>capturedError
785
+ };
786
+ }
787
+ constructor(underlyingPage, interfaceType, opts){
788
+ _define_property(this, "underlyingPage", void 0);
789
+ _define_property(this, "waitForNavigationTimeout", void 0);
790
+ _define_property(this, "waitForNetworkIdleTimeout", void 0);
791
+ _define_property(this, "viewportSize", void 0);
792
+ _define_property(this, "onBeforeInvokeAction", void 0);
793
+ _define_property(this, "onAfterInvokeAction", void 0);
794
+ _define_property(this, "customActions", void 0);
795
+ _define_property(this, "enableTouchEventsInActionSpace", void 0);
796
+ _define_property(this, "puppeteerFileChooserSession", void 0);
797
+ _define_property(this, "puppeteerFileChooserHandler", void 0);
798
+ _define_property(this, "playwrightNetworkIdleWarningShown", false);
799
+ _define_property(this, "activeMjpegStream", void 0);
800
+ _define_property(this, "interfaceType", void 0);
801
+ _define_property(this, "everMoved", false);
802
+ this.underlyingPage = underlyingPage;
803
+ this.interfaceType = interfaceType;
804
+ this.waitForNavigationTimeout = opts?.waitForNavigationTimeout ?? constants_namespaceObject.DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT;
805
+ this.waitForNetworkIdleTimeout = opts?.waitForNetworkIdleTimeout ?? constants_namespaceObject.DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;
806
+ this.onBeforeInvokeAction = opts?.beforeInvokeAction;
807
+ this.onAfterInvokeAction = opts?.afterInvokeAction;
808
+ this.customActions = opts?.customActions;
809
+ this.enableTouchEventsInActionSpace = opts?.enableTouchEventsInActionSpace ?? false;
810
+ }
811
+ }
812
+ function forceClosePopup(page, debugProfile) {
813
+ page.on('popup', async (popup)=>{
814
+ if (!popup) return void console.warn('got a popup event, but the popup is not ready yet, skip');
815
+ const url = await popup.url();
816
+ console.log(`Popup opened: ${url}`);
817
+ if (popup.isClosed()) debugProfile(`popup is already closed, skip close ${url}`);
818
+ else try {
819
+ await popup.close();
820
+ } catch (error) {
821
+ debugProfile(`failed to close popup ${url}, error: ${error}`);
822
+ }
823
+ if (page.isClosed()) debugProfile(`page is already closed, skip goto ${url}`);
824
+ else try {
825
+ await page.goto(url);
826
+ } catch (error) {
827
+ debugProfile(`failed to goto ${url}, error: ${error}`);
828
+ }
829
+ });
830
+ }
831
+ function forceChromeSelectRendering(page) {
832
+ const styleContent = `
833
+ /* Add by Midscene because of forceChromeSelectRendering is enabled*/
834
+ select {
835
+ &, &::picker(select) {
836
+ appearance: base-select !important;
837
+ }
838
+ }`;
839
+ const styleId = 'midscene-force-select-rendering';
840
+ const injectStyle = async ()=>{
841
+ try {
842
+ await page.evaluate(({ id, content })=>{
843
+ if (document.getElementById(id)) return;
844
+ const style = document.createElement('style');
845
+ style.id = id;
846
+ style.textContent = content;
847
+ document.head.appendChild(style);
848
+ }, {
849
+ id: styleId,
850
+ content: styleContent
851
+ });
852
+ debugPage('Midscene - Added base-select appearance style for select elements because of forceChromeSelectRendering is enabled');
853
+ } catch (err) {
854
+ console.log('Midscene - Failed to add base-select appearance style:', err);
855
+ }
856
+ };
857
+ injectStyle();
858
+ page.on('load', ()=>{
859
+ injectStyle();
860
+ });
861
+ }
862
+ exports.BROWSER_NAVIGATION_ERROR_PATTERN = __webpack_exports__.BROWSER_NAVIGATION_ERROR_PATTERN;
863
+ exports.Page = __webpack_exports__.Page;
864
+ exports.debugPage = __webpack_exports__.debugPage;
865
+ exports.forceChromeSelectRendering = __webpack_exports__.forceChromeSelectRendering;
866
+ exports.forceClosePopup = __webpack_exports__.forceClosePopup;
867
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
868
+ "BROWSER_NAVIGATION_ERROR_PATTERN",
869
+ "Page",
870
+ "debugPage",
871
+ "forceChromeSelectRendering",
872
+ "forceClosePopup"
873
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
874
+ Object.defineProperty(exports, '__esModule', {
875
+ value: true
876
+ });