@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 CHANGED
@@ -713,16 +713,26 @@ var Humanize = {
713
713
  while (current) {
714
714
  if (isScrollable(current)) {
715
715
  const crect = current.getBoundingClientRect();
716
- if (rect.top < crect.top || rect.bottom > crect.bottom) {
716
+ if (rect.top < crect.top + 2 || rect.bottom > crect.bottom - 2) {
717
717
  return true;
718
718
  }
719
719
  }
720
720
  current = current.parentElement;
721
721
  }
722
+ const cx = rect.left + rect.width / 2;
723
+ const cy = rect.top + rect.height / 2;
724
+ if (cx < 0 || cx > window.innerWidth || cy < 0 || cy > window.innerHeight) return true;
725
+ const pointElement = document.elementFromPoint(cx, cy);
726
+ if (pointElement) {
727
+ if (el.contains(pointElement) || pointElement.contains(el)) {
728
+ return false;
729
+ }
730
+ return true;
731
+ }
722
732
  return false;
723
733
  });
724
734
  if (!needsScroll) {
725
- logger4.debug("humanScroll", "Element already in view");
735
+ logger4.debug("humanScroll", "Element already in view (and unobstructed)");
726
736
  return { element, didScroll: false, restore: async () => {
727
737
  } };
728
738
  }
@@ -771,9 +781,21 @@ var Humanize = {
771
781
  }
772
782
  const scrollingElement = document.scrollingElement || document.documentElement;
773
783
  if (scrollingElement) scrollables.push(scrollingElement);
784
+ const cx = rect.left + rect.width / 2;
785
+ const cy = rect.top + rect.height / 2;
786
+ const viewH = window.innerHeight;
787
+ let isObstructed = false;
788
+ if (cy < 0 || cy > viewH) {
789
+ isObstructed = true;
790
+ } else {
791
+ const pointElement = document.elementFromPoint(cx, cy);
792
+ if (pointElement && !el.contains(pointElement) && !pointElement.contains(el)) {
793
+ isObstructed = true;
794
+ }
795
+ }
774
796
  let target2 = null;
775
797
  for (const container of scrollables) {
776
- const crect = container === scrollingElement ? { top: 0, bottom: window.innerHeight } : container.getBoundingClientRect();
798
+ const crect = container === scrollingElement ? { top: 0, bottom: viewH } : container.getBoundingClientRect();
777
799
  if (rect.top < crect.top + 2) {
778
800
  target2 = { container, direction: -1 };
779
801
  break;
@@ -782,26 +804,39 @@ var Humanize = {
782
804
  target2 = { container, direction: 1 };
783
805
  break;
784
806
  }
807
+ if (isObstructed) {
808
+ const containerCenter = (crect.top + crect.bottom) / 2;
809
+ const elementCenter = (rect.top + rect.bottom) / 2;
810
+ const forceDirection = elementCenter > containerCenter ? 1 : -1;
811
+ const canScroll = forceDirection === 1 ? container.scrollTop < container.scrollHeight - container.clientHeight : container.scrollTop > 0;
812
+ if (canScroll) {
813
+ target2 = { container, direction: forceDirection };
814
+ break;
815
+ }
816
+ }
785
817
  }
786
818
  if (!target2) {
787
- return { moved: false, inView: true };
819
+ return { moved: false, inView: !isObstructed };
788
820
  }
789
821
  const maxScroll = target2.container.scrollHeight - target2.container.clientHeight;
790
822
  if (maxScroll <= 0) {
791
- return { moved: false, inView: false };
823
+ return { moved: false, inView: !isObstructed };
792
824
  }
793
825
  const before = target2.container.scrollTop;
794
826
  let next = before + target2.direction * stepPx;
795
827
  if (next < 0) next = 0;
796
828
  if (next > maxScroll) next = maxScroll;
797
- if (next === before) {
798
- return { moved: false, inView: false };
829
+ if (Math.abs(next - before) < 1) {
830
+ return { moved: false, inView: !isObstructed };
799
831
  }
800
832
  target2.container.scrollTop = next;
801
833
  return { moved: true, inView: false };
802
834
  }, step);
803
835
  if (result.inView) break;
804
- if (!result.moved) break;
836
+ if (!result.moved && !result.inView) {
837
+ logger4.warn("humanScroll", "Stuck: cannot scroll further but element still obstructed/hidden");
838
+ break;
839
+ }
805
840
  await (0, import_delay2.default)(this.jitterMs(120, 0.4));
806
841
  }
807
842
  const restore = async () => {
@@ -1282,18 +1317,9 @@ var Captcha = {
1282
1317
  };
1283
1318
 
1284
1319
  // src/sse.js
1285
- var import_got_scraping = require("got-scraping");
1286
- var import_http = require("http");
1287
- var import_https = require("https");
1320
+ var import_https = __toESM(require("https"), 1);
1321
+ var import_url = require("url");
1288
1322
  var logger7 = createLogger("Sse");
1289
- var SHARED_HTTP_AGENT = new import_http.Agent({ keepAlive: false });
1290
- var SHARED_HTTPS_AGENT = new import_https.Agent({ keepAlive: false, rejectUnauthorized: false });
1291
- var SHARED_GOT_OPTIONS = {
1292
- http2: false,
1293
- retry: { limit: 0 },
1294
- throwHttpErrors: false,
1295
- decompress: false
1296
- };
1297
1323
  var Sse = {
1298
1324
  /**
1299
1325
  * 解析 SSE 流文本
@@ -1320,7 +1346,7 @@ var Sse = {
1320
1346
  return events;
1321
1347
  },
1322
1348
  /**
1323
- * 拦截网络请求并使用 got-scraping 转发,以解决流式数据捕获问题。
1349
+ * 拦截网络请求并使用 Node.js 原生 https 模块转发,以解决流式数据捕获问题。
1324
1350
  * @param {import('playwright').Page} page
1325
1351
  * @param {string|RegExp} urlPattern - 拦截的 URL 模式
1326
1352
  * @param {object} options
@@ -1328,7 +1354,7 @@ var Sse = {
1328
1354
  * @param {function(string, function): void} [options.onEnd] - (fullText, resolve) => void
1329
1355
  * @param {function(Error, function): void} [options.onTimeout] - (error, reject) => void
1330
1356
  * @param {number} [options.initialTimeout=90000] - 初始数据接收超时 (ms),默认 90s
1331
- * @param {number} [options.overallTimeout=180000] - 整体请求超时时间 (ms),默认 180s
1357
+ * @param {number} [options.timeout=180000] - 整体请求超时时间 (ms),默认 180s
1332
1358
  * @returns {Promise<any>} - 返回 Promise,当流满足条件时 resolve
1333
1359
  */
1334
1360
  async intercept(page, urlPattern, options = {}) {
@@ -1354,39 +1380,19 @@ var Sse = {
1354
1380
  logger7.info(`[MITM] \u5DF2\u62E6\u622A\u8BF7\u6C42: ${request.url()}`);
1355
1381
  try {
1356
1382
  const headers = await request.allHeaders();
1357
- delete headers["host"];
1383
+ const postData = request.postData();
1384
+ const urlObj = new import_url.URL(request.url());
1358
1385
  delete headers["accept-encoding"];
1359
1386
  delete headers["content-length"];
1360
- const currentAcceptLanguage = headers["accept-language"] || "";
1361
- AntiCheat.applyLocaleHeaders(headers, currentAcceptLanguage);
1362
- const resolvedAcceptLanguage = headers["accept-language"] || "";
1363
- const userAgent = headers["user-agent"] || "";
1364
- const method = request.method();
1365
- const postData = method !== "GET" && method !== "HEAD" ? request.postDataBuffer() : void 0;
1366
1387
  const reqOptions = {
1367
- ...SHARED_GOT_OPTIONS,
1368
- url: request.url(),
1369
- method,
1388
+ hostname: urlObj.hostname,
1389
+ port: 443,
1390
+ path: urlObj.pathname + urlObj.search,
1391
+ method: request.method(),
1370
1392
  headers,
1371
- headerGeneratorOptions: AntiCheat.getTlsFingerprintOptions(userAgent, resolvedAcceptLanguage),
1372
- agent: {
1373
- http: SHARED_HTTP_AGENT,
1374
- https: SHARED_HTTPS_AGENT
1375
- },
1376
- timeout: { request: overallTimeout }
1393
+ timeout: overallTimeout
1377
1394
  };
1378
- if (postData) {
1379
- reqOptions.body = postData;
1380
- }
1381
- const stream = import_got_scraping.gotScraping.stream(reqOptions);
1382
- const handleStreamError = (error) => {
1383
- clearAllTimers();
1384
- stream.destroy();
1385
- route.abort().catch(() => {
1386
- });
1387
- reject(error);
1388
- };
1389
- stream.on("response", (res) => {
1395
+ const req = import_https.default.request(reqOptions, (res) => {
1390
1396
  const chunks = [];
1391
1397
  let accumulatedText = "";
1392
1398
  res.on("data", (chunk) => {
@@ -1409,7 +1415,6 @@ var Sse = {
1409
1415
  }
1410
1416
  }
1411
1417
  });
1412
- res.on("error", handleStreamError);
1413
1418
  res.on("end", () => {
1414
1419
  logger7.info("[MITM] \u4E0A\u6E38\u54CD\u5E94\u7ED3\u675F");
1415
1420
  clearAllTimers();
@@ -1422,28 +1427,22 @@ var Sse = {
1422
1427
  } else if (!onData) {
1423
1428
  resolve(accumulatedText);
1424
1429
  }
1425
- const resHeaders = {};
1426
- for (const [key, value] of Object.entries(res.headers || {})) {
1427
- if (Array.isArray(value)) {
1428
- resHeaders[key] = value.join(", ");
1429
- } else if (value) {
1430
- resHeaders[key] = String(value);
1431
- }
1432
- }
1433
- delete resHeaders["content-encoding"];
1434
- delete resHeaders["content-length"];
1435
- delete resHeaders["transfer-encoding"];
1436
- delete resHeaders["connection"];
1437
- delete resHeaders["keep-alive"];
1438
1430
  route.fulfill({
1439
- status: res.statusCode || 200,
1440
- headers: resHeaders,
1431
+ status: res.statusCode,
1432
+ headers: res.headers,
1441
1433
  body: Buffer.concat(chunks)
1442
1434
  }).catch(() => {
1443
1435
  });
1444
1436
  });
1445
1437
  });
1446
- stream.on("error", handleStreamError);
1438
+ req.on("error", (e) => {
1439
+ clearAllTimers();
1440
+ route.abort().catch(() => {
1441
+ });
1442
+ reject(e);
1443
+ });
1444
+ if (postData) req.write(postData);
1445
+ req.end();
1447
1446
  } catch (e) {
1448
1447
  clearAllTimers();
1449
1448
  route.continue().catch(() => {
@@ -1502,12 +1501,12 @@ var Sse = {
1502
1501
  };
1503
1502
 
1504
1503
  // src/interception.js
1505
- var import_got_scraping2 = require("got-scraping");
1506
- var import_http2 = require("http");
1504
+ var import_got_scraping = require("got-scraping");
1505
+ var import_http = require("http");
1507
1506
  var import_https2 = require("https");
1508
1507
  var logger8 = createLogger("Interception");
1509
- var SHARED_HTTP_AGENT2 = new import_http2.Agent({ keepAlive: false });
1510
- var SHARED_HTTPS_AGENT2 = new import_https2.Agent({ keepAlive: false, rejectUnauthorized: false });
1508
+ var SHARED_HTTP_AGENT = new import_http.Agent({ keepAlive: false });
1509
+ var SHARED_HTTPS_AGENT = new import_https2.Agent({ keepAlive: false, rejectUnauthorized: false });
1511
1510
  var DirectConfig = {
1512
1511
  /** 直连请求超时时间(秒) */
1513
1512
  directTimeout: 12,
@@ -1560,7 +1559,7 @@ var DEFAULT_BLOCKING_CONFIG = {
1560
1559
  /** 额外自定义扩展名列表 */
1561
1560
  customExtensions: []
1562
1561
  };
1563
- var SHARED_GOT_OPTIONS2 = {
1562
+ var SHARED_GOT_OPTIONS = {
1564
1563
  http2: false,
1565
1564
  // 禁用 HTTP2 避免在拦截场景下的握手兼容性问题
1566
1565
  retry: { limit: 0 },
@@ -1669,8 +1668,8 @@ var Interception = {
1669
1668
  const userAgent = reqHeaders["user-agent"] || "";
1670
1669
  const method = request.method();
1671
1670
  const postData = method !== "GET" && method !== "HEAD" ? request.postDataBuffer() : void 0;
1672
- const response = await (0, import_got_scraping2.gotScraping)({
1673
- ...SHARED_GOT_OPTIONS2,
1671
+ const response = await (0, import_got_scraping.gotScraping)({
1672
+ ...SHARED_GOT_OPTIONS,
1674
1673
  // 应用通用配置
1675
1674
  url,
1676
1675
  method,
@@ -1682,8 +1681,8 @@ var Interception = {
1682
1681
  headerGeneratorOptions: AntiCheat.getTlsFingerprintOptions(userAgent, resolvedAcceptLanguage),
1683
1682
  // 使用共享的 Agent 单例(keepAlive: false,不会池化连接)
1684
1683
  agent: {
1685
- http: SHARED_HTTP_AGENT2,
1686
- https: SHARED_HTTPS_AGENT2
1684
+ http: SHARED_HTTP_AGENT,
1685
+ https: SHARED_HTTPS_AGENT
1687
1686
  },
1688
1687
  // 超时时间
1689
1688
  timeout: { request: DirectConfig.directTimeout * 1e3 }