@skrillex1224/playwright-toolkit 2.1.27 → 2.1.29
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/dist/index.cjs +74 -75
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +75 -76
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -684,16 +684,26 @@ var Humanize = {
|
|
|
684
684
|
while (current) {
|
|
685
685
|
if (isScrollable(current)) {
|
|
686
686
|
const crect = current.getBoundingClientRect();
|
|
687
|
-
if (rect.top < crect.top || rect.bottom > crect.bottom) {
|
|
687
|
+
if (rect.top < crect.top + 2 || rect.bottom > crect.bottom - 2) {
|
|
688
688
|
return true;
|
|
689
689
|
}
|
|
690
690
|
}
|
|
691
691
|
current = current.parentElement;
|
|
692
692
|
}
|
|
693
|
+
const cx = rect.left + rect.width / 2;
|
|
694
|
+
const cy = rect.top + rect.height / 2;
|
|
695
|
+
if (cx < 0 || cx > window.innerWidth || cy < 0 || cy > window.innerHeight) return true;
|
|
696
|
+
const pointElement = document.elementFromPoint(cx, cy);
|
|
697
|
+
if (pointElement) {
|
|
698
|
+
if (el.contains(pointElement) || pointElement.contains(el)) {
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
693
703
|
return false;
|
|
694
704
|
});
|
|
695
705
|
if (!needsScroll) {
|
|
696
|
-
logger4.debug("humanScroll", "Element already in view");
|
|
706
|
+
logger4.debug("humanScroll", "Element already in view (and unobstructed)");
|
|
697
707
|
return { element, didScroll: false, restore: async () => {
|
|
698
708
|
} };
|
|
699
709
|
}
|
|
@@ -742,9 +752,21 @@ var Humanize = {
|
|
|
742
752
|
}
|
|
743
753
|
const scrollingElement = document.scrollingElement || document.documentElement;
|
|
744
754
|
if (scrollingElement) scrollables.push(scrollingElement);
|
|
755
|
+
const cx = rect.left + rect.width / 2;
|
|
756
|
+
const cy = rect.top + rect.height / 2;
|
|
757
|
+
const viewH = window.innerHeight;
|
|
758
|
+
let isObstructed = false;
|
|
759
|
+
if (cy < 0 || cy > viewH) {
|
|
760
|
+
isObstructed = true;
|
|
761
|
+
} else {
|
|
762
|
+
const pointElement = document.elementFromPoint(cx, cy);
|
|
763
|
+
if (pointElement && !el.contains(pointElement) && !pointElement.contains(el)) {
|
|
764
|
+
isObstructed = true;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
745
767
|
let target2 = null;
|
|
746
768
|
for (const container of scrollables) {
|
|
747
|
-
const crect = container === scrollingElement ? { top: 0, bottom:
|
|
769
|
+
const crect = container === scrollingElement ? { top: 0, bottom: viewH } : container.getBoundingClientRect();
|
|
748
770
|
if (rect.top < crect.top + 2) {
|
|
749
771
|
target2 = { container, direction: -1 };
|
|
750
772
|
break;
|
|
@@ -753,26 +775,39 @@ var Humanize = {
|
|
|
753
775
|
target2 = { container, direction: 1 };
|
|
754
776
|
break;
|
|
755
777
|
}
|
|
778
|
+
if (isObstructed) {
|
|
779
|
+
const containerCenter = (crect.top + crect.bottom) / 2;
|
|
780
|
+
const elementCenter = (rect.top + rect.bottom) / 2;
|
|
781
|
+
const forceDirection = elementCenter > containerCenter ? 1 : -1;
|
|
782
|
+
const canScroll = forceDirection === 1 ? container.scrollTop < container.scrollHeight - container.clientHeight : container.scrollTop > 0;
|
|
783
|
+
if (canScroll) {
|
|
784
|
+
target2 = { container, direction: forceDirection };
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
756
788
|
}
|
|
757
789
|
if (!target2) {
|
|
758
|
-
return { moved: false, inView:
|
|
790
|
+
return { moved: false, inView: !isObstructed };
|
|
759
791
|
}
|
|
760
792
|
const maxScroll = target2.container.scrollHeight - target2.container.clientHeight;
|
|
761
793
|
if (maxScroll <= 0) {
|
|
762
|
-
return { moved: false, inView:
|
|
794
|
+
return { moved: false, inView: !isObstructed };
|
|
763
795
|
}
|
|
764
796
|
const before = target2.container.scrollTop;
|
|
765
797
|
let next = before + target2.direction * stepPx;
|
|
766
798
|
if (next < 0) next = 0;
|
|
767
799
|
if (next > maxScroll) next = maxScroll;
|
|
768
|
-
if (next
|
|
769
|
-
return { moved: false, inView:
|
|
800
|
+
if (Math.abs(next - before) < 1) {
|
|
801
|
+
return { moved: false, inView: !isObstructed };
|
|
770
802
|
}
|
|
771
803
|
target2.container.scrollTop = next;
|
|
772
804
|
return { moved: true, inView: false };
|
|
773
805
|
}, step);
|
|
774
806
|
if (result.inView) break;
|
|
775
|
-
if (!result.moved)
|
|
807
|
+
if (!result.moved && !result.inView) {
|
|
808
|
+
logger4.warn("humanScroll", "Stuck: cannot scroll further but element still obstructed/hidden");
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
776
811
|
await delay2(this.jitterMs(120, 0.4));
|
|
777
812
|
}
|
|
778
813
|
const restore = async () => {
|
|
@@ -1253,18 +1288,9 @@ var Captcha = {
|
|
|
1253
1288
|
};
|
|
1254
1289
|
|
|
1255
1290
|
// src/sse.js
|
|
1256
|
-
import
|
|
1257
|
-
import {
|
|
1258
|
-
import { Agent as HttpsAgent } from "https";
|
|
1291
|
+
import https from "https";
|
|
1292
|
+
import { URL as URL2 } from "url";
|
|
1259
1293
|
var logger7 = createLogger("Sse");
|
|
1260
|
-
var SHARED_HTTP_AGENT = new HttpAgent({ keepAlive: false });
|
|
1261
|
-
var SHARED_HTTPS_AGENT = new HttpsAgent({ keepAlive: false, rejectUnauthorized: false });
|
|
1262
|
-
var SHARED_GOT_OPTIONS = {
|
|
1263
|
-
http2: false,
|
|
1264
|
-
retry: { limit: 0 },
|
|
1265
|
-
throwHttpErrors: false,
|
|
1266
|
-
decompress: false
|
|
1267
|
-
};
|
|
1268
1294
|
var Sse = {
|
|
1269
1295
|
/**
|
|
1270
1296
|
* 解析 SSE 流文本
|
|
@@ -1291,7 +1317,7 @@ var Sse = {
|
|
|
1291
1317
|
return events;
|
|
1292
1318
|
},
|
|
1293
1319
|
/**
|
|
1294
|
-
* 拦截网络请求并使用
|
|
1320
|
+
* 拦截网络请求并使用 Node.js 原生 https 模块转发,以解决流式数据捕获问题。
|
|
1295
1321
|
* @param {import('playwright').Page} page
|
|
1296
1322
|
* @param {string|RegExp} urlPattern - 拦截的 URL 模式
|
|
1297
1323
|
* @param {object} options
|
|
@@ -1299,7 +1325,7 @@ var Sse = {
|
|
|
1299
1325
|
* @param {function(string, function): void} [options.onEnd] - (fullText, resolve) => void
|
|
1300
1326
|
* @param {function(Error, function): void} [options.onTimeout] - (error, reject) => void
|
|
1301
1327
|
* @param {number} [options.initialTimeout=90000] - 初始数据接收超时 (ms),默认 90s
|
|
1302
|
-
* @param {number} [options.
|
|
1328
|
+
* @param {number} [options.timeout=180000] - 整体请求超时时间 (ms),默认 180s
|
|
1303
1329
|
* @returns {Promise<any>} - 返回 Promise,当流满足条件时 resolve
|
|
1304
1330
|
*/
|
|
1305
1331
|
async intercept(page, urlPattern, options = {}) {
|
|
@@ -1325,39 +1351,19 @@ var Sse = {
|
|
|
1325
1351
|
logger7.info(`[MITM] \u5DF2\u62E6\u622A\u8BF7\u6C42: ${request.url()}`);
|
|
1326
1352
|
try {
|
|
1327
1353
|
const headers = await request.allHeaders();
|
|
1328
|
-
|
|
1354
|
+
const postData = request.postData();
|
|
1355
|
+
const urlObj = new URL2(request.url());
|
|
1329
1356
|
delete headers["accept-encoding"];
|
|
1330
1357
|
delete headers["content-length"];
|
|
1331
|
-
const currentAcceptLanguage = headers["accept-language"] || "";
|
|
1332
|
-
AntiCheat.applyLocaleHeaders(headers, currentAcceptLanguage);
|
|
1333
|
-
const resolvedAcceptLanguage = headers["accept-language"] || "";
|
|
1334
|
-
const userAgent = headers["user-agent"] || "";
|
|
1335
|
-
const method = request.method();
|
|
1336
|
-
const postData = method !== "GET" && method !== "HEAD" ? request.postDataBuffer() : void 0;
|
|
1337
1358
|
const reqOptions = {
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1359
|
+
hostname: urlObj.hostname,
|
|
1360
|
+
port: 443,
|
|
1361
|
+
path: urlObj.pathname + urlObj.search,
|
|
1362
|
+
method: request.method(),
|
|
1341
1363
|
headers,
|
|
1342
|
-
|
|
1343
|
-
agent: {
|
|
1344
|
-
http: SHARED_HTTP_AGENT,
|
|
1345
|
-
https: SHARED_HTTPS_AGENT
|
|
1346
|
-
},
|
|
1347
|
-
timeout: { request: overallTimeout }
|
|
1348
|
-
};
|
|
1349
|
-
if (postData) {
|
|
1350
|
-
reqOptions.body = postData;
|
|
1351
|
-
}
|
|
1352
|
-
const stream = gotScraping.stream(reqOptions);
|
|
1353
|
-
const handleStreamError = (error) => {
|
|
1354
|
-
clearAllTimers();
|
|
1355
|
-
stream.destroy();
|
|
1356
|
-
route.abort().catch(() => {
|
|
1357
|
-
});
|
|
1358
|
-
reject(error);
|
|
1364
|
+
timeout: overallTimeout
|
|
1359
1365
|
};
|
|
1360
|
-
|
|
1366
|
+
const req = https.request(reqOptions, (res) => {
|
|
1361
1367
|
const chunks = [];
|
|
1362
1368
|
let accumulatedText = "";
|
|
1363
1369
|
res.on("data", (chunk) => {
|
|
@@ -1380,7 +1386,6 @@ var Sse = {
|
|
|
1380
1386
|
}
|
|
1381
1387
|
}
|
|
1382
1388
|
});
|
|
1383
|
-
res.on("error", handleStreamError);
|
|
1384
1389
|
res.on("end", () => {
|
|
1385
1390
|
logger7.info("[MITM] \u4E0A\u6E38\u54CD\u5E94\u7ED3\u675F");
|
|
1386
1391
|
clearAllTimers();
|
|
@@ -1393,28 +1398,22 @@ var Sse = {
|
|
|
1393
1398
|
} else if (!onData) {
|
|
1394
1399
|
resolve(accumulatedText);
|
|
1395
1400
|
}
|
|
1396
|
-
const resHeaders = {};
|
|
1397
|
-
for (const [key, value] of Object.entries(res.headers || {})) {
|
|
1398
|
-
if (Array.isArray(value)) {
|
|
1399
|
-
resHeaders[key] = value.join(", ");
|
|
1400
|
-
} else if (value) {
|
|
1401
|
-
resHeaders[key] = String(value);
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
delete resHeaders["content-encoding"];
|
|
1405
|
-
delete resHeaders["content-length"];
|
|
1406
|
-
delete resHeaders["transfer-encoding"];
|
|
1407
|
-
delete resHeaders["connection"];
|
|
1408
|
-
delete resHeaders["keep-alive"];
|
|
1409
1401
|
route.fulfill({
|
|
1410
|
-
status: res.statusCode
|
|
1411
|
-
headers:
|
|
1402
|
+
status: res.statusCode,
|
|
1403
|
+
headers: res.headers,
|
|
1412
1404
|
body: Buffer.concat(chunks)
|
|
1413
1405
|
}).catch(() => {
|
|
1414
1406
|
});
|
|
1415
1407
|
});
|
|
1416
1408
|
});
|
|
1417
|
-
|
|
1409
|
+
req.on("error", (e) => {
|
|
1410
|
+
clearAllTimers();
|
|
1411
|
+
route.abort().catch(() => {
|
|
1412
|
+
});
|
|
1413
|
+
reject(e);
|
|
1414
|
+
});
|
|
1415
|
+
if (postData) req.write(postData);
|
|
1416
|
+
req.end();
|
|
1418
1417
|
} catch (e) {
|
|
1419
1418
|
clearAllTimers();
|
|
1420
1419
|
route.continue().catch(() => {
|
|
@@ -1473,12 +1472,12 @@ var Sse = {
|
|
|
1473
1472
|
};
|
|
1474
1473
|
|
|
1475
1474
|
// src/interception.js
|
|
1476
|
-
import { gotScraping
|
|
1477
|
-
import { Agent as
|
|
1478
|
-
import { Agent as
|
|
1475
|
+
import { gotScraping } from "got-scraping";
|
|
1476
|
+
import { Agent as HttpAgent } from "http";
|
|
1477
|
+
import { Agent as HttpsAgent } from "https";
|
|
1479
1478
|
var logger8 = createLogger("Interception");
|
|
1480
|
-
var
|
|
1481
|
-
var
|
|
1479
|
+
var SHARED_HTTP_AGENT = new HttpAgent({ keepAlive: false });
|
|
1480
|
+
var SHARED_HTTPS_AGENT = new HttpsAgent({ keepAlive: false, rejectUnauthorized: false });
|
|
1482
1481
|
var DirectConfig = {
|
|
1483
1482
|
/** 直连请求超时时间(秒) */
|
|
1484
1483
|
directTimeout: 12,
|
|
@@ -1531,7 +1530,7 @@ var DEFAULT_BLOCKING_CONFIG = {
|
|
|
1531
1530
|
/** 额外自定义扩展名列表 */
|
|
1532
1531
|
customExtensions: []
|
|
1533
1532
|
};
|
|
1534
|
-
var
|
|
1533
|
+
var SHARED_GOT_OPTIONS = {
|
|
1535
1534
|
http2: false,
|
|
1536
1535
|
// 禁用 HTTP2 避免在拦截场景下的握手兼容性问题
|
|
1537
1536
|
retry: { limit: 0 },
|
|
@@ -1640,8 +1639,8 @@ var Interception = {
|
|
|
1640
1639
|
const userAgent = reqHeaders["user-agent"] || "";
|
|
1641
1640
|
const method = request.method();
|
|
1642
1641
|
const postData = method !== "GET" && method !== "HEAD" ? request.postDataBuffer() : void 0;
|
|
1643
|
-
const response = await
|
|
1644
|
-
...
|
|
1642
|
+
const response = await gotScraping({
|
|
1643
|
+
...SHARED_GOT_OPTIONS,
|
|
1645
1644
|
// 应用通用配置
|
|
1646
1645
|
url,
|
|
1647
1646
|
method,
|
|
@@ -1653,8 +1652,8 @@ var Interception = {
|
|
|
1653
1652
|
headerGeneratorOptions: AntiCheat.getTlsFingerprintOptions(userAgent, resolvedAcceptLanguage),
|
|
1654
1653
|
// 使用共享的 Agent 单例(keepAlive: false,不会池化连接)
|
|
1655
1654
|
agent: {
|
|
1656
|
-
http:
|
|
1657
|
-
https:
|
|
1655
|
+
http: SHARED_HTTP_AGENT,
|
|
1656
|
+
https: SHARED_HTTPS_AGENT
|
|
1658
1657
|
},
|
|
1659
1658
|
// 超时时间
|
|
1660
1659
|
timeout: { request: DirectConfig.directTimeout * 1e3 }
|