@skrillex1224/playwright-toolkit 2.1.134 → 2.1.135
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/README.md +0 -6
- package/dist/index.cjs +80 -69
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +80 -69
- package/dist/index.js.map +2 -2
- package/index.d.ts +30 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -182,12 +182,6 @@ Captcha.useCaptchaMonitor(page, {
|
|
|
182
182
|
urlPattern: '/captcha',
|
|
183
183
|
onDetected: async () => { await Actor.fail('检测到验证码'); }
|
|
184
184
|
});
|
|
185
|
-
|
|
186
|
-
// Title 监控模式
|
|
187
|
-
Captcha.useCaptchaMonitor(page, {
|
|
188
|
-
titlePattern: /human verification|access denied/i,
|
|
189
|
-
onDetected: async () => { await Actor.fail('检测到风控标题'); }
|
|
190
|
-
});
|
|
191
185
|
```
|
|
192
186
|
|
|
193
187
|
### Constants
|
package/dist/index.cjs
CHANGED
|
@@ -1345,19 +1345,15 @@ var LiveView = {
|
|
|
1345
1345
|
var import_uuid = require("uuid");
|
|
1346
1346
|
var logger7 = createInternalLogger("Captcha");
|
|
1347
1347
|
function useCaptchaMonitor(page, options) {
|
|
1348
|
-
const { domSelector, urlPattern,
|
|
1349
|
-
if (!domSelector && !urlPattern
|
|
1350
|
-
throw new Error("[CaptchaMonitor] \u5FC5\u987B\u63D0\u4F9B domSelector
|
|
1348
|
+
const { domSelector, urlPattern, onDetected } = options;
|
|
1349
|
+
if (!domSelector && !urlPattern) {
|
|
1350
|
+
throw new Error("[CaptchaMonitor] \u5FC5\u987B\u63D0\u4F9B domSelector \u6216 urlPattern \u81F3\u5C11\u4E00\u4E2A");
|
|
1351
1351
|
}
|
|
1352
1352
|
if (!onDetected || typeof onDetected !== "function") {
|
|
1353
1353
|
throw new Error("[CaptchaMonitor] onDetected \u5FC5\u987B\u662F\u4E00\u4E2A\u51FD\u6570");
|
|
1354
1354
|
}
|
|
1355
|
-
if (titlePattern && typeof titlePattern !== "string" && !(titlePattern instanceof RegExp)) {
|
|
1356
|
-
throw new Error("[CaptchaMonitor] titlePattern \u5FC5\u987B\u662F string \u6216 RegExp");
|
|
1357
|
-
}
|
|
1358
1355
|
let isHandled = false;
|
|
1359
1356
|
let frameHandler = null;
|
|
1360
|
-
let titleFrameHandler = null;
|
|
1361
1357
|
let exposedFunctionName = null;
|
|
1362
1358
|
const triggerDetected = async () => {
|
|
1363
1359
|
if (isHandled) return;
|
|
@@ -1447,51 +1443,6 @@ function useCaptchaMonitor(page, options) {
|
|
|
1447
1443
|
page.off("framenavigated", frameHandler);
|
|
1448
1444
|
});
|
|
1449
1445
|
}
|
|
1450
|
-
if (titlePattern) {
|
|
1451
|
-
let titleTimer = null;
|
|
1452
|
-
let lastTitle = null;
|
|
1453
|
-
const matchTitle = (title) => {
|
|
1454
|
-
if (!titlePattern) return false;
|
|
1455
|
-
const normalized = String(title || "");
|
|
1456
|
-
if (titlePattern instanceof RegExp) {
|
|
1457
|
-
titlePattern.lastIndex = 0;
|
|
1458
|
-
return titlePattern.test(normalized);
|
|
1459
|
-
}
|
|
1460
|
-
return normalized.includes(String(titlePattern));
|
|
1461
|
-
};
|
|
1462
|
-
const checkCurrentTitle = async (force = false) => {
|
|
1463
|
-
try {
|
|
1464
|
-
const currentTitle = await page.title();
|
|
1465
|
-
if (!force && currentTitle === lastTitle) return;
|
|
1466
|
-
lastTitle = currentTitle;
|
|
1467
|
-
if (!matchTitle(currentTitle)) return;
|
|
1468
|
-
await triggerDetected();
|
|
1469
|
-
} catch {
|
|
1470
|
-
}
|
|
1471
|
-
};
|
|
1472
|
-
titleFrameHandler = async (frame) => {
|
|
1473
|
-
if (frame !== page.mainFrame()) return;
|
|
1474
|
-
await checkCurrentTitle();
|
|
1475
|
-
};
|
|
1476
|
-
page.on("framenavigated", titleFrameHandler);
|
|
1477
|
-
checkCurrentTitle(true).catch(() => {
|
|
1478
|
-
});
|
|
1479
|
-
titleTimer = setInterval(() => {
|
|
1480
|
-
if (isHandled) return;
|
|
1481
|
-
checkCurrentTitle(false).catch(() => {
|
|
1482
|
-
});
|
|
1483
|
-
}, 1e3);
|
|
1484
|
-
logger7.success("useCaptchaMonitor", `Title \u76D1\u63A7\u5DF2\u542F\u7528: ${String(titlePattern)}`);
|
|
1485
|
-
cleanupFns.push(async () => {
|
|
1486
|
-
if (titleTimer) {
|
|
1487
|
-
clearInterval(titleTimer);
|
|
1488
|
-
titleTimer = null;
|
|
1489
|
-
}
|
|
1490
|
-
if (titleFrameHandler) {
|
|
1491
|
-
page.off("framenavigated", titleFrameHandler);
|
|
1492
|
-
}
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
1446
|
return {
|
|
1496
1447
|
stop: async () => {
|
|
1497
1448
|
logger7.info("useCaptchaMonitor", "\u6B63\u5728\u505C\u6B62\u76D1\u63A7...");
|
|
@@ -1756,12 +1707,6 @@ var SHARED_HTTPS_AGENT = new import_https2.Agent({
|
|
|
1756
1707
|
maxFreeSockets: 10,
|
|
1757
1708
|
rejectUnauthorized: false
|
|
1758
1709
|
});
|
|
1759
|
-
var DirectConfig = {
|
|
1760
|
-
/** 直连请求超时时间(秒) */
|
|
1761
|
-
directTimeout: 12,
|
|
1762
|
-
/** 静默扩展名:这些扩展名的直连成功日志用 debug 级别 */
|
|
1763
|
-
silentExtensions: [".js"]
|
|
1764
|
-
};
|
|
1765
1710
|
var ARCHIVE_EXTENSIONS = [".7z", ".zip", ".rar", ".gz", ".bz2", ".tar", ".zst"];
|
|
1766
1711
|
var EXECUTABLE_EXTENSIONS = [".exe", ".apk", ".bin", ".dmg", ".jar", ".class"];
|
|
1767
1712
|
var DOCUMENT_EXTENSIONS = [".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".csv"];
|
|
@@ -1787,7 +1732,14 @@ var IMAGE_EXTENSIONS = [
|
|
|
1787
1732
|
var MEDIA_EXTENSIONS = [".mp3", ".mp4", ".avi", ".mkv", ".webm", ".midi", ".mid", ".ogg", ".flac", ".swf"];
|
|
1788
1733
|
var FONT_EXTENSIONS = [".woff", ".woff2", ".ttf", ".otf"];
|
|
1789
1734
|
var CSS_EXTENSIONS = [".css"];
|
|
1735
|
+
var JS_EXTENSIONS = [".js"];
|
|
1790
1736
|
var OTHER_EXTENSIONS = [".ps", ".iso"];
|
|
1737
|
+
var DirectConfig = {
|
|
1738
|
+
/** 直连请求超时时间(秒) */
|
|
1739
|
+
directTimeout: 12,
|
|
1740
|
+
/** 静默扩展名:这些扩展名的直连成功日志用 debug 级别 */
|
|
1741
|
+
silentExtensions: JS_EXTENSIONS
|
|
1742
|
+
};
|
|
1791
1743
|
var DEFAULT_BLOCKING_CONFIG = {
|
|
1792
1744
|
/** 屏蔽压缩包 */
|
|
1793
1745
|
blockArchive: true,
|
|
@@ -1804,9 +1756,29 @@ var DEFAULT_BLOCKING_CONFIG = {
|
|
|
1804
1756
|
/** 屏蔽 CSS (注意:可能影响页面视觉效果) */
|
|
1805
1757
|
blockCss: false,
|
|
1806
1758
|
/** 屏蔽其他资源 */
|
|
1807
|
-
blockOther: true
|
|
1808
|
-
|
|
1809
|
-
|
|
1759
|
+
blockOther: true
|
|
1760
|
+
};
|
|
1761
|
+
var DEFAULT_DIRECT_CONFIG = {
|
|
1762
|
+
/** 直连域名 */
|
|
1763
|
+
domains: [],
|
|
1764
|
+
/** 直连压缩包 */
|
|
1765
|
+
directAllArchive: false,
|
|
1766
|
+
/** 直连可执行文件 */
|
|
1767
|
+
directAllExecutable: false,
|
|
1768
|
+
/** 直连办公文档 */
|
|
1769
|
+
directAllDocument: false,
|
|
1770
|
+
/** 直连图片 */
|
|
1771
|
+
directAllImage: false,
|
|
1772
|
+
/** 直连音视频 */
|
|
1773
|
+
directAllMedia: false,
|
|
1774
|
+
/** 直连字体 */
|
|
1775
|
+
directAllFont: false,
|
|
1776
|
+
/** 直连 CSS */
|
|
1777
|
+
directAllCss: false,
|
|
1778
|
+
/** 直连 JS */
|
|
1779
|
+
directAllJs: false,
|
|
1780
|
+
/** 直连其他资源 */
|
|
1781
|
+
directAllOther: false
|
|
1810
1782
|
};
|
|
1811
1783
|
var SHARED_GOT_OPTIONS = {
|
|
1812
1784
|
http2: false,
|
|
@@ -1834,9 +1806,6 @@ var Interception = {
|
|
|
1834
1806
|
if (mergedConfig.blockFont) extensions.push(...FONT_EXTENSIONS);
|
|
1835
1807
|
if (mergedConfig.blockCss) extensions.push(...CSS_EXTENSIONS);
|
|
1836
1808
|
if (mergedConfig.blockOther) extensions.push(...OTHER_EXTENSIONS);
|
|
1837
|
-
if (mergedConfig.customExtensions?.length > 0) {
|
|
1838
|
-
extensions.push(...mergedConfig.customExtensions);
|
|
1839
|
-
}
|
|
1840
1809
|
return [...new Set(extensions)];
|
|
1841
1810
|
},
|
|
1842
1811
|
/**
|
|
@@ -1856,12 +1825,34 @@ var Interception = {
|
|
|
1856
1825
|
other: { name: "\u5176\u4ED6\u8D44\u6E90", extensions: OTHER_EXTENSIONS }
|
|
1857
1826
|
};
|
|
1858
1827
|
},
|
|
1828
|
+
/**
|
|
1829
|
+
* 根据配置生成需要直连的扩展名列表
|
|
1830
|
+
*
|
|
1831
|
+
* @param {Object} [config] - 直连扩展名配置
|
|
1832
|
+
* @returns {string[]} 需要直连的扩展名列表
|
|
1833
|
+
*/
|
|
1834
|
+
getDirectExtensions(config = {}) {
|
|
1835
|
+
const mergedConfig = { ...DEFAULT_DIRECT_CONFIG, ...config };
|
|
1836
|
+
const extensions = [];
|
|
1837
|
+
if (mergedConfig.directAllArchive) extensions.push(...ARCHIVE_EXTENSIONS);
|
|
1838
|
+
if (mergedConfig.directAllExecutable) extensions.push(...EXECUTABLE_EXTENSIONS);
|
|
1839
|
+
if (mergedConfig.directAllDocument) extensions.push(...DOCUMENT_EXTENSIONS);
|
|
1840
|
+
if (mergedConfig.directAllImage) extensions.push(...IMAGE_EXTENSIONS);
|
|
1841
|
+
if (mergedConfig.directAllMedia) extensions.push(...MEDIA_EXTENSIONS);
|
|
1842
|
+
if (mergedConfig.directAllFont) extensions.push(...FONT_EXTENSIONS);
|
|
1843
|
+
if (mergedConfig.directAllCss) extensions.push(...CSS_EXTENSIONS);
|
|
1844
|
+
if (mergedConfig.directAllJs) extensions.push(...JS_EXTENSIONS);
|
|
1845
|
+
if (mergedConfig.directAllOther) extensions.push(...OTHER_EXTENSIONS);
|
|
1846
|
+
return [...new Set(extensions)];
|
|
1847
|
+
},
|
|
1859
1848
|
/**
|
|
1860
1849
|
* 设置网络拦截规则(资源屏蔽 + CDN 直连)
|
|
1861
1850
|
*
|
|
1862
1851
|
* @param {import('playwright').Page} page - Playwright Page 对象
|
|
1863
1852
|
* @param {Object} [options] - 配置选项
|
|
1864
|
-
* @param {string[]} [options.directDomains] -
|
|
1853
|
+
* @param {string[]} [options.directDomains] - [已过时] 需要直连的域名列表,请改用 directConfig.domains
|
|
1854
|
+
* @param {string[]} [options.directExtensions] - 需要直连的扩展名(如 .js/.css)
|
|
1855
|
+
* @param {Object} [options.directConfig] - 直连配置(参考 blockingConfig),支持 domains/directAllXxx 系列开关
|
|
1865
1856
|
* @param {Object} [options.blockingConfig] - 资源屏蔽配置
|
|
1866
1857
|
* @param {boolean} [options.fallbackToProxy] - 直连失败时是否回退到代理(默认 true)
|
|
1867
1858
|
* @returns {Promise<void>}
|
|
@@ -1869,12 +1860,27 @@ var Interception = {
|
|
|
1869
1860
|
async setup(page, options = {}) {
|
|
1870
1861
|
const {
|
|
1871
1862
|
directDomains = [],
|
|
1863
|
+
directExtensions = [],
|
|
1864
|
+
directConfig = {},
|
|
1872
1865
|
blockingConfig = {},
|
|
1873
1866
|
fallbackToProxy = true
|
|
1874
1867
|
} = options;
|
|
1875
1868
|
const mergedBlockingConfig = { ...DEFAULT_BLOCKING_CONFIG, ...blockingConfig };
|
|
1869
|
+
const mergedDirectConfig = { ...DEFAULT_DIRECT_CONFIG, ...directConfig };
|
|
1876
1870
|
const blockedExtensions = this.getBlockedExtensions(mergedBlockingConfig);
|
|
1877
|
-
const
|
|
1871
|
+
const configuredDirectExtensions = this.getDirectExtensions(mergedDirectConfig);
|
|
1872
|
+
const normalizedDirectDomains = Array.from(
|
|
1873
|
+
new Set(
|
|
1874
|
+
[...directDomains, ...mergedDirectConfig.domains || []].map((domain) => String(domain || "").toLowerCase().trim()).filter(Boolean)
|
|
1875
|
+
)
|
|
1876
|
+
);
|
|
1877
|
+
const hasDirectDomains = normalizedDirectDomains.length > 0;
|
|
1878
|
+
const normalizedDirectExtensions = Array.from(
|
|
1879
|
+
new Set(
|
|
1880
|
+
[...directExtensions, ...configuredDirectExtensions].map((ext) => String(ext || "").toLowerCase().trim()).filter(Boolean).map((ext) => ext.startsWith(".") ? ext : `.${ext}`)
|
|
1881
|
+
)
|
|
1882
|
+
);
|
|
1883
|
+
const hasDirectExtensions = normalizedDirectExtensions.length > 0;
|
|
1878
1884
|
const enabledCategories = [];
|
|
1879
1885
|
if (mergedBlockingConfig.blockArchive) enabledCategories.push("\u538B\u7F29\u5305");
|
|
1880
1886
|
if (mergedBlockingConfig.blockExecutable) enabledCategories.push("\u53EF\u6267\u884C\u6587\u4EF6");
|
|
@@ -1884,7 +1890,10 @@ var Interception = {
|
|
|
1884
1890
|
if (mergedBlockingConfig.blockFont) enabledCategories.push("\u5B57\u4F53");
|
|
1885
1891
|
if (mergedBlockingConfig.blockCss) enabledCategories.push("CSS");
|
|
1886
1892
|
if (mergedBlockingConfig.blockOther) enabledCategories.push("\u5176\u4ED6");
|
|
1887
|
-
|
|
1893
|
+
const directRules = [];
|
|
1894
|
+
if (hasDirectDomains) directRules.push(`\u76F4\u8FDE\u57DF\u540D: [${normalizedDirectDomains.length} \u4E2A]`);
|
|
1895
|
+
if (hasDirectExtensions) directRules.push(`\u76F4\u8FDE\u6269\u5C55\u540D: [${normalizedDirectExtensions.join(", ")}]`);
|
|
1896
|
+
logger9.start("setup", directRules.length > 0 ? `${directRules.join(" | ")} | \u5C4F\u853D: [${enabledCategories.join(", ")}]` : `\u4EC5\u8D44\u6E90\u5C4F\u853D\u6A21\u5F0F | \u5C4F\u853D: [${enabledCategories.join(", ")}]`);
|
|
1888
1897
|
await page.route("**/*", async (route) => {
|
|
1889
1898
|
let handled = false;
|
|
1890
1899
|
try {
|
|
@@ -1899,14 +1908,16 @@ var Interception = {
|
|
|
1899
1908
|
handled = true;
|
|
1900
1909
|
return;
|
|
1901
1910
|
}
|
|
1902
|
-
let
|
|
1911
|
+
let isDirectByDomain = false;
|
|
1903
1912
|
if (hasDirectDomains) {
|
|
1904
1913
|
try {
|
|
1905
1914
|
const hostname = new URL(url).hostname;
|
|
1906
|
-
|
|
1915
|
+
isDirectByDomain = normalizedDirectDomains.some((domain) => hostname.startsWith(domain));
|
|
1907
1916
|
} catch (e) {
|
|
1908
1917
|
}
|
|
1909
1918
|
}
|
|
1919
|
+
const isDirectByExtension = hasDirectExtensions ? normalizedDirectExtensions.some((ext) => urlPath.endsWith(ext)) : false;
|
|
1920
|
+
const isDirect = isDirectByDomain || isDirectByExtension;
|
|
1910
1921
|
if (isDirect) {
|
|
1911
1922
|
try {
|
|
1912
1923
|
const reqHeaders = await request.allHeaders();
|