@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 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 maxHeight = document.body.scrollHeight;
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 > maxHeight) {
375
- maxHeight = el.scrollHeight;
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 maxHeight;
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: maxScrollHeight + buffer
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
- await page.evaluate(() => {
401
- document.querySelectorAll(".__pk_expanded__").forEach((el) => {
402
- el.style.overflow = el.dataset.pkOrigOverflow || "";
403
- el.style.height = el.dataset.pkOrigHeight || "";
404
- el.style.maxHeight = el.dataset.pkOrigMaxHeight || "";
405
- delete el.dataset.pkOrigOverflow;
406
- delete el.dataset.pkOrigHeight;
407
- delete el.dataset.pkOrigMaxHeight;
408
- el.classList.remove("__pk_expanded__");
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
- if (originalViewport) {
412
- await page.setViewportSize(originalViewport);
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: