@skrillex1224/playwright-toolkit 2.1.37 → 2.1.38
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 +201 -18
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +201 -18
- package/dist/index.js.map +4 -4
- package/index.d.ts +54 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -357,6 +357,8 @@ var Utils = {
|
|
|
357
357
|
* @param {import('playwright').Page} page - Playwright page 对象
|
|
358
358
|
* @param {Object} [options] - 配置选项
|
|
359
359
|
* @param {number} [options.buffer] - 额外缓冲高度 (默认: 视口高度的一半)
|
|
360
|
+
* @param {boolean} [options.restore] - 截图后是否恢复页面高度和样式 (默认: false)
|
|
361
|
+
* @param {number} [options.maxHeight] - 最大截图高度 (默认: 8000px)
|
|
360
362
|
* @returns {Promise<string>} - base64 编码的 PNG 图片
|
|
361
363
|
*/
|
|
362
364
|
async fullPageScreenshot(page, options = {}) {
|
|
@@ -364,15 +366,17 @@ var Utils = {
|
|
|
364
366
|
const originalViewport = page.viewportSize();
|
|
365
367
|
const defaultBuffer = Math.round((originalViewport?.height || 1080) / 2);
|
|
366
368
|
const buffer = options.buffer ?? defaultBuffer;
|
|
369
|
+
const restore = options.restore ?? false;
|
|
370
|
+
const maxHeight = options.maxHeight ?? 8e3;
|
|
367
371
|
try {
|
|
368
372
|
const maxScrollHeight = await page.evaluate(() => {
|
|
369
|
-
let
|
|
373
|
+
let maxHeight2 = document.body.scrollHeight;
|
|
370
374
|
document.querySelectorAll("*").forEach((el) => {
|
|
371
375
|
const style = window.getComputedStyle(el);
|
|
372
376
|
const overflowY = style.overflowY;
|
|
373
377
|
if ((overflowY === "auto" || overflowY === "scroll") && el.scrollHeight > el.clientHeight) {
|
|
374
|
-
if (el.scrollHeight >
|
|
375
|
-
|
|
378
|
+
if (el.scrollHeight > maxHeight2) {
|
|
379
|
+
maxHeight2 = el.scrollHeight;
|
|
376
380
|
}
|
|
377
381
|
el.dataset.pkOrigOverflow = el.style.overflow;
|
|
378
382
|
el.dataset.pkOrigHeight = el.style.height;
|
|
@@ -383,11 +387,12 @@ var Utils = {
|
|
|
383
387
|
el.style.maxHeight = "none";
|
|
384
388
|
}
|
|
385
389
|
});
|
|
386
|
-
return
|
|
390
|
+
return maxHeight2;
|
|
387
391
|
});
|
|
392
|
+
const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
|
|
388
393
|
await page.setViewportSize({
|
|
389
394
|
width: originalViewport?.width || 1280,
|
|
390
|
-
height:
|
|
395
|
+
height: targetHeight
|
|
391
396
|
});
|
|
392
397
|
await (0, import_delay.default)(1e3);
|
|
393
398
|
const buffer_ = await page.screenshot({
|
|
@@ -397,19 +402,21 @@ var Utils = {
|
|
|
397
402
|
logger2.success("fullPageScreenshot", `captured ${Math.round(buffer_.length / 1024)} KB`);
|
|
398
403
|
return buffer_.toString("base64");
|
|
399
404
|
} finally {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
405
|
+
if (restore) {
|
|
406
|
+
await page.evaluate(() => {
|
|
407
|
+
document.querySelectorAll(".__pk_expanded__").forEach((el) => {
|
|
408
|
+
el.style.overflow = el.dataset.pkOrigOverflow || "";
|
|
409
|
+
el.style.height = el.dataset.pkOrigHeight || "";
|
|
410
|
+
el.style.maxHeight = el.dataset.pkOrigMaxHeight || "";
|
|
411
|
+
delete el.dataset.pkOrigOverflow;
|
|
412
|
+
delete el.dataset.pkOrigHeight;
|
|
413
|
+
delete el.dataset.pkOrigMaxHeight;
|
|
414
|
+
el.classList.remove("__pk_expanded__");
|
|
415
|
+
});
|
|
409
416
|
});
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
417
|
+
if (originalViewport) {
|
|
418
|
+
await page.setViewportSize(originalViewport);
|
|
419
|
+
}
|
|
413
420
|
}
|
|
414
421
|
}
|
|
415
422
|
}
|
|
@@ -1532,6 +1539,181 @@ function isIgnorableError(error) {
|
|
|
1532
1539
|
return msg.includes("already handled") || msg.includes("Target closed") || msg.includes("closed");
|
|
1533
1540
|
}
|
|
1534
1541
|
|
|
1542
|
+
// src/mutation.js
|
|
1543
|
+
var import_uuid2 = require("uuid");
|
|
1544
|
+
var logger9 = createLogger("Mutation");
|
|
1545
|
+
function generateKey(prefix) {
|
|
1546
|
+
return `__${prefix}_${(0, import_uuid2.v4)().replace(/-/g, "_")}`;
|
|
1547
|
+
}
|
|
1548
|
+
var Mutation = {
|
|
1549
|
+
/**
|
|
1550
|
+
* 等待 DOM 元素稳定(无变化)
|
|
1551
|
+
* 使用 MutationObserver 监控指定元素,当元素持续一段时间无变化时 resolve
|
|
1552
|
+
*
|
|
1553
|
+
* @param {import('playwright').Page} page - Playwright page 对象
|
|
1554
|
+
* @param {string | string[]} selectors - 要监控的 CSS 选择器,单个或多个
|
|
1555
|
+
* @param {Object} [options] - 配置选项
|
|
1556
|
+
* @param {number} [options.stableTime] - 无变化持续时间后 resolve (毫秒, 默认: 5000)
|
|
1557
|
+
* @param {number} [options.timeout] - 整体超时时间 (毫秒, 默认: 60000)
|
|
1558
|
+
* @param {Function} [options.onMutation] - 变化时的回调钩子 (mutationCount: number) => void
|
|
1559
|
+
* @returns {Promise<{ mutationCount: number, stableTime: number }>} - 返回变化次数和稳定时长
|
|
1560
|
+
*/
|
|
1561
|
+
async waitForStable(page, selectors, options = {}) {
|
|
1562
|
+
const selectorList = Array.isArray(selectors) ? selectors : [selectors];
|
|
1563
|
+
const stableTime = options.stableTime ?? 5e3;
|
|
1564
|
+
const timeout = options.timeout ?? 6e4;
|
|
1565
|
+
const onMutation = options.onMutation;
|
|
1566
|
+
logger9.start("waitForStable", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668, \u7A33\u5B9A\u65F6\u95F4=${stableTime}ms`);
|
|
1567
|
+
const eventName = generateKey("pk_mut_evt");
|
|
1568
|
+
const callbackName = generateKey("pk_mut_cb");
|
|
1569
|
+
if (onMutation) {
|
|
1570
|
+
try {
|
|
1571
|
+
await page.exposeFunction(callbackName, (count) => {
|
|
1572
|
+
try {
|
|
1573
|
+
onMutation(count);
|
|
1574
|
+
} catch (e) {
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
} catch (e) {
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
const result = await page.evaluate(
|
|
1581
|
+
async ({ selectorList: selectorList2, stableTime: stableTime2, timeout: timeout2, eventName: eventName2, callbackName: callbackName2, hasCallback }) => {
|
|
1582
|
+
return new Promise((resolve, reject) => {
|
|
1583
|
+
let mutationCount = 0;
|
|
1584
|
+
let stableTimer = null;
|
|
1585
|
+
let timeoutTimer = null;
|
|
1586
|
+
const observers = [];
|
|
1587
|
+
const cleanup = () => {
|
|
1588
|
+
observers.forEach((obs) => obs.disconnect());
|
|
1589
|
+
if (stableTimer) clearTimeout(stableTimer);
|
|
1590
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1591
|
+
};
|
|
1592
|
+
const resetStableTimer = () => {
|
|
1593
|
+
if (stableTimer) clearTimeout(stableTimer);
|
|
1594
|
+
stableTimer = setTimeout(() => {
|
|
1595
|
+
cleanup();
|
|
1596
|
+
resolve({ mutationCount, stableTime: stableTime2 });
|
|
1597
|
+
}, stableTime2);
|
|
1598
|
+
};
|
|
1599
|
+
timeoutTimer = setTimeout(() => {
|
|
1600
|
+
cleanup();
|
|
1601
|
+
reject(new Error(`waitForStable \u8D85\u65F6 (${timeout2}ms), \u5DF2\u68C0\u6D4B\u5230 ${mutationCount} \u6B21\u53D8\u5316`));
|
|
1602
|
+
}, timeout2);
|
|
1603
|
+
selectorList2.forEach((selector) => {
|
|
1604
|
+
const elements = document.querySelectorAll(selector);
|
|
1605
|
+
elements.forEach((element) => {
|
|
1606
|
+
const observer = new MutationObserver((mutations) => {
|
|
1607
|
+
mutationCount += mutations.length;
|
|
1608
|
+
if (hasCallback && window[callbackName2]) {
|
|
1609
|
+
window[callbackName2](mutationCount);
|
|
1610
|
+
}
|
|
1611
|
+
resetStableTimer();
|
|
1612
|
+
});
|
|
1613
|
+
observer.observe(element, {
|
|
1614
|
+
childList: true,
|
|
1615
|
+
subtree: true,
|
|
1616
|
+
characterData: true,
|
|
1617
|
+
attributes: true
|
|
1618
|
+
});
|
|
1619
|
+
observers.push(observer);
|
|
1620
|
+
});
|
|
1621
|
+
});
|
|
1622
|
+
if (observers.length === 0) {
|
|
1623
|
+
cleanup();
|
|
1624
|
+
resolve({ mutationCount: 0, stableTime: 0 });
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
resetStableTimer();
|
|
1628
|
+
});
|
|
1629
|
+
},
|
|
1630
|
+
{ selectorList, stableTime, timeout, eventName, callbackName, hasCallback: !!onMutation }
|
|
1631
|
+
);
|
|
1632
|
+
logger9.success("waitForStable", `DOM \u7A33\u5B9A, \u603B\u5171 ${result.mutationCount} \u6B21\u53D8\u5316`);
|
|
1633
|
+
return result;
|
|
1634
|
+
},
|
|
1635
|
+
/**
|
|
1636
|
+
* 创建一个持续监控 DOM 变化的监控器
|
|
1637
|
+
*
|
|
1638
|
+
* @param {import('playwright').Page} page - Playwright page 对象
|
|
1639
|
+
* @param {string | string[]} selectors - 要监控的 CSS 选择器
|
|
1640
|
+
* @param {Object} [options] - 配置选项
|
|
1641
|
+
* @param {Function} [options.onMutation] - 变化时的回调 (mutationCount: number) => void
|
|
1642
|
+
* @returns {Promise<{ stop: () => Promise<{ totalMutations: number }> }>} - 返回停止函数
|
|
1643
|
+
*/
|
|
1644
|
+
async createMonitor(page, selectors, options = {}) {
|
|
1645
|
+
const selectorList = Array.isArray(selectors) ? selectors : [selectors];
|
|
1646
|
+
const onMutation = options.onMutation;
|
|
1647
|
+
logger9.start("createMonitor", `\u76D1\u63A7 ${selectorList.length} \u4E2A\u9009\u62E9\u5668`);
|
|
1648
|
+
const monitorKey = generateKey("pk_mon");
|
|
1649
|
+
const callbackName = generateKey("pk_mon_cb");
|
|
1650
|
+
const cleanerName = generateKey("pk_mon_clean");
|
|
1651
|
+
if (onMutation) {
|
|
1652
|
+
try {
|
|
1653
|
+
await page.exposeFunction(callbackName, (count) => {
|
|
1654
|
+
try {
|
|
1655
|
+
onMutation(count);
|
|
1656
|
+
} catch (e) {
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
} catch (e) {
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
await page.evaluate(({ selectorList: selectorList2, monitorKey: monitorKey2, callbackName: callbackName2, cleanerName: cleanerName2, hasCallback }) => {
|
|
1663
|
+
const monitor = {
|
|
1664
|
+
observers: [],
|
|
1665
|
+
totalMutations: 0,
|
|
1666
|
+
running: true
|
|
1667
|
+
};
|
|
1668
|
+
selectorList2.forEach((selector) => {
|
|
1669
|
+
const elements = document.querySelectorAll(selector);
|
|
1670
|
+
elements.forEach((element) => {
|
|
1671
|
+
const observer = new MutationObserver((mutations) => {
|
|
1672
|
+
if (!monitor.running) return;
|
|
1673
|
+
monitor.totalMutations += mutations.length;
|
|
1674
|
+
if (hasCallback && window[callbackName2]) {
|
|
1675
|
+
window[callbackName2](monitor.totalMutations);
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
observer.observe(element, {
|
|
1679
|
+
childList: true,
|
|
1680
|
+
subtree: true,
|
|
1681
|
+
characterData: true,
|
|
1682
|
+
attributes: true
|
|
1683
|
+
});
|
|
1684
|
+
monitor.observers.push(observer);
|
|
1685
|
+
});
|
|
1686
|
+
});
|
|
1687
|
+
window[monitorKey2] = monitor;
|
|
1688
|
+
window[cleanerName2] = () => {
|
|
1689
|
+
monitor.running = false;
|
|
1690
|
+
monitor.observers.forEach((obs) => obs.disconnect());
|
|
1691
|
+
const total = monitor.totalMutations;
|
|
1692
|
+
delete window[monitorKey2];
|
|
1693
|
+
delete window[cleanerName2];
|
|
1694
|
+
return total;
|
|
1695
|
+
};
|
|
1696
|
+
}, { selectorList, monitorKey, callbackName, cleanerName, hasCallback: !!onMutation });
|
|
1697
|
+
logger9.success("createMonitor", "\u76D1\u63A7\u5668\u5DF2\u542F\u52A8");
|
|
1698
|
+
return {
|
|
1699
|
+
stop: async () => {
|
|
1700
|
+
let totalMutations = 0;
|
|
1701
|
+
try {
|
|
1702
|
+
totalMutations = await page.evaluate((cleanerName2) => {
|
|
1703
|
+
if (window[cleanerName2]) {
|
|
1704
|
+
return window[cleanerName2]();
|
|
1705
|
+
}
|
|
1706
|
+
return 0;
|
|
1707
|
+
}, cleanerName);
|
|
1708
|
+
} catch (e) {
|
|
1709
|
+
}
|
|
1710
|
+
logger9.success("createMonitor.stop", `\u76D1\u63A7\u5DF2\u505C\u6B62, \u5171 ${totalMutations} \u6B21\u53D8\u5316`);
|
|
1711
|
+
return { totalMutations };
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1535
1717
|
// index.js
|
|
1536
1718
|
var usePlaywrightToolKit = () => {
|
|
1537
1719
|
return {
|
|
@@ -1545,7 +1727,8 @@ var usePlaywrightToolKit = () => {
|
|
|
1545
1727
|
Captcha,
|
|
1546
1728
|
Sse,
|
|
1547
1729
|
Errors: errors_exports,
|
|
1548
|
-
Interception
|
|
1730
|
+
Interception,
|
|
1731
|
+
Mutation
|
|
1549
1732
|
};
|
|
1550
1733
|
};
|
|
1551
1734
|
// Annotate the CommonJS export names for ESM import in node:
|