@letsscrapedata/controller 0.0.51 → 0.0.53
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 +2189 -132
- package/dist/index.d.cts +145 -28
- package/dist/index.d.ts +145 -28
- package/dist/index.js +2191 -134
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -31,6 +31,32 @@ import { getCurrentUnixTime as getCurrentUnixTime2, sleep } from "@letsscrapedat
|
|
|
31
31
|
import EventEmitter from "events";
|
|
32
32
|
import { getCurrentUnixTime, unreachable as unreachable2 } from "@letsscrapedata/utils";
|
|
33
33
|
|
|
34
|
+
// src/utils/common.ts
|
|
35
|
+
function convertDataAttributeName(attr) {
|
|
36
|
+
if (!attr.startsWith("data-")) {
|
|
37
|
+
return "";
|
|
38
|
+
}
|
|
39
|
+
const parts = attr.split("-");
|
|
40
|
+
let name = parts[1];
|
|
41
|
+
for (const part of parts.slice(2)) {
|
|
42
|
+
if (!part) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
name = `${name}${part[1].toUpperCase()}${part.slice(1).toLowerCase()}`;
|
|
46
|
+
}
|
|
47
|
+
return name;
|
|
48
|
+
}
|
|
49
|
+
function getIframeSelector(iframeOption) {
|
|
50
|
+
const { src = "", id = "", selector = "" } = iframeOption;
|
|
51
|
+
if (typeof src === "string" && src) {
|
|
52
|
+
return `iframe[src^="${src}"]`;
|
|
53
|
+
} else if (typeof id === "string" && id) {
|
|
54
|
+
return `iframe[id="${id}"]`;
|
|
55
|
+
} else {
|
|
56
|
+
return selector;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
34
60
|
// src/playwright/element.ts
|
|
35
61
|
import { unreachable } from "@letsscrapedata/utils";
|
|
36
62
|
var PlaywrightElement = class _PlaywrightElement {
|
|
@@ -51,6 +77,29 @@ var PlaywrightElement = class _PlaywrightElement {
|
|
|
51
77
|
const names = await this.#locator.evaluate((node) => node.getAttributeNames());
|
|
52
78
|
return names;
|
|
53
79
|
}
|
|
80
|
+
async dataset() {
|
|
81
|
+
try {
|
|
82
|
+
const dataset = await this.#locator.evaluate((node) => node.dataset);
|
|
83
|
+
return dataset;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async evaluate(func, args) {
|
|
89
|
+
try {
|
|
90
|
+
const frame = this.#frame;
|
|
91
|
+
;
|
|
92
|
+
if (typeof frame.parentFrame === "function") {
|
|
93
|
+
return await frame.evaluate(func, args);
|
|
94
|
+
} else {
|
|
95
|
+
const locator = this.#frame.owner();
|
|
96
|
+
return await locator.evaluate(func, args);
|
|
97
|
+
}
|
|
98
|
+
} catch (err) {
|
|
99
|
+
logerr(err);
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
54
103
|
/*
|
|
55
104
|
async #getChildFrame(parentFrame: Frame, iframeOption: IframeOption): Promise<Frame | null> {
|
|
56
105
|
if (!parentFrame) {
|
|
@@ -84,22 +133,15 @@ var PlaywrightElement = class _PlaywrightElement {
|
|
|
84
133
|
return null;
|
|
85
134
|
}
|
|
86
135
|
*/
|
|
87
|
-
#getIframeSelector(iframeOption) {
|
|
88
|
-
const { src = "", selector = "" } = iframeOption;
|
|
89
|
-
if (!src && !selector) {
|
|
90
|
-
throw new Error("Invalid parent frame");
|
|
91
|
-
}
|
|
92
|
-
return selector ? selector : `iframe[src^="${src}"]`;
|
|
93
|
-
}
|
|
94
136
|
async #getChildFrameLocator(parent, iframeOption) {
|
|
95
|
-
return parent.frameLocator(
|
|
137
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
96
138
|
}
|
|
97
139
|
async #getDescendantFrame(parent, iframeOptions) {
|
|
98
140
|
try {
|
|
99
141
|
if (iframeOptions.length <= 0) {
|
|
100
142
|
return null;
|
|
101
143
|
}
|
|
102
|
-
let frameLocator = parent.frameLocator(
|
|
144
|
+
let frameLocator = parent.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
103
145
|
for (const iframeOption of iframeOptions.slice(1)) {
|
|
104
146
|
if (!frameLocator) {
|
|
105
147
|
return null;
|
|
@@ -116,12 +158,12 @@ var PlaywrightElement = class _PlaywrightElement {
|
|
|
116
158
|
let frame = this.#frame;
|
|
117
159
|
const retObj = { frame, locators: [] };
|
|
118
160
|
if (iframeOptions.length > 0) {
|
|
119
|
-
|
|
120
|
-
if (!
|
|
161
|
+
const childFrame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
162
|
+
if (!childFrame) {
|
|
121
163
|
return retObj;
|
|
122
164
|
}
|
|
123
|
-
retObj.frame =
|
|
124
|
-
parent =
|
|
165
|
+
retObj.frame = childFrame;
|
|
166
|
+
parent = childFrame;
|
|
125
167
|
}
|
|
126
168
|
try {
|
|
127
169
|
let locators = [];
|
|
@@ -306,6 +348,7 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
306
348
|
#page;
|
|
307
349
|
#status;
|
|
308
350
|
#pageId;
|
|
351
|
+
#closeWhenFree;
|
|
309
352
|
#resquestInterceptionOptions;
|
|
310
353
|
#responseInterceptionOptions;
|
|
311
354
|
#client;
|
|
@@ -436,29 +479,57 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
436
479
|
return null;
|
|
437
480
|
}
|
|
438
481
|
*/
|
|
439
|
-
#
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
throw new Error("Invalid parent frame");
|
|
482
|
+
async #findDescendantFrame(src, id) {
|
|
483
|
+
if (!this.#page) {
|
|
484
|
+
throw new Error("No valid page");
|
|
443
485
|
}
|
|
444
|
-
|
|
486
|
+
const frames = this.#page.frames();
|
|
487
|
+
for (const frame of frames) {
|
|
488
|
+
const url = frame.url();
|
|
489
|
+
if (typeof src === "string" && src) {
|
|
490
|
+
if (url.startsWith(src)) {
|
|
491
|
+
return frame;
|
|
492
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
493
|
+
return frame;
|
|
494
|
+
}
|
|
495
|
+
} else if (src instanceof RegExp) {
|
|
496
|
+
if (url.match(src)) {
|
|
497
|
+
return frame;
|
|
498
|
+
}
|
|
499
|
+
} else if (id) {
|
|
500
|
+
const element = await frame.frameElement();
|
|
501
|
+
if (element) {
|
|
502
|
+
const frameId = await frame.evaluate(([ele, attr]) => ele.getAttribute(attr), [element, "id"]);
|
|
503
|
+
if (frameId === id) {
|
|
504
|
+
return frame;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
445
510
|
}
|
|
446
511
|
async #getChildFrameLocator(parent, iframeOption) {
|
|
447
|
-
return parent.frameLocator(
|
|
512
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
448
513
|
}
|
|
449
|
-
async #
|
|
514
|
+
async #getDescendantFrame(mainFrame, iframeOptions) {
|
|
450
515
|
try {
|
|
451
516
|
if (iframeOptions.length <= 0) {
|
|
452
517
|
return null;
|
|
453
518
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
519
|
+
if (iframeOptions.length === 1 && !iframeOptions[0].selector) {
|
|
520
|
+
const { src = "", id = "" } = iframeOptions[0];
|
|
521
|
+
const frame = await this.#findDescendantFrame(src, id);
|
|
522
|
+
return frame;
|
|
523
|
+
} else {
|
|
524
|
+
let frameLocator = mainFrame.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
525
|
+
for (const iframeOption of iframeOptions.slice(1)) {
|
|
526
|
+
if (!frameLocator) {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
frameLocator = await this.#getChildFrameLocator(frameLocator, iframeOption);
|
|
458
530
|
}
|
|
459
|
-
|
|
531
|
+
return frameLocator;
|
|
460
532
|
}
|
|
461
|
-
return frameLocator;
|
|
462
533
|
} catch (err) {
|
|
463
534
|
throw new Error(`No child iframe: ${JSON.stringify(iframeOptions)}`);
|
|
464
535
|
}
|
|
@@ -470,7 +541,7 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
470
541
|
let frame = this.#page.mainFrame();
|
|
471
542
|
const retObj = { frame, locators: [] };
|
|
472
543
|
if (iframeOptions.length > 0) {
|
|
473
|
-
frame = await this.#
|
|
544
|
+
frame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
474
545
|
if (!frame) {
|
|
475
546
|
return retObj;
|
|
476
547
|
}
|
|
@@ -543,12 +614,32 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
543
614
|
const { browserIdx = 0, browserContextIdx = 0, pageIdx = 0, openType = "other", openTime = currentTime, lastStatusUpdateTime = currentTime, taskId = 0, relatedId = 0, misc = {} } = pageInfo ? pageInfo : {};
|
|
544
615
|
this.#page.pageInfo = { browserIdx, browserContextIdx, pageIdx, openType, openTime, lastStatusUpdateTime, taskId, relatedId, misc };
|
|
545
616
|
this.#pageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
617
|
+
this.#closeWhenFree = false;
|
|
546
618
|
this.#resquestInterceptionOptions = [];
|
|
547
619
|
this.#responseInterceptionOptions = [];
|
|
548
620
|
this.#client = null;
|
|
549
621
|
this.#responseCb = null;
|
|
550
622
|
this.#addPageOn();
|
|
551
623
|
}
|
|
624
|
+
async addPreloadScript(scriptOrFunc, arg) {
|
|
625
|
+
if (!this.#page) {
|
|
626
|
+
throw new Error("No valid page");
|
|
627
|
+
}
|
|
628
|
+
if (typeof scriptOrFunc === "string") {
|
|
629
|
+
await this.#page.addInitScript({ content: scriptOrFunc });
|
|
630
|
+
} else if (typeof scriptOrFunc === "function") {
|
|
631
|
+
await this.#page.addInitScript(scriptOrFunc, arg);
|
|
632
|
+
} else {
|
|
633
|
+
throw new Error(`Invalid type of scriptOrFunc ${typeof scriptOrFunc}`);
|
|
634
|
+
}
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
async addScriptTag(options) {
|
|
638
|
+
if (!this.#page) {
|
|
639
|
+
throw new Error("No valid page");
|
|
640
|
+
}
|
|
641
|
+
return this.#page.addScriptTag(options);
|
|
642
|
+
}
|
|
552
643
|
apiContext() {
|
|
553
644
|
return this.browserContext().apiContext();
|
|
554
645
|
}
|
|
@@ -608,7 +699,7 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
608
699
|
}
|
|
609
700
|
async close() {
|
|
610
701
|
if (this.#status === "closed") {
|
|
611
|
-
|
|
702
|
+
logwarn(`Page ${this.#pageId} is already closed.`);
|
|
612
703
|
return true;
|
|
613
704
|
} else if (this.#status === "busy") {
|
|
614
705
|
throw new Error(`Page ${this.#pageId} cannot be closed because it is busy.`);
|
|
@@ -621,13 +712,16 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
621
712
|
this.#status = "closed";
|
|
622
713
|
return true;
|
|
623
714
|
}
|
|
715
|
+
closeWhenFree() {
|
|
716
|
+
return this.#closeWhenFree;
|
|
717
|
+
}
|
|
624
718
|
async content(iframeOptions = []) {
|
|
625
719
|
if (!this.#page) {
|
|
626
720
|
throw new Error("No valid page");
|
|
627
721
|
}
|
|
628
722
|
let content = "";
|
|
629
723
|
if (iframeOptions.length > 0) {
|
|
630
|
-
const frameLocator = await this.#
|
|
724
|
+
const frameLocator = await this.#getDescendantFrame(this.#page.mainFrame(), iframeOptions);
|
|
631
725
|
if (frameLocator) {
|
|
632
726
|
content = await frameLocator.locator(":root").evaluate(() => document.documentElement.outerHTML);
|
|
633
727
|
}
|
|
@@ -649,11 +743,18 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
649
743
|
const height = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
650
744
|
return height;
|
|
651
745
|
}
|
|
652
|
-
async
|
|
746
|
+
async evaluate(func, args) {
|
|
747
|
+
if (!this.#page) {
|
|
748
|
+
throw new Error("No valid page");
|
|
749
|
+
}
|
|
750
|
+
return this.#page.evaluate(func, args);
|
|
751
|
+
}
|
|
752
|
+
async exposeFunction(name, callbackFunction) {
|
|
653
753
|
if (!this.#page) {
|
|
654
754
|
throw new Error("No valid page");
|
|
655
755
|
}
|
|
656
|
-
|
|
756
|
+
await this.#page.exposeFunction(name, callbackFunction);
|
|
757
|
+
return;
|
|
657
758
|
}
|
|
658
759
|
async findElement(selectorOrXpath, iframeOptions = []) {
|
|
659
760
|
if (!this.#page) {
|
|
@@ -691,7 +792,7 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
691
792
|
}
|
|
692
793
|
async free() {
|
|
693
794
|
if (this.#status === "free") {
|
|
694
|
-
|
|
795
|
+
logwarn(`Page ${this.#pageId} is already free.`);
|
|
695
796
|
}
|
|
696
797
|
this.#status = "free";
|
|
697
798
|
await this.clearRequestInterceptions();
|
|
@@ -831,6 +932,10 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
831
932
|
}
|
|
832
933
|
return response;
|
|
833
934
|
}
|
|
935
|
+
setCloseWhenFree(closeWhenFree) {
|
|
936
|
+
this.#closeWhenFree = closeWhenFree;
|
|
937
|
+
return true;
|
|
938
|
+
}
|
|
834
939
|
async setCookies(cookies) {
|
|
835
940
|
if (!this.#page) {
|
|
836
941
|
throw new Error("No valid page");
|
|
@@ -1014,7 +1119,7 @@ var PlaywrightPage = class extends EventEmitter {
|
|
|
1014
1119
|
}
|
|
1015
1120
|
const actOptions = Array.isArray(options) ? options : [options];
|
|
1016
1121
|
if (actOptions.length <= 0) {
|
|
1017
|
-
|
|
1122
|
+
logwarn("Invalid paras in setResponseInterception");
|
|
1018
1123
|
return false;
|
|
1019
1124
|
}
|
|
1020
1125
|
const firstResponseInterception = this.#responseInterceptionOptions.length <= 0;
|
|
@@ -1498,7 +1603,11 @@ var PlaywrightBrowserContext = class extends EventEmitter2 {
|
|
|
1498
1603
|
};
|
|
1499
1604
|
|
|
1500
1605
|
// src/playwright/browser.ts
|
|
1501
|
-
var PlaywrightBrowser = class extends EventEmitter3 {
|
|
1606
|
+
var PlaywrightBrowser = class _PlaywrightBrowser extends EventEmitter3 {
|
|
1607
|
+
static #supportedBrowserTypes = ["chromium", "firefox", "webkit"];
|
|
1608
|
+
static doesSupport(browserType) {
|
|
1609
|
+
return _PlaywrightBrowser.#supportedBrowserTypes.includes(browserType);
|
|
1610
|
+
}
|
|
1502
1611
|
#browser;
|
|
1503
1612
|
#browserIdx;
|
|
1504
1613
|
#pid;
|
|
@@ -1527,7 +1636,7 @@ var PlaywrightBrowser = class extends EventEmitter3 {
|
|
|
1527
1636
|
return this.#options.maxPageFreeSeconds ? this.#options.maxPageFreeSeconds : 900;
|
|
1528
1637
|
}
|
|
1529
1638
|
// constructor: called only by LsdBrowserController.launch/connect
|
|
1530
|
-
constructor(browser,
|
|
1639
|
+
constructor(browser, browserType, browserCreateMethod, options, browserIdx = 0, pid = 0) {
|
|
1531
1640
|
if (!browser || typeof browser.contexts !== "function") {
|
|
1532
1641
|
throw new Error(`Invalid playwright browser parameter`);
|
|
1533
1642
|
}
|
|
@@ -1539,7 +1648,10 @@ var PlaywrightBrowser = class extends EventEmitter3 {
|
|
|
1539
1648
|
this.#createTime = getCurrentUnixTime3();
|
|
1540
1649
|
this.#lsdBrowserContexts = [];
|
|
1541
1650
|
this.#browserControllerType = "playwright";
|
|
1542
|
-
this.#browserType =
|
|
1651
|
+
this.#browserType = browserType;
|
|
1652
|
+
if (!_PlaywrightBrowser.#supportedBrowserTypes.includes(browserType)) {
|
|
1653
|
+
throw new Error(`Browser controller ${this.#browserControllerType} doesnot support browserType ${browserType}`);
|
|
1654
|
+
}
|
|
1543
1655
|
this.#browserCreationMethod = browserCreateMethod;
|
|
1544
1656
|
this.#headless = headless;
|
|
1545
1657
|
this.#proxy = options?.proxy ? Object.assign({}, options.proxy) : null;
|
|
@@ -1716,6 +1828,26 @@ var PuppeteerElement = class _PuppeteerElement {
|
|
|
1716
1828
|
const names = await this.#frame.evaluate((ele) => ele.getAttributeNames(), this.#$ele);
|
|
1717
1829
|
return names;
|
|
1718
1830
|
}
|
|
1831
|
+
async dataset() {
|
|
1832
|
+
try {
|
|
1833
|
+
const attributeNames = await this.attributeNames();
|
|
1834
|
+
const dataset = {};
|
|
1835
|
+
for (const attributeName of attributeNames) {
|
|
1836
|
+
if (!attributeName.startsWith("data-")) {
|
|
1837
|
+
continue;
|
|
1838
|
+
}
|
|
1839
|
+
const val = await this.attribute(attributeName);
|
|
1840
|
+
const key = convertDataAttributeName(attributeName);
|
|
1841
|
+
dataset[key] = val;
|
|
1842
|
+
}
|
|
1843
|
+
return dataset;
|
|
1844
|
+
} catch (err) {
|
|
1845
|
+
return {};
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
async evaluate(func, args) {
|
|
1849
|
+
return await this.#frame.evaluate(func, args);
|
|
1850
|
+
}
|
|
1719
1851
|
async #getChildFrame(parentFrame, iframeOption) {
|
|
1720
1852
|
if (!parentFrame) {
|
|
1721
1853
|
throw new Error("Invalid parent frame");
|
|
@@ -1765,13 +1897,13 @@ var PuppeteerElement = class _PuppeteerElement {
|
|
|
1765
1897
|
let frame = this.#frame;
|
|
1766
1898
|
const retObj = { frame, elementHandles: [] };
|
|
1767
1899
|
if (iframeOptions.length > 0) {
|
|
1768
|
-
|
|
1769
|
-
if (!
|
|
1900
|
+
const childFrame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
1901
|
+
if (!childFrame) {
|
|
1770
1902
|
return retObj;
|
|
1771
1903
|
}
|
|
1772
|
-
retObj.frame =
|
|
1904
|
+
retObj.frame = childFrame;
|
|
1773
1905
|
absolute = true;
|
|
1774
|
-
parent =
|
|
1906
|
+
parent = childFrame;
|
|
1775
1907
|
}
|
|
1776
1908
|
try {
|
|
1777
1909
|
if (selector.startsWith("./") || selector.startsWith("/") || selector.startsWith("..")) {
|
|
@@ -1973,6 +2105,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
1973
2105
|
#page;
|
|
1974
2106
|
#status;
|
|
1975
2107
|
#pageId;
|
|
2108
|
+
#closeWhenFree;
|
|
1976
2109
|
#requestInterceptionNum;
|
|
1977
2110
|
#responseInterceptionNum;
|
|
1978
2111
|
#client;
|
|
@@ -2047,15 +2180,41 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2047
2180
|
});
|
|
2048
2181
|
return true;
|
|
2049
2182
|
}
|
|
2183
|
+
async #findDescendantFrame(src, id) {
|
|
2184
|
+
if (!this.#page) {
|
|
2185
|
+
throw new Error("No valid page");
|
|
2186
|
+
}
|
|
2187
|
+
const frames = this.#page.frames();
|
|
2188
|
+
for (const frame of frames) {
|
|
2189
|
+
const url = frame.url();
|
|
2190
|
+
if (typeof src === "string" && src) {
|
|
2191
|
+
if (url.startsWith(src)) {
|
|
2192
|
+
return frame;
|
|
2193
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
2194
|
+
return frame;
|
|
2195
|
+
}
|
|
2196
|
+
} else if (src instanceof RegExp) {
|
|
2197
|
+
if (url.match(src)) {
|
|
2198
|
+
return frame;
|
|
2199
|
+
}
|
|
2200
|
+
} else if (id) {
|
|
2201
|
+
const element = await frame.frameElement();
|
|
2202
|
+
if (element) {
|
|
2203
|
+
const frameId = await frame.evaluate((ele, attr) => ele.getAttribute(attr), element, "id");
|
|
2204
|
+
if (frameId === id) {
|
|
2205
|
+
return frame;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
return null;
|
|
2211
|
+
}
|
|
2050
2212
|
async #getChildFrame(parentFrame, iframeOption) {
|
|
2051
2213
|
if (!parentFrame) {
|
|
2052
2214
|
throw new Error("Invalid parent frame");
|
|
2053
2215
|
}
|
|
2054
2216
|
let iframe = null;
|
|
2055
|
-
let { src = ""
|
|
2056
|
-
if (!src && !selector) {
|
|
2057
|
-
throw new Error("Invalid IframeOption");
|
|
2058
|
-
}
|
|
2217
|
+
let { src = "" } = iframeOption;
|
|
2059
2218
|
if (src) {
|
|
2060
2219
|
const childFrames = parentFrame.childFrames();
|
|
2061
2220
|
for (const childFrame of childFrames) {
|
|
@@ -2073,7 +2232,8 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2073
2232
|
}
|
|
2074
2233
|
}
|
|
2075
2234
|
} else {
|
|
2076
|
-
const
|
|
2235
|
+
const frameSelector = getIframeSelector(iframeOption);
|
|
2236
|
+
const $eleIframe = await parentFrame.$(frameSelector);
|
|
2077
2237
|
if ($eleIframe) {
|
|
2078
2238
|
iframe = await $eleIframe.contentFrame();
|
|
2079
2239
|
return iframe;
|
|
@@ -2083,11 +2243,16 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2083
2243
|
}
|
|
2084
2244
|
async #getDescendantFrame(parentFrame, iframeOptions) {
|
|
2085
2245
|
let iframe = parentFrame;
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2246
|
+
if (iframeOptions.length === 1 && !iframeOptions[0].selector) {
|
|
2247
|
+
const { src = "", id = "" } = iframeOptions[0];
|
|
2248
|
+
iframe = await this.#findDescendantFrame(src, id);
|
|
2249
|
+
} else {
|
|
2250
|
+
for (const iframeOption of iframeOptions) {
|
|
2251
|
+
if (!iframe) {
|
|
2252
|
+
return null;
|
|
2253
|
+
}
|
|
2254
|
+
iframe = await this.#getChildFrame(iframe, iframeOption);
|
|
2089
2255
|
}
|
|
2090
|
-
iframe = await this.#getChildFrame(iframe, iframeOption);
|
|
2091
2256
|
}
|
|
2092
2257
|
return iframe;
|
|
2093
2258
|
}
|
|
@@ -2116,7 +2281,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2116
2281
|
}
|
|
2117
2282
|
return retObj;
|
|
2118
2283
|
} catch (err) {
|
|
2119
|
-
|
|
2284
|
+
loginfo(err);
|
|
2120
2285
|
return retObj;
|
|
2121
2286
|
}
|
|
2122
2287
|
}
|
|
@@ -2169,11 +2334,31 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2169
2334
|
const { browserIdx = 0, browserContextIdx = 0, pageIdx = 0, openType = "other", openTime = currentTime, lastStatusUpdateTime = currentTime, taskId = 0, relatedId = 0, misc = {} } = pageInfo ? pageInfo : {};
|
|
2170
2335
|
this.#page.pageInfo = { browserIdx, browserContextIdx, pageIdx, openType, openTime, lastStatusUpdateTime, taskId, relatedId, misc };
|
|
2171
2336
|
this.#pageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
2337
|
+
this.#closeWhenFree = false;
|
|
2172
2338
|
this.#requestInterceptionNum = 0;
|
|
2173
2339
|
this.#responseInterceptionNum = 0;
|
|
2174
2340
|
this.#client = null;
|
|
2175
2341
|
this.#addPageOn();
|
|
2176
2342
|
}
|
|
2343
|
+
async addPreloadScript(scriptOrFunc, arg) {
|
|
2344
|
+
if (!this.#page) {
|
|
2345
|
+
throw new Error("No valid page");
|
|
2346
|
+
}
|
|
2347
|
+
if (typeof scriptOrFunc === "string") {
|
|
2348
|
+
await this.#page.evaluateOnNewDocument(scriptOrFunc);
|
|
2349
|
+
} else if (typeof scriptOrFunc === "function") {
|
|
2350
|
+
await this.#page.evaluateOnNewDocument(scriptOrFunc, arg);
|
|
2351
|
+
} else {
|
|
2352
|
+
throw new Error(`Invalid type of scriptOrFunc ${typeof scriptOrFunc}`);
|
|
2353
|
+
}
|
|
2354
|
+
return true;
|
|
2355
|
+
}
|
|
2356
|
+
async addScriptTag(options) {
|
|
2357
|
+
if (!this.#page) {
|
|
2358
|
+
throw new Error("No valid page");
|
|
2359
|
+
}
|
|
2360
|
+
return this.#page.addScriptTag(options);
|
|
2361
|
+
}
|
|
2177
2362
|
apiContext() {
|
|
2178
2363
|
throw new Error("Not supported in PuppeteerPage.");
|
|
2179
2364
|
}
|
|
@@ -2230,7 +2415,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2230
2415
|
}
|
|
2231
2416
|
async close() {
|
|
2232
2417
|
if (this.#status === "closed") {
|
|
2233
|
-
|
|
2418
|
+
logwarn(`Page ${this.#pageId} is already closed.`);
|
|
2234
2419
|
return true;
|
|
2235
2420
|
} else if (this.#status === "busy") {
|
|
2236
2421
|
throw new Error(`Page ${this.#pageId} cannot be closed because it is busy.`);
|
|
@@ -2243,6 +2428,9 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2243
2428
|
this.#status = "closed";
|
|
2244
2429
|
return true;
|
|
2245
2430
|
}
|
|
2431
|
+
closeWhenFree() {
|
|
2432
|
+
return this.#closeWhenFree;
|
|
2433
|
+
}
|
|
2246
2434
|
async content(iframeOptions = []) {
|
|
2247
2435
|
if (!this.#page) {
|
|
2248
2436
|
throw new Error("No valid page");
|
|
@@ -2271,11 +2459,18 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2271
2459
|
const height = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
2272
2460
|
return height;
|
|
2273
2461
|
}
|
|
2274
|
-
async
|
|
2462
|
+
async evaluate(func, args) {
|
|
2463
|
+
if (!this.#page) {
|
|
2464
|
+
throw new Error("No valid page");
|
|
2465
|
+
}
|
|
2466
|
+
return this.#page.evaluate(func, args);
|
|
2467
|
+
}
|
|
2468
|
+
async exposeFunction(name, callbackFunction) {
|
|
2275
2469
|
if (!this.#page) {
|
|
2276
2470
|
throw new Error("No valid page");
|
|
2277
2471
|
}
|
|
2278
|
-
|
|
2472
|
+
await this.#page.exposeFunction(name, callbackFunction);
|
|
2473
|
+
return;
|
|
2279
2474
|
}
|
|
2280
2475
|
async findElement(selectorOrXpath, iframeOptions = []) {
|
|
2281
2476
|
if (!this.#page) {
|
|
@@ -2313,7 +2508,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2313
2508
|
}
|
|
2314
2509
|
async free() {
|
|
2315
2510
|
if (this.#status === "free") {
|
|
2316
|
-
|
|
2511
|
+
logwarn(`Page ${this.#pageId} is already free.`);
|
|
2317
2512
|
}
|
|
2318
2513
|
this.#status = "free";
|
|
2319
2514
|
await this.clearRequestInterceptions();
|
|
@@ -2456,6 +2651,10 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2456
2651
|
}
|
|
2457
2652
|
return response;
|
|
2458
2653
|
}
|
|
2654
|
+
setCloseWhenFree(closeWhenFree) {
|
|
2655
|
+
this.#closeWhenFree = closeWhenFree;
|
|
2656
|
+
return true;
|
|
2657
|
+
}
|
|
2459
2658
|
async setCookies(cookies) {
|
|
2460
2659
|
if (!this.#page) {
|
|
2461
2660
|
throw new Error("No valid page");
|
|
@@ -2531,7 +2730,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2531
2730
|
}
|
|
2532
2731
|
const actOptions = Array.isArray(options) ? options : [options];
|
|
2533
2732
|
if (actOptions.length <= 0) {
|
|
2534
|
-
|
|
2733
|
+
logwarn("Invalid paras in setRequestInterception");
|
|
2535
2734
|
return false;
|
|
2536
2735
|
}
|
|
2537
2736
|
if (this.#requestInterceptionNum <= 0) {
|
|
@@ -2567,7 +2766,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2567
2766
|
await request.continue();
|
|
2568
2767
|
return true;
|
|
2569
2768
|
} catch (err) {
|
|
2570
|
-
|
|
2769
|
+
logerr(err);
|
|
2571
2770
|
return false;
|
|
2572
2771
|
}
|
|
2573
2772
|
});
|
|
@@ -2579,7 +2778,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2579
2778
|
}
|
|
2580
2779
|
const actOptions = Array.isArray(options) ? options : [options];
|
|
2581
2780
|
if (actOptions.length <= 0) {
|
|
2582
|
-
|
|
2781
|
+
logwarn("Invalid paras in setResponseInterception");
|
|
2583
2782
|
return false;
|
|
2584
2783
|
}
|
|
2585
2784
|
this.#responseInterceptionNum++;
|
|
@@ -2634,7 +2833,7 @@ var PuppeteerPage = class extends EventEmitter4 {
|
|
|
2634
2833
|
}
|
|
2635
2834
|
return true;
|
|
2636
2835
|
} catch (err) {
|
|
2637
|
-
|
|
2836
|
+
logerr(err);
|
|
2638
2837
|
return false;
|
|
2639
2838
|
}
|
|
2640
2839
|
});
|
|
@@ -3092,7 +3291,11 @@ var PuppeteerBrowserContext = class extends EventEmitter5 {
|
|
|
3092
3291
|
};
|
|
3093
3292
|
|
|
3094
3293
|
// src/puppeteer/browser.ts
|
|
3095
|
-
var PuppeteerBrowser = class extends EventEmitter6 {
|
|
3294
|
+
var PuppeteerBrowser = class _PuppeteerBrowser extends EventEmitter6 {
|
|
3295
|
+
static #supportedBrowserTypes = ["chromium"];
|
|
3296
|
+
static doesSupport(browserType) {
|
|
3297
|
+
return _PuppeteerBrowser.#supportedBrowserTypes.includes(browserType);
|
|
3298
|
+
}
|
|
3096
3299
|
#browser;
|
|
3097
3300
|
#browserIdx;
|
|
3098
3301
|
#pid;
|
|
@@ -3124,7 +3327,7 @@ var PuppeteerBrowser = class extends EventEmitter6 {
|
|
|
3124
3327
|
return this.#options.userAgent ? this.#options.userAgent : "";
|
|
3125
3328
|
}
|
|
3126
3329
|
// constructor: called only by LsdBrowserController.launch/connect
|
|
3127
|
-
constructor(browser,
|
|
3330
|
+
constructor(browser, browserType, browserCreateMethod, options, browserIdx = 0, pid = 0) {
|
|
3128
3331
|
if (!browser || typeof browser.browserContexts !== "function") {
|
|
3129
3332
|
throw new Error(`Invalid puppeteer browser parameter`);
|
|
3130
3333
|
}
|
|
@@ -3136,7 +3339,10 @@ var PuppeteerBrowser = class extends EventEmitter6 {
|
|
|
3136
3339
|
this.#createTime = getCurrentUnixTime6();
|
|
3137
3340
|
this.#lsdBrowserContexts = [];
|
|
3138
3341
|
this.#browserControllerType = "puppeteer";
|
|
3139
|
-
this.#browserType =
|
|
3342
|
+
this.#browserType = browserType;
|
|
3343
|
+
if (!_PuppeteerBrowser.#supportedBrowserTypes.includes(browserType)) {
|
|
3344
|
+
throw new Error(`Browser controller ${this.#browserControllerType} doesnot support browserType ${browserType}`);
|
|
3345
|
+
}
|
|
3140
3346
|
this.#browserCreationMethod = browserCreateMethod;
|
|
3141
3347
|
this.#headless = headless;
|
|
3142
3348
|
this.#proxy = options?.proxy ? Object.assign({}, options.proxy) : null;
|
|
@@ -3294,6 +3500,22 @@ var CheerioElement = class _CheerioElement {
|
|
|
3294
3500
|
return Array.from(Object.keys(element.attribs));
|
|
3295
3501
|
}
|
|
3296
3502
|
}
|
|
3503
|
+
async dataset() {
|
|
3504
|
+
const attributeNames = await this.attributeNames();
|
|
3505
|
+
const dataset = {};
|
|
3506
|
+
for (const attributeName of attributeNames) {
|
|
3507
|
+
if (!attributeName.startsWith("data-")) {
|
|
3508
|
+
continue;
|
|
3509
|
+
}
|
|
3510
|
+
const val = await this.attribute(attributeName);
|
|
3511
|
+
const key = convertDataAttributeName(attributeName);
|
|
3512
|
+
dataset[key] = val;
|
|
3513
|
+
}
|
|
3514
|
+
return dataset;
|
|
3515
|
+
}
|
|
3516
|
+
async evaluate() {
|
|
3517
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3518
|
+
}
|
|
3297
3519
|
#findNodes(selector, absolute) {
|
|
3298
3520
|
if (selector.startsWith("./") || selector.startsWith("/")) {
|
|
3299
3521
|
throw new Error("Do not support XPath in cheerio.");
|
|
@@ -3414,6 +3636,12 @@ var CheerioPage = class extends EventEmitter7 {
|
|
|
3414
3636
|
_origPage() {
|
|
3415
3637
|
throw new Error("Method not implemented.");
|
|
3416
3638
|
}
|
|
3639
|
+
async addPreloadScript() {
|
|
3640
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3641
|
+
}
|
|
3642
|
+
addScriptTag() {
|
|
3643
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3644
|
+
}
|
|
3417
3645
|
apiContext() {
|
|
3418
3646
|
throw new Error("Not supported in CheerioPage.");
|
|
3419
3647
|
}
|
|
@@ -3441,13 +3669,19 @@ var CheerioPage = class extends EventEmitter7 {
|
|
|
3441
3669
|
async close() {
|
|
3442
3670
|
throw new Error("Not supported in CheerioPage.");
|
|
3443
3671
|
}
|
|
3672
|
+
closeWhenFree() {
|
|
3673
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3674
|
+
}
|
|
3444
3675
|
async content() {
|
|
3445
3676
|
throw new Error("Not supported in CheerioPage.");
|
|
3446
3677
|
}
|
|
3447
3678
|
async cookies() {
|
|
3448
3679
|
throw new Error("Not supported in CheerioPage.");
|
|
3449
3680
|
}
|
|
3450
|
-
async
|
|
3681
|
+
async evaluate() {
|
|
3682
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3683
|
+
}
|
|
3684
|
+
exposeFunction() {
|
|
3451
3685
|
throw new Error("Not supported in CheerioPage.");
|
|
3452
3686
|
}
|
|
3453
3687
|
#findNodes(selector) {
|
|
@@ -3544,6 +3778,9 @@ var CheerioPage = class extends EventEmitter7 {
|
|
|
3544
3778
|
async sendCDPMessage() {
|
|
3545
3779
|
throw new Error("Method not implemented.");
|
|
3546
3780
|
}
|
|
3781
|
+
setCloseWhenFree() {
|
|
3782
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3783
|
+
}
|
|
3547
3784
|
async setCookies() {
|
|
3548
3785
|
throw new Error("Not supported in CheerioPage.");
|
|
3549
3786
|
}
|
|
@@ -3600,89 +3837,1863 @@ var CheerioPage = class extends EventEmitter7 {
|
|
|
3600
3837
|
// src/controller/controller.ts
|
|
3601
3838
|
import os from "os";
|
|
3602
3839
|
import puppeteer from "puppeteer";
|
|
3603
|
-
import playwright, { request as
|
|
3604
|
-
import
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
import
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3840
|
+
import playwright, { request as apiRequestInPlaywright } from "playwright";
|
|
3841
|
+
import patchright from "patchright";
|
|
3842
|
+
|
|
3843
|
+
// src/patchright/browser.ts
|
|
3844
|
+
import EventEmitter10 from "events";
|
|
3845
|
+
import { getCurrentUnixTime as getCurrentUnixTime9, getPerformanceOfPidTree as getPerformanceOfPidTree3 } from "@letsscrapedata/utils";
|
|
3846
|
+
|
|
3847
|
+
// src/patchright/context.ts
|
|
3848
|
+
import EventEmitter9 from "events";
|
|
3849
|
+
import { getCurrentUnixTime as getCurrentUnixTime8, sleep as sleep3 } from "@letsscrapedata/utils";
|
|
3850
|
+
|
|
3851
|
+
// src/patchright/page.ts
|
|
3852
|
+
import EventEmitter8 from "events";
|
|
3853
|
+
import { getCurrentUnixTime as getCurrentUnixTime7, unreachable as unreachable6 } from "@letsscrapedata/utils";
|
|
3854
|
+
|
|
3855
|
+
// src/patchright/element.ts
|
|
3856
|
+
import { unreachable as unreachable5 } from "@letsscrapedata/utils";
|
|
3857
|
+
var PatchrightElement = class _PatchrightElement {
|
|
3858
|
+
#frame;
|
|
3859
|
+
#locator;
|
|
3860
|
+
constructor(locator, frame) {
|
|
3861
|
+
if (!frame.locator || !locator.click) {
|
|
3862
|
+
throw new Error("Invalid paras in new PatchrightElement");
|
|
3620
3863
|
}
|
|
3621
|
-
this.#
|
|
3622
|
-
|
|
3623
|
-
this.#playwrightBrowserTypes = { chromium: chromium2, firefox: firefox2, webkit: webkit2 };
|
|
3624
|
-
this.#osPlatform = os.platform();
|
|
3625
|
-
this.#nextBrowserIdx = 1;
|
|
3626
|
-
_LsdBrowserController.#forbidConstructor = true;
|
|
3864
|
+
this.#frame = frame;
|
|
3865
|
+
this.#locator = locator;
|
|
3627
3866
|
}
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
} else if (connectFlag) {
|
|
3632
|
-
throw new Error(`playwright only can connect to chromium browser, not support ${browserType} browser`);
|
|
3633
|
-
} else if (browserType === "firefox") {
|
|
3634
|
-
return this.#playwrightBrowserTypes.firefox;
|
|
3635
|
-
} else if (browserType === "webkit") {
|
|
3636
|
-
return this.#playwrightBrowserTypes.webkit;
|
|
3637
|
-
} else {
|
|
3638
|
-
throw new Error(`Invalid playwright browserType ${browserType}`);
|
|
3639
|
-
}
|
|
3867
|
+
async attribute(attributeName) {
|
|
3868
|
+
const attributeValue = await this.#locator.getAttribute(attributeName);
|
|
3869
|
+
return attributeValue ? attributeValue : "";
|
|
3640
3870
|
}
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3871
|
+
async attributeNames() {
|
|
3872
|
+
const names = await this.#locator.evaluate((node) => node.getAttributeNames());
|
|
3873
|
+
return names;
|
|
3874
|
+
}
|
|
3875
|
+
async dataset() {
|
|
3876
|
+
try {
|
|
3877
|
+
const dataset = await this.#locator.evaluate((node) => node.dataset);
|
|
3878
|
+
return dataset;
|
|
3879
|
+
} catch (err) {
|
|
3880
|
+
return {};
|
|
3646
3881
|
}
|
|
3647
3882
|
}
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3883
|
+
async evaluate(func, args) {
|
|
3884
|
+
try {
|
|
3885
|
+
const frame = this.#frame;
|
|
3886
|
+
;
|
|
3887
|
+
if (typeof frame.parentFrame === "function") {
|
|
3888
|
+
return await frame.evaluate(func, args);
|
|
3889
|
+
} else {
|
|
3890
|
+
const locator = this.#frame.owner();
|
|
3891
|
+
return await locator.evaluate(func, args);
|
|
3892
|
+
}
|
|
3893
|
+
} catch (err) {
|
|
3894
|
+
logerr(err);
|
|
3895
|
+
return "";
|
|
3653
3896
|
}
|
|
3654
|
-
return true;
|
|
3655
3897
|
}
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3898
|
+
/*
|
|
3899
|
+
async #getChildFrame(parentFrame: Frame, iframeOption: IframeOption): Promise<Frame | null> {
|
|
3900
|
+
if (!parentFrame) {
|
|
3901
|
+
throw new Error("Invalid parent frame");
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3904
|
+
let { src = "" } = iframeOption;
|
|
3905
|
+
if (!src) {
|
|
3906
|
+
throw new Error("Invalid src in IframeOption");
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
// src: use childFrames()
|
|
3910
|
+
const childFrames = parentFrame.childFrames();
|
|
3911
|
+
for (const childFrame of childFrames) {
|
|
3912
|
+
const url = childFrame.url();
|
|
3913
|
+
if (typeof src === "string") {
|
|
3914
|
+
// src: string
|
|
3915
|
+
if (url.startsWith(src)) {
|
|
3916
|
+
return childFrame;
|
|
3917
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
3918
|
+
return childFrame;
|
|
3919
|
+
}
|
|
3668
3920
|
} else {
|
|
3669
|
-
|
|
3921
|
+
// src: RegExp
|
|
3922
|
+
if (url.match(src)) {
|
|
3923
|
+
return childFrame;
|
|
3924
|
+
}
|
|
3670
3925
|
}
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
return null;
|
|
3929
|
+
}
|
|
3930
|
+
*/
|
|
3931
|
+
async #getChildFrameLocator(parent, iframeOption) {
|
|
3932
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
3933
|
+
}
|
|
3934
|
+
async #getDescendantFrame(parent, iframeOptions) {
|
|
3935
|
+
try {
|
|
3936
|
+
if (iframeOptions.length <= 0) {
|
|
3937
|
+
return null;
|
|
3938
|
+
}
|
|
3939
|
+
let frameLocator = parent.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
3940
|
+
for (const iframeOption of iframeOptions.slice(1)) {
|
|
3941
|
+
if (!frameLocator) {
|
|
3942
|
+
return null;
|
|
3677
3943
|
}
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3944
|
+
frameLocator = await this.#getChildFrameLocator(frameLocator, iframeOption);
|
|
3945
|
+
}
|
|
3946
|
+
return frameLocator;
|
|
3947
|
+
} catch (err) {
|
|
3948
|
+
throw new Error(`No child iframe: ${JSON.stringify(iframeOptions)}`);
|
|
3681
3949
|
}
|
|
3682
|
-
return true;
|
|
3683
3950
|
}
|
|
3684
|
-
async
|
|
3685
|
-
let
|
|
3951
|
+
async #findElementHandles(selector, absolute = false, iframeOptions = []) {
|
|
3952
|
+
let parent = absolute ? this.#frame : this.#locator;
|
|
3953
|
+
let frame = this.#frame;
|
|
3954
|
+
const retObj = { frame, locators: [] };
|
|
3955
|
+
if (iframeOptions.length > 0) {
|
|
3956
|
+
const childFrame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
3957
|
+
if (!childFrame) {
|
|
3958
|
+
return retObj;
|
|
3959
|
+
}
|
|
3960
|
+
retObj.frame = childFrame;
|
|
3961
|
+
parent = childFrame;
|
|
3962
|
+
}
|
|
3963
|
+
try {
|
|
3964
|
+
let locators = [];
|
|
3965
|
+
if (selector.startsWith("./") || selector.startsWith("/") || selector.startsWith("..")) {
|
|
3966
|
+
locators = await parent.locator(`xpath=${selector}`).all();
|
|
3967
|
+
} else {
|
|
3968
|
+
if (selector !== ".") {
|
|
3969
|
+
locators = await parent.locator(selector).all();
|
|
3970
|
+
} else {
|
|
3971
|
+
locators = [this.#locator];
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
retObj.locators = locators;
|
|
3975
|
+
return retObj;
|
|
3976
|
+
} catch (err) {
|
|
3977
|
+
loginfo(err);
|
|
3978
|
+
return retObj;
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
async findElement(selectorOrXpath, iframeOptions = [], absolute = false) {
|
|
3982
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
3983
|
+
if (!Array.isArray(selectors)) {
|
|
3984
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElement`);
|
|
3985
|
+
}
|
|
3986
|
+
for (const selector of selectors) {
|
|
3987
|
+
const { frame, locators } = await this.#findElementHandles(selector, absolute, iframeOptions);
|
|
3988
|
+
if (locators.length > 0) {
|
|
3989
|
+
const playwrightElement = new _PatchrightElement(locators[0], frame);
|
|
3990
|
+
return playwrightElement;
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
return null;
|
|
3994
|
+
}
|
|
3995
|
+
async findElements(selectorOrXpath, iframeOptions = [], absolute = false) {
|
|
3996
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
3997
|
+
if (!Array.isArray(selectors)) {
|
|
3998
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElements`);
|
|
3999
|
+
}
|
|
4000
|
+
for (const selector of selectors) {
|
|
4001
|
+
const { frame, locators } = await this.#findElementHandles(selector, absolute, iframeOptions);
|
|
4002
|
+
if (locators.length > 0) {
|
|
4003
|
+
const playwrightElements = locators.map((locator) => new _PatchrightElement(locator, frame));
|
|
4004
|
+
return playwrightElements;
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
return [];
|
|
4008
|
+
}
|
|
4009
|
+
async hasAttribute(attributeName) {
|
|
4010
|
+
const hasFlag = await this.#locator.evaluate((node, attr) => node.hasAttribute(attr), attributeName);
|
|
4011
|
+
return hasFlag;
|
|
4012
|
+
}
|
|
4013
|
+
async innerHtml() {
|
|
4014
|
+
const html = await this.#locator.innerHTML();
|
|
4015
|
+
return html;
|
|
4016
|
+
}
|
|
4017
|
+
async innerText(onlyChild = false) {
|
|
4018
|
+
let text = "";
|
|
4019
|
+
if (onlyChild) {
|
|
4020
|
+
text = await this.#locator.evaluate((node) => {
|
|
4021
|
+
let child = node.firstChild;
|
|
4022
|
+
let texts = [];
|
|
4023
|
+
while (child) {
|
|
4024
|
+
if (child.nodeType == 3) {
|
|
4025
|
+
texts.push(child.data);
|
|
4026
|
+
}
|
|
4027
|
+
child = child.nextSibling;
|
|
4028
|
+
}
|
|
4029
|
+
return texts.join(" ");
|
|
4030
|
+
});
|
|
4031
|
+
} else {
|
|
4032
|
+
text = await this.#locator.innerText();
|
|
4033
|
+
}
|
|
4034
|
+
return text;
|
|
4035
|
+
}
|
|
4036
|
+
async outerHtml() {
|
|
4037
|
+
const html = await this.#locator.evaluate((node) => node.outerHTML);
|
|
4038
|
+
return html;
|
|
4039
|
+
}
|
|
4040
|
+
async textContent() {
|
|
4041
|
+
const text = await this.#locator.textContent();
|
|
4042
|
+
return text ? text : "";
|
|
4043
|
+
}
|
|
4044
|
+
async click(options = {}) {
|
|
4045
|
+
const { button, clickCount: count, delay, position: offset, clickType = "click" } = options;
|
|
4046
|
+
const actOptions = { button, count, delay, offset };
|
|
4047
|
+
if (clickType === "click") {
|
|
4048
|
+
await this.#locator.click(actOptions);
|
|
4049
|
+
} else if (clickType === "evaluate") {
|
|
4050
|
+
await this.#locator.evaluate(async (ev) => await ev.click());
|
|
4051
|
+
} else {
|
|
4052
|
+
unreachable5(clickType);
|
|
4053
|
+
}
|
|
4054
|
+
return true;
|
|
4055
|
+
}
|
|
4056
|
+
async focus() {
|
|
4057
|
+
await this.#locator.focus();
|
|
4058
|
+
return true;
|
|
4059
|
+
}
|
|
4060
|
+
async hover() {
|
|
4061
|
+
await this.#locator.hover();
|
|
4062
|
+
return true;
|
|
4063
|
+
}
|
|
4064
|
+
async input(value, options = {}) {
|
|
4065
|
+
const { delay = 0, replace = false, enter = false } = options;
|
|
4066
|
+
if (replace) {
|
|
4067
|
+
await this.#locator.click({ button: "left", clickCount: 3 });
|
|
4068
|
+
}
|
|
4069
|
+
if (delay > 0) {
|
|
4070
|
+
await this.#locator.fill(value);
|
|
4071
|
+
} else {
|
|
4072
|
+
await this.#locator.fill(value);
|
|
4073
|
+
}
|
|
4074
|
+
if (enter) {
|
|
4075
|
+
await this.#locator.press("Enter");
|
|
4076
|
+
}
|
|
4077
|
+
return true;
|
|
4078
|
+
}
|
|
4079
|
+
async press(key, options = {}) {
|
|
4080
|
+
await this.#locator.press(key, options);
|
|
4081
|
+
return true;
|
|
4082
|
+
}
|
|
4083
|
+
async screenshot(options) {
|
|
4084
|
+
return await this.#locator.screenshot(options);
|
|
4085
|
+
}
|
|
4086
|
+
async scrollIntoView() {
|
|
4087
|
+
await this.#locator.scrollIntoViewIfNeeded();
|
|
4088
|
+
return true;
|
|
4089
|
+
}
|
|
4090
|
+
async select(options) {
|
|
4091
|
+
const { type, values = [], labels = [], indexes = [] } = options;
|
|
4092
|
+
switch (type) {
|
|
4093
|
+
case "value":
|
|
4094
|
+
if (values.length > 0) {
|
|
4095
|
+
await this.#locator.selectOption(values);
|
|
4096
|
+
}
|
|
4097
|
+
break;
|
|
4098
|
+
case "label":
|
|
4099
|
+
if (labels.length > 0) {
|
|
4100
|
+
await this.#locator.selectOption(labels.map((label) => {
|
|
4101
|
+
return { label };
|
|
4102
|
+
}));
|
|
4103
|
+
}
|
|
4104
|
+
break;
|
|
4105
|
+
case "index":
|
|
4106
|
+
if (indexes.length > 0) {
|
|
4107
|
+
const indexValues = await this.#locator.evaluate(
|
|
4108
|
+
(node, indexes2) => {
|
|
4109
|
+
const options2 = node.options;
|
|
4110
|
+
const len = options2.length;
|
|
4111
|
+
const vals = [];
|
|
4112
|
+
for (const index of indexes2.filter((i) => i >= 0 && i < len)) {
|
|
4113
|
+
vals.push(options2[index].value);
|
|
4114
|
+
}
|
|
4115
|
+
return vals;
|
|
4116
|
+
},
|
|
4117
|
+
indexes
|
|
4118
|
+
);
|
|
4119
|
+
if (indexValues.length > 0) {
|
|
4120
|
+
await this.#locator.selectOption(indexValues);
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
break;
|
|
4124
|
+
default:
|
|
4125
|
+
unreachable5(type);
|
|
4126
|
+
}
|
|
4127
|
+
return true;
|
|
4128
|
+
}
|
|
4129
|
+
async setAttribute(attributeName, newValue) {
|
|
4130
|
+
await this.#locator.evaluate((node, argvs) => {
|
|
4131
|
+
node.setAttribute(argvs[0], argvs[1]);
|
|
4132
|
+
}, [attributeName, newValue]);
|
|
4133
|
+
return true;
|
|
4134
|
+
}
|
|
4135
|
+
_origElement() {
|
|
4136
|
+
return this.#locator;
|
|
4137
|
+
}
|
|
4138
|
+
};
|
|
4139
|
+
|
|
4140
|
+
// src/patchright/page.ts
|
|
4141
|
+
var PatchrightPage = class extends EventEmitter8 {
|
|
4142
|
+
#lsdBrowserContext;
|
|
4143
|
+
#page;
|
|
4144
|
+
#status;
|
|
4145
|
+
#pageId;
|
|
4146
|
+
#closeWhenFree;
|
|
4147
|
+
#resquestInterceptionOptions;
|
|
4148
|
+
#responseInterceptionOptions;
|
|
4149
|
+
#client;
|
|
4150
|
+
#responseCb;
|
|
4151
|
+
#hasValidUrl(page) {
|
|
4152
|
+
const url = page.url();
|
|
4153
|
+
return url.toLowerCase().startsWith("http");
|
|
4154
|
+
}
|
|
4155
|
+
async #clearCookies(page) {
|
|
4156
|
+
if (!this.#hasValidUrl(page)) {
|
|
4157
|
+
throw new Error("Please open related url before clearing cookies");
|
|
4158
|
+
}
|
|
4159
|
+
const browserContext = this.#lsdBrowserContext._origBrowserContext();
|
|
4160
|
+
if (!browserContext) {
|
|
4161
|
+
throw new Error(`Invalid LsdBrowserContext`);
|
|
4162
|
+
}
|
|
4163
|
+
const cookieItems = await this.#getCookies(page);
|
|
4164
|
+
const domainSet = new Set(cookieItems.map((c) => c.domain));
|
|
4165
|
+
if (domainSet.size !== 1) {
|
|
4166
|
+
logwarn(`Domains in clearCookies: ${Array.from(domainSet.values())}`);
|
|
4167
|
+
}
|
|
4168
|
+
for (const domain of domainSet.values()) {
|
|
4169
|
+
await browserContext.clearCookies({ domain });
|
|
4170
|
+
}
|
|
4171
|
+
return true;
|
|
4172
|
+
}
|
|
4173
|
+
async #getCookies(page) {
|
|
4174
|
+
if (!this.#hasValidUrl(page)) {
|
|
4175
|
+
throw new Error("Please open related url before getting cookies");
|
|
4176
|
+
}
|
|
4177
|
+
const browserContext = this.#lsdBrowserContext._origBrowserContext();
|
|
4178
|
+
if (!browserContext) {
|
|
4179
|
+
throw new Error(`Invalid LsdBrowserContext`);
|
|
4180
|
+
}
|
|
4181
|
+
const url = page.url();
|
|
4182
|
+
const origCookies = await browserContext.cookies(url);
|
|
4183
|
+
const cookies = origCookies.map((origCookie) => {
|
|
4184
|
+
const { name, value, domain, path, expires, httpOnly, secure, sameSite = "Lax" } = origCookie;
|
|
4185
|
+
return { name, value, domain, path, expires, httpOnly, secure, sameSite };
|
|
4186
|
+
});
|
|
4187
|
+
return cookies;
|
|
4188
|
+
}
|
|
4189
|
+
async #setCookies(page, cookies) {
|
|
4190
|
+
if (!page) {
|
|
4191
|
+
throw new Error("No valid page");
|
|
4192
|
+
}
|
|
4193
|
+
if (Array.isArray(cookies) && cookies.length > 0 && cookies.every((c) => typeof c.name === "string")) {
|
|
4194
|
+
const browserContext = this.#lsdBrowserContext._origBrowserContext();
|
|
4195
|
+
if (!browserContext) {
|
|
4196
|
+
throw new Error(`Invalid LsdBrowserContext`);
|
|
4197
|
+
}
|
|
4198
|
+
await browserContext.addCookies(cookies);
|
|
4199
|
+
return true;
|
|
4200
|
+
} else {
|
|
4201
|
+
return false;
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
async #clearLocalStorage(page) {
|
|
4205
|
+
if (!this.#hasValidUrl(page)) {
|
|
4206
|
+
throw new Error("Please open related url before clearing localStorage");
|
|
4207
|
+
}
|
|
4208
|
+
await page.evaluate(() => window.localStorage.clear());
|
|
4209
|
+
return true;
|
|
4210
|
+
}
|
|
4211
|
+
async #getLocalStorage(page) {
|
|
4212
|
+
if (!this.#hasValidUrl(page)) {
|
|
4213
|
+
throw new Error("Please open related url before getting localStorage");
|
|
4214
|
+
}
|
|
4215
|
+
const localStorageStr = await page.evaluate(() => JSON.stringify(window.localStorage));
|
|
4216
|
+
const localStorageObj = JSON.parse(localStorageStr);
|
|
4217
|
+
const localStorageItems = Object.keys(localStorageObj).map((name) => ({ name, value: localStorageObj[name] }));
|
|
4218
|
+
const url = new URL(page.url());
|
|
4219
|
+
return [{ origin: url.origin, localStorage: localStorageItems }];
|
|
4220
|
+
}
|
|
4221
|
+
async #setLocalStorage(page, localStorageItems) {
|
|
4222
|
+
if (!this.#hasValidUrl(page)) {
|
|
4223
|
+
throw new Error("Please open related url before setting localStorage");
|
|
4224
|
+
}
|
|
4225
|
+
await page.evaluate((items) => {
|
|
4226
|
+
for (const item of items) {
|
|
4227
|
+
window.localStorage.setItem(item.name, item.value);
|
|
4228
|
+
}
|
|
4229
|
+
}, localStorageItems);
|
|
4230
|
+
return true;
|
|
4231
|
+
}
|
|
4232
|
+
async #clearIndexedDB(page) {
|
|
4233
|
+
if (!this.#hasValidUrl(page)) {
|
|
4234
|
+
throw new Error("Please open related url before clearing indexedDB");
|
|
4235
|
+
}
|
|
4236
|
+
await page.evaluate(async () => {
|
|
4237
|
+
for (const db of await indexedDB.databases?.() || []) {
|
|
4238
|
+
if (db.name)
|
|
4239
|
+
indexedDB.deleteDatabase(db.name);
|
|
4240
|
+
}
|
|
4241
|
+
});
|
|
4242
|
+
return true;
|
|
4243
|
+
}
|
|
4244
|
+
/*
|
|
4245
|
+
async #getChildFrame(parentFrame: Frame, iframeOption: IframeOption): Promise<Frame | null> {
|
|
4246
|
+
if (!parentFrame) {
|
|
4247
|
+
throw new Error("Invalid parent frame");
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4250
|
+
let { src = "" } = iframeOption;
|
|
4251
|
+
if (!src) {
|
|
4252
|
+
throw new Error("Invalid src in IframeOption");
|
|
4253
|
+
}
|
|
4254
|
+
|
|
4255
|
+
// src: use childFrames()
|
|
4256
|
+
const childFrames = parentFrame.childFrames();
|
|
4257
|
+
for (const childFrame of childFrames) {
|
|
4258
|
+
const url = childFrame.url();
|
|
4259
|
+
if (typeof src === "string") {
|
|
4260
|
+
// src: string
|
|
4261
|
+
if (url.startsWith(src)) {
|
|
4262
|
+
return childFrame;
|
|
4263
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
4264
|
+
return childFrame;
|
|
4265
|
+
}
|
|
4266
|
+
} else {
|
|
4267
|
+
// src: RegExp
|
|
4268
|
+
if (url.match(src)) {
|
|
4269
|
+
return childFrame;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4274
|
+
return null;
|
|
4275
|
+
}
|
|
4276
|
+
*/
|
|
4277
|
+
async #findDescendantFrame(src, id) {
|
|
4278
|
+
if (!this.#page) {
|
|
4279
|
+
throw new Error("No valid page");
|
|
4280
|
+
}
|
|
4281
|
+
const frames = this.#page.frames();
|
|
4282
|
+
for (const frame of frames) {
|
|
4283
|
+
const url = frame.url();
|
|
4284
|
+
if (typeof src === "string" && src) {
|
|
4285
|
+
if (url.startsWith(src)) {
|
|
4286
|
+
return frame;
|
|
4287
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
4288
|
+
return frame;
|
|
4289
|
+
}
|
|
4290
|
+
} else if (src instanceof RegExp) {
|
|
4291
|
+
if (url.match(src)) {
|
|
4292
|
+
return frame;
|
|
4293
|
+
}
|
|
4294
|
+
} else if (id) {
|
|
4295
|
+
const element = await frame.frameElement();
|
|
4296
|
+
if (element) {
|
|
4297
|
+
const frameId = await frame.evaluate(([ele, attr]) => ele.getAttribute(attr), [element, "id"]);
|
|
4298
|
+
if (frameId === id) {
|
|
4299
|
+
return frame;
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
return null;
|
|
4305
|
+
}
|
|
4306
|
+
async #getChildFrameLocator(parent, iframeOption) {
|
|
4307
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
4308
|
+
}
|
|
4309
|
+
async #getDescendantFrame(mainFrame, iframeOptions) {
|
|
4310
|
+
try {
|
|
4311
|
+
if (iframeOptions.length <= 0) {
|
|
4312
|
+
return null;
|
|
4313
|
+
}
|
|
4314
|
+
if (iframeOptions.length === 1 && !iframeOptions[0].selector) {
|
|
4315
|
+
const { src = "", id = "" } = iframeOptions[0];
|
|
4316
|
+
const frame = await this.#findDescendantFrame(src, id);
|
|
4317
|
+
return frame;
|
|
4318
|
+
} else {
|
|
4319
|
+
let frameLocator = mainFrame.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
4320
|
+
for (const iframeOption of iframeOptions.slice(1)) {
|
|
4321
|
+
if (!frameLocator) {
|
|
4322
|
+
return null;
|
|
4323
|
+
}
|
|
4324
|
+
frameLocator = await this.#getChildFrameLocator(frameLocator, iframeOption);
|
|
4325
|
+
}
|
|
4326
|
+
return frameLocator;
|
|
4327
|
+
}
|
|
4328
|
+
} catch (err) {
|
|
4329
|
+
throw new Error(`No child iframe: ${JSON.stringify(iframeOptions)}`);
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
async #findElementHandles(selector, iframeOptions = []) {
|
|
4333
|
+
if (!this.#page) {
|
|
4334
|
+
throw new Error("No valid page");
|
|
4335
|
+
}
|
|
4336
|
+
let frame = this.#page.mainFrame();
|
|
4337
|
+
const retObj = { frame, locators: [] };
|
|
4338
|
+
if (iframeOptions.length > 0) {
|
|
4339
|
+
frame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
4340
|
+
if (!frame) {
|
|
4341
|
+
return retObj;
|
|
4342
|
+
}
|
|
4343
|
+
retObj.frame = frame;
|
|
4344
|
+
}
|
|
4345
|
+
try {
|
|
4346
|
+
let locators = [];
|
|
4347
|
+
if (selector.startsWith("./") || selector.startsWith("/") || selector.startsWith("..")) {
|
|
4348
|
+
locators = await frame.locator(`xpath=${selector}`).all();
|
|
4349
|
+
} else {
|
|
4350
|
+
if (selector !== ".") {
|
|
4351
|
+
locators = await frame.locator(selector).all();
|
|
4352
|
+
} else {
|
|
4353
|
+
throw new Error("Cannot use selector '.' on page");
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
retObj.locators = locators;
|
|
4357
|
+
return retObj;
|
|
4358
|
+
} catch (err) {
|
|
4359
|
+
loginfo(err);
|
|
4360
|
+
return retObj;
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
#addPageOn() {
|
|
4364
|
+
if (!this.#page) {
|
|
4365
|
+
throw new Error("No valid page");
|
|
4366
|
+
}
|
|
4367
|
+
const page = this.#page;
|
|
4368
|
+
const pageId = this.#pageId;
|
|
4369
|
+
page.on("close", async () => {
|
|
4370
|
+
loginfo(`##browser ${pageId} closed`);
|
|
4371
|
+
if (!page.pageInfo) {
|
|
4372
|
+
logerr(`Logic error in page.on("close")`);
|
|
4373
|
+
}
|
|
4374
|
+
this.emit("pageClose");
|
|
4375
|
+
this.#lsdBrowserContext.emit("pageClose", this);
|
|
4376
|
+
});
|
|
4377
|
+
page.on("popup", (p) => {
|
|
4378
|
+
if (p) {
|
|
4379
|
+
let evtData = null;
|
|
4380
|
+
const pageInfo = p.pageInfo;
|
|
4381
|
+
let popupPageId = "page";
|
|
4382
|
+
if (pageInfo) {
|
|
4383
|
+
const { browserIdx, browserContextIdx, pageIdx } = pageInfo;
|
|
4384
|
+
popupPageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
4385
|
+
pageInfo.openType = "popup";
|
|
4386
|
+
evtData = this.browserContext().page(pageIdx);
|
|
4387
|
+
if (evtData && page.pageInfo?.taskId) {
|
|
4388
|
+
pageInfo.relatedId = page.pageInfo.taskId;
|
|
4389
|
+
}
|
|
4390
|
+
} else {
|
|
4391
|
+
logerr(`##browser ${pageId} has popup without page.pageInfo`);
|
|
4392
|
+
}
|
|
4393
|
+
loginfo(`##browser ${pageId} has popup ${popupPageId}`);
|
|
4394
|
+
this.emit("pagePopup", evtData);
|
|
4395
|
+
} else {
|
|
4396
|
+
logerr(`##browser ${pageId} has popup page with null page`);
|
|
4397
|
+
}
|
|
4398
|
+
});
|
|
4399
|
+
}
|
|
4400
|
+
constructor(browserContext, page, pageInfo) {
|
|
4401
|
+
if (!browserContext.pages || !page?.goto) {
|
|
4402
|
+
throw new Error("Invalid paras in new LsdPage");
|
|
4403
|
+
}
|
|
4404
|
+
super();
|
|
4405
|
+
this.#lsdBrowserContext = browserContext;
|
|
4406
|
+
this.#page = page;
|
|
4407
|
+
this.#status = "free";
|
|
4408
|
+
const currentTime = getCurrentUnixTime7();
|
|
4409
|
+
const { browserIdx = 0, browserContextIdx = 0, pageIdx = 0, openType = "other", openTime = currentTime, lastStatusUpdateTime = currentTime, taskId = 0, relatedId = 0, misc = {} } = pageInfo ? pageInfo : {};
|
|
4410
|
+
this.#page.pageInfo = { browserIdx, browserContextIdx, pageIdx, openType, openTime, lastStatusUpdateTime, taskId, relatedId, misc };
|
|
4411
|
+
this.#pageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
4412
|
+
this.#closeWhenFree = false;
|
|
4413
|
+
this.#resquestInterceptionOptions = [];
|
|
4414
|
+
this.#responseInterceptionOptions = [];
|
|
4415
|
+
this.#client = null;
|
|
4416
|
+
this.#responseCb = null;
|
|
4417
|
+
this.#addPageOn();
|
|
4418
|
+
}
|
|
4419
|
+
async addPreloadScript(scriptOrFunc, arg) {
|
|
4420
|
+
if (!this.#page) {
|
|
4421
|
+
throw new Error("No valid page");
|
|
4422
|
+
}
|
|
4423
|
+
if (typeof scriptOrFunc === "string") {
|
|
4424
|
+
await this.#page.addInitScript({ content: scriptOrFunc });
|
|
4425
|
+
} else if (typeof scriptOrFunc === "function") {
|
|
4426
|
+
await this.#page.addInitScript(scriptOrFunc, arg);
|
|
4427
|
+
} else {
|
|
4428
|
+
throw new Error(`Invalid type of scriptOrFunc ${typeof scriptOrFunc}`);
|
|
4429
|
+
}
|
|
4430
|
+
return true;
|
|
4431
|
+
}
|
|
4432
|
+
async addScriptTag(options) {
|
|
4433
|
+
if (!this.#page) {
|
|
4434
|
+
throw new Error("No valid page");
|
|
4435
|
+
}
|
|
4436
|
+
return this.#page.addScriptTag(options);
|
|
4437
|
+
}
|
|
4438
|
+
apiContext() {
|
|
4439
|
+
return this.browserContext().apiContext();
|
|
4440
|
+
}
|
|
4441
|
+
async bringToFront() {
|
|
4442
|
+
if (!this.#page) {
|
|
4443
|
+
throw new Error("No valid page");
|
|
4444
|
+
}
|
|
4445
|
+
await this.#page.bringToFront();
|
|
4446
|
+
return true;
|
|
4447
|
+
}
|
|
4448
|
+
browserContext() {
|
|
4449
|
+
return this.#lsdBrowserContext;
|
|
4450
|
+
}
|
|
4451
|
+
async clearCookies() {
|
|
4452
|
+
if (!this.#page) {
|
|
4453
|
+
throw new Error("No valid page");
|
|
4454
|
+
}
|
|
4455
|
+
return await this.#clearCookies(this.#page);
|
|
4456
|
+
}
|
|
4457
|
+
async clearLocalStorage() {
|
|
4458
|
+
if (!this.#page) {
|
|
4459
|
+
throw new Error("No valid page");
|
|
4460
|
+
}
|
|
4461
|
+
return await this.#clearLocalStorage(this.#page);
|
|
4462
|
+
}
|
|
4463
|
+
async clearRequestInterceptions() {
|
|
4464
|
+
if (!this.#page) {
|
|
4465
|
+
throw new Error("No valid page");
|
|
4466
|
+
}
|
|
4467
|
+
await this.#page.unrouteAll();
|
|
4468
|
+
return true;
|
|
4469
|
+
}
|
|
4470
|
+
async clearResponseInterceptions() {
|
|
4471
|
+
if (!this.#page) {
|
|
4472
|
+
throw new Error("No valid page");
|
|
4473
|
+
}
|
|
4474
|
+
try {
|
|
4475
|
+
if (this.#responseInterceptionOptions.length > 0) {
|
|
4476
|
+
if (this.#responseCb) {
|
|
4477
|
+
this.#page.removeListener("response", this.#responseCb);
|
|
4478
|
+
}
|
|
4479
|
+
this.#responseInterceptionOptions = [];
|
|
4480
|
+
}
|
|
4481
|
+
return true;
|
|
4482
|
+
} catch (err) {
|
|
4483
|
+
logerr(err);
|
|
4484
|
+
return false;
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
async clearStateData() {
|
|
4488
|
+
if (!this.#page) {
|
|
4489
|
+
throw new Error("No valid page");
|
|
4490
|
+
}
|
|
4491
|
+
await this.#clearCookies(this.#page);
|
|
4492
|
+
await this.#clearIndexedDB(this.#page);
|
|
4493
|
+
return await this.#clearLocalStorage(this.#page);
|
|
4494
|
+
}
|
|
4495
|
+
async close() {
|
|
4496
|
+
if (this.#status === "closed") {
|
|
4497
|
+
logwarn(`Page ${this.#pageId} is already closed.`);
|
|
4498
|
+
return true;
|
|
4499
|
+
} else if (this.#status === "busy") {
|
|
4500
|
+
throw new Error(`Page ${this.#pageId} cannot be closed because it is busy.`);
|
|
4501
|
+
}
|
|
4502
|
+
if (!this.#page) {
|
|
4503
|
+
throw new Error("No valid page");
|
|
4504
|
+
}
|
|
4505
|
+
await this.#page.close();
|
|
4506
|
+
this.#page = null;
|
|
4507
|
+
this.#status = "closed";
|
|
4508
|
+
return true;
|
|
4509
|
+
}
|
|
4510
|
+
closeWhenFree() {
|
|
4511
|
+
return this.#closeWhenFree;
|
|
4512
|
+
}
|
|
4513
|
+
async content(iframeOptions = []) {
|
|
4514
|
+
if (!this.#page) {
|
|
4515
|
+
throw new Error("No valid page");
|
|
4516
|
+
}
|
|
4517
|
+
let content = "";
|
|
4518
|
+
if (iframeOptions.length > 0) {
|
|
4519
|
+
const frameLocator = await this.#getDescendantFrame(this.#page.mainFrame(), iframeOptions);
|
|
4520
|
+
if (frameLocator) {
|
|
4521
|
+
content = await frameLocator.locator(":root").evaluate(() => document.documentElement.outerHTML);
|
|
4522
|
+
}
|
|
4523
|
+
} else {
|
|
4524
|
+
content = await this.#page.content();
|
|
4525
|
+
}
|
|
4526
|
+
return content;
|
|
4527
|
+
}
|
|
4528
|
+
async cookies() {
|
|
4529
|
+
if (!this.#page) {
|
|
4530
|
+
throw new Error("No valid page");
|
|
4531
|
+
}
|
|
4532
|
+
return this.#getCookies(this.#page);
|
|
4533
|
+
}
|
|
4534
|
+
async documentHeight() {
|
|
4535
|
+
if (!this.#page) {
|
|
4536
|
+
throw new Error("No valid page");
|
|
4537
|
+
}
|
|
4538
|
+
const height = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
4539
|
+
return height;
|
|
4540
|
+
}
|
|
4541
|
+
async evaluate(func, args) {
|
|
4542
|
+
if (!this.#page) {
|
|
4543
|
+
throw new Error("No valid page");
|
|
4544
|
+
}
|
|
4545
|
+
return this.#page.evaluate(func, args);
|
|
4546
|
+
}
|
|
4547
|
+
async exposeFunction(name, callbackFunction) {
|
|
4548
|
+
if (!this.#page) {
|
|
4549
|
+
throw new Error("No valid page");
|
|
4550
|
+
}
|
|
4551
|
+
await this.#page.exposeFunction(name, callbackFunction);
|
|
4552
|
+
return;
|
|
4553
|
+
}
|
|
4554
|
+
async findElement(selectorOrXpath, iframeOptions = []) {
|
|
4555
|
+
if (!this.#page) {
|
|
4556
|
+
throw new Error("No valid page");
|
|
4557
|
+
}
|
|
4558
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
4559
|
+
if (!Array.isArray(selectors)) {
|
|
4560
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElement`);
|
|
4561
|
+
}
|
|
4562
|
+
for (const selector of selectors) {
|
|
4563
|
+
const { frame, locators } = await this.#findElementHandles(selector, iframeOptions);
|
|
4564
|
+
if (locators.length > 0) {
|
|
4565
|
+
const playwrightElement = new PatchrightElement(locators[0], frame);
|
|
4566
|
+
return playwrightElement;
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
return null;
|
|
4570
|
+
}
|
|
4571
|
+
async findElements(selectorOrXpath, iframeOptions = []) {
|
|
4572
|
+
if (!this.#page) {
|
|
4573
|
+
throw new Error("No valid page");
|
|
4574
|
+
}
|
|
4575
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
4576
|
+
if (!Array.isArray(selectors)) {
|
|
4577
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElements`);
|
|
4578
|
+
}
|
|
4579
|
+
for (const selector of selectors) {
|
|
4580
|
+
const { frame, locators } = await this.#findElementHandles(selector, iframeOptions);
|
|
4581
|
+
if (locators.length > 0) {
|
|
4582
|
+
const playwrightElements = locators.map((locator) => new PatchrightElement(locator, frame));
|
|
4583
|
+
return playwrightElements;
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
return [];
|
|
4587
|
+
}
|
|
4588
|
+
async free() {
|
|
4589
|
+
if (this.#status === "free") {
|
|
4590
|
+
logwarn(`Page ${this.#pageId} is already free.`);
|
|
4591
|
+
}
|
|
4592
|
+
this.#status = "free";
|
|
4593
|
+
await this.clearRequestInterceptions();
|
|
4594
|
+
await this.clearResponseInterceptions();
|
|
4595
|
+
return true;
|
|
4596
|
+
}
|
|
4597
|
+
#getWaitUntil(origWaitUntil) {
|
|
4598
|
+
if (origWaitUntil === "networkidle0" || origWaitUntil === "networkidle2") {
|
|
4599
|
+
return "networkidle";
|
|
4600
|
+
} else {
|
|
4601
|
+
return origWaitUntil;
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
async goto(url, options) {
|
|
4605
|
+
if (!this.#page) {
|
|
4606
|
+
throw new Error("No valid page");
|
|
4607
|
+
}
|
|
4608
|
+
if (options) {
|
|
4609
|
+
const { referer, timeout, waitUntil = "load" } = options;
|
|
4610
|
+
const newOptions = {};
|
|
4611
|
+
if (referer) {
|
|
4612
|
+
newOptions.referer = referer;
|
|
4613
|
+
}
|
|
4614
|
+
if (timeout) {
|
|
4615
|
+
newOptions.timeout = timeout;
|
|
4616
|
+
}
|
|
4617
|
+
newOptions.waitUntil = this.#getWaitUntil(waitUntil);
|
|
4618
|
+
await this.#page.goto(url, newOptions);
|
|
4619
|
+
} else {
|
|
4620
|
+
await this.#page.goto(url);
|
|
4621
|
+
}
|
|
4622
|
+
return true;
|
|
4623
|
+
}
|
|
4624
|
+
id() {
|
|
4625
|
+
return this.#pageId;
|
|
4626
|
+
}
|
|
4627
|
+
isFree() {
|
|
4628
|
+
return this.#status === "free";
|
|
4629
|
+
}
|
|
4630
|
+
async localStroage() {
|
|
4631
|
+
if (!this.#page) {
|
|
4632
|
+
throw new Error("No valid page");
|
|
4633
|
+
}
|
|
4634
|
+
return this.#getLocalStorage(this.#page);
|
|
4635
|
+
}
|
|
4636
|
+
load() {
|
|
4637
|
+
throw new Error("Not supported in PatchrightPage.");
|
|
4638
|
+
}
|
|
4639
|
+
mainFrame() {
|
|
4640
|
+
if (!this.#page) {
|
|
4641
|
+
throw new Error("No valid page");
|
|
4642
|
+
}
|
|
4643
|
+
return this.#page.mainFrame();
|
|
4644
|
+
}
|
|
4645
|
+
async maximizeViewport() {
|
|
4646
|
+
const height = await this.pageHeight();
|
|
4647
|
+
const width = await this.pageWidth();
|
|
4648
|
+
return await this.setViewportSize({ height, width });
|
|
4649
|
+
}
|
|
4650
|
+
async pageHeight() {
|
|
4651
|
+
if (!this.#page) {
|
|
4652
|
+
throw new Error("No valid page");
|
|
4653
|
+
}
|
|
4654
|
+
const bodyHeight = await this.#page.evaluate(() => document.body.scrollHeight);
|
|
4655
|
+
const documentHeight = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
4656
|
+
const windowHeight = await this.#page.evaluate(() => window.outerHeight);
|
|
4657
|
+
const pageHeight = Math.max(bodyHeight, documentHeight, windowHeight);
|
|
4658
|
+
return pageHeight;
|
|
4659
|
+
}
|
|
4660
|
+
pageInfo() {
|
|
4661
|
+
if (!this.#page) {
|
|
4662
|
+
throw new Error("No valid page");
|
|
4663
|
+
}
|
|
4664
|
+
return Object.assign({}, this.#page.pageInfo);
|
|
4665
|
+
}
|
|
4666
|
+
async pageWidth() {
|
|
4667
|
+
if (!this.#page) {
|
|
4668
|
+
throw new Error("No valid page");
|
|
4669
|
+
}
|
|
4670
|
+
const offsetWidth = await this.#page.evaluate(() => document.documentElement.offsetWidth);
|
|
4671
|
+
const windowWidth = await this.#page.evaluate(() => window.outerWidth);
|
|
4672
|
+
const pageWidth = Math.max(offsetWidth, windowWidth);
|
|
4673
|
+
return pageWidth;
|
|
4674
|
+
}
|
|
4675
|
+
async pdf(options) {
|
|
4676
|
+
if (!this.#page) {
|
|
4677
|
+
throw new Error("No valid page");
|
|
4678
|
+
}
|
|
4679
|
+
const buffer = await this.#page.pdf(options);
|
|
4680
|
+
return buffer;
|
|
4681
|
+
}
|
|
4682
|
+
async screenshot(options) {
|
|
4683
|
+
if (!this.#page) {
|
|
4684
|
+
throw new Error("No valid page");
|
|
4685
|
+
}
|
|
4686
|
+
return await this.#page.screenshot(options);
|
|
4687
|
+
}
|
|
4688
|
+
async scrollBy(x, y) {
|
|
4689
|
+
if (!this.#page) {
|
|
4690
|
+
throw new Error("No valid page");
|
|
4691
|
+
}
|
|
4692
|
+
await this.#page.evaluate(
|
|
4693
|
+
([x2, y2]) => {
|
|
4694
|
+
window.scrollBy(x2, y2);
|
|
4695
|
+
},
|
|
4696
|
+
[x, y]
|
|
4697
|
+
);
|
|
4698
|
+
return true;
|
|
4699
|
+
}
|
|
4700
|
+
async scrollTo(x, y) {
|
|
4701
|
+
if (!this.#page) {
|
|
4702
|
+
throw new Error("No valid page");
|
|
4703
|
+
}
|
|
4704
|
+
await this.#page.evaluate(
|
|
4705
|
+
([x2, y2]) => {
|
|
4706
|
+
window.scrollTo(x2, y2);
|
|
4707
|
+
},
|
|
4708
|
+
[x, y]
|
|
4709
|
+
);
|
|
4710
|
+
return true;
|
|
4711
|
+
}
|
|
4712
|
+
async sendCDPMessage(method, params = null, detach = true) {
|
|
4713
|
+
if (!this.#client) {
|
|
4714
|
+
const origContext = this.browserContext()._origBrowserContext();
|
|
4715
|
+
if (!origContext) {
|
|
4716
|
+
throw new Error(`Invalid playwright browserContext`);
|
|
4717
|
+
}
|
|
4718
|
+
this.#client = await origContext.newCDPSession(this.#page);
|
|
4719
|
+
}
|
|
4720
|
+
if (!this.#client) {
|
|
4721
|
+
throw new Error("No valid CDP session to send message");
|
|
4722
|
+
}
|
|
4723
|
+
const response = params ? await this.#client.send(method, params) : await this.#client.send(method);
|
|
4724
|
+
if (detach) {
|
|
4725
|
+
await this.#client.detach();
|
|
4726
|
+
this.#client = null;
|
|
4727
|
+
}
|
|
4728
|
+
return response;
|
|
4729
|
+
}
|
|
4730
|
+
setCloseWhenFree(closeWhenFree) {
|
|
4731
|
+
this.#closeWhenFree = closeWhenFree;
|
|
4732
|
+
return true;
|
|
4733
|
+
}
|
|
4734
|
+
async setCookies(cookies) {
|
|
4735
|
+
if (!this.#page) {
|
|
4736
|
+
throw new Error("No valid page");
|
|
4737
|
+
}
|
|
4738
|
+
return await this.#setCookies(this.#page, cookies);
|
|
4739
|
+
}
|
|
4740
|
+
async setExtraHTTPHeaders(headers) {
|
|
4741
|
+
if (!this.#page) {
|
|
4742
|
+
throw new Error("No valid page");
|
|
4743
|
+
}
|
|
4744
|
+
await this.#page.setExtraHTTPHeaders(headers);
|
|
4745
|
+
return true;
|
|
4746
|
+
}
|
|
4747
|
+
async setLocalStroage(localStorageItems) {
|
|
4748
|
+
if (!this.#page) {
|
|
4749
|
+
throw new Error("No valid page");
|
|
4750
|
+
}
|
|
4751
|
+
return await this.#setLocalStorage(this.#page, localStorageItems);
|
|
4752
|
+
}
|
|
4753
|
+
setPageInfo(pageInfo) {
|
|
4754
|
+
if (!this.#page?.pageInfo) {
|
|
4755
|
+
throw new Error("No valid page or pageInfo");
|
|
4756
|
+
}
|
|
4757
|
+
if (!pageInfo) {
|
|
4758
|
+
throw new Error("Invalid paras in setPageInfo");
|
|
4759
|
+
}
|
|
4760
|
+
const actPageInfo = this.#page.pageInfo;
|
|
4761
|
+
const { lastStatusUpdateTime, taskId, relatedId, misc } = pageInfo;
|
|
4762
|
+
if (typeof lastStatusUpdateTime === "number") {
|
|
4763
|
+
actPageInfo.lastStatusUpdateTime = lastStatusUpdateTime;
|
|
4764
|
+
}
|
|
4765
|
+
if (typeof taskId === "number") {
|
|
4766
|
+
actPageInfo.taskId = taskId;
|
|
4767
|
+
}
|
|
4768
|
+
if (typeof relatedId === "number") {
|
|
4769
|
+
actPageInfo.relatedId = relatedId;
|
|
4770
|
+
}
|
|
4771
|
+
if (misc && typeof misc === "object") {
|
|
4772
|
+
for (const key of Object.keys(misc)) {
|
|
4773
|
+
actPageInfo.misc[key] = misc[key];
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
return true;
|
|
4777
|
+
}
|
|
4778
|
+
#checkRequestMatch(request, requestMatch) {
|
|
4779
|
+
try {
|
|
4780
|
+
if (!request) {
|
|
4781
|
+
return false;
|
|
4782
|
+
}
|
|
4783
|
+
const { methods, postData, resourceTypes, url } = requestMatch;
|
|
4784
|
+
if (methods && !methods.includes(request.method().toUpperCase())) {
|
|
4785
|
+
return false;
|
|
4786
|
+
}
|
|
4787
|
+
if (resourceTypes && !resourceTypes.includes(request.resourceType())) {
|
|
4788
|
+
return false;
|
|
4789
|
+
}
|
|
4790
|
+
if (url && !request.url().match(url)) {
|
|
4791
|
+
return false;
|
|
4792
|
+
}
|
|
4793
|
+
const origData = request.postData();
|
|
4794
|
+
const data = origData ? origData : "";
|
|
4795
|
+
if (postData && !data.match(postData)) {
|
|
4796
|
+
return false;
|
|
4797
|
+
}
|
|
4798
|
+
return true;
|
|
4799
|
+
} catch (err) {
|
|
4800
|
+
logerr(err);
|
|
4801
|
+
return false;
|
|
4802
|
+
}
|
|
4803
|
+
}
|
|
4804
|
+
async setRequestInterception(options) {
|
|
4805
|
+
if (!this.#page) {
|
|
4806
|
+
throw new Error("No valid page");
|
|
4807
|
+
}
|
|
4808
|
+
const actOptions = Array.isArray(options) ? options : [options];
|
|
4809
|
+
if (actOptions.length <= 0) {
|
|
4810
|
+
logwarn("Invalid paras in setRequestInterception");
|
|
4811
|
+
return false;
|
|
4812
|
+
}
|
|
4813
|
+
const firstRequestInterception = this.#resquestInterceptionOptions.length <= 0;
|
|
4814
|
+
for (const option of actOptions) {
|
|
4815
|
+
switch (option.action) {
|
|
4816
|
+
case "abort":
|
|
4817
|
+
case "fulfill":
|
|
4818
|
+
this.#resquestInterceptionOptions.push(option);
|
|
4819
|
+
break;
|
|
4820
|
+
default:
|
|
4821
|
+
unreachable6(option.action);
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
if (firstRequestInterception && this.#resquestInterceptionOptions.length > 0) {
|
|
4825
|
+
this.#page.route("**", async (route) => {
|
|
4826
|
+
try {
|
|
4827
|
+
for (const option of actOptions) {
|
|
4828
|
+
const { requestMatch, action, fulfill } = option;
|
|
4829
|
+
const request = route.request();
|
|
4830
|
+
const matchedFlag = !requestMatch || this.#checkRequestMatch(request, requestMatch);
|
|
4831
|
+
if (matchedFlag) {
|
|
4832
|
+
switch (action) {
|
|
4833
|
+
case "abort":
|
|
4834
|
+
await route.abort();
|
|
4835
|
+
break;
|
|
4836
|
+
case "fulfill":
|
|
4837
|
+
const body = fulfill ? fulfill : `<html><body><h1>${request.url()}</h1></body></html>`;
|
|
4838
|
+
route.fulfill({
|
|
4839
|
+
status: 200,
|
|
4840
|
+
// contentType: "text/html; charset=utf-8", // "text/plain",
|
|
4841
|
+
body
|
|
4842
|
+
});
|
|
4843
|
+
break;
|
|
4844
|
+
default:
|
|
4845
|
+
unreachable6(action);
|
|
4846
|
+
}
|
|
4847
|
+
return true;
|
|
4848
|
+
} else {
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
await route.continue();
|
|
4852
|
+
return true;
|
|
4853
|
+
} catch (err) {
|
|
4854
|
+
logerr(err);
|
|
4855
|
+
return false;
|
|
4856
|
+
}
|
|
4857
|
+
});
|
|
4858
|
+
}
|
|
4859
|
+
return true;
|
|
4860
|
+
}
|
|
4861
|
+
async #responseListener(response) {
|
|
4862
|
+
try {
|
|
4863
|
+
const pageUrl = this.#page ? this.#page.url() : "";
|
|
4864
|
+
if (!response.ok()) {
|
|
4865
|
+
return;
|
|
4866
|
+
}
|
|
4867
|
+
const request = response.request();
|
|
4868
|
+
if (!request) {
|
|
4869
|
+
return;
|
|
4870
|
+
}
|
|
4871
|
+
for (const option of this.#responseInterceptionOptions) {
|
|
4872
|
+
const { requestMatch, responseMatch, responseItems, handler, handlerOptions = {} } = option;
|
|
4873
|
+
let matchedFlag = !requestMatch || this.#checkRequestMatch(request, requestMatch);
|
|
4874
|
+
if (matchedFlag && responseMatch) {
|
|
4875
|
+
const { minLength, maxLength } = responseMatch;
|
|
4876
|
+
const text = await response.text();
|
|
4877
|
+
const len = text.length;
|
|
4878
|
+
if (minLength && minLength > 0 && len < minLength || maxLength && maxLength > 0 && len > maxLength) {
|
|
4879
|
+
matchedFlag = false;
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
if (!matchedFlag) {
|
|
4883
|
+
continue;
|
|
4884
|
+
}
|
|
4885
|
+
if (Array.isArray(responseItems)) {
|
|
4886
|
+
const requestMethod = request.method();
|
|
4887
|
+
const requestUrl = request.url();
|
|
4888
|
+
const reqData2 = request.postData();
|
|
4889
|
+
const requestData = reqData2 ? reqData2 : "";
|
|
4890
|
+
const responseData = await response.text();
|
|
4891
|
+
responseItems.push({
|
|
4892
|
+
pageUrl,
|
|
4893
|
+
requestMethod,
|
|
4894
|
+
requestUrl,
|
|
4895
|
+
requestData,
|
|
4896
|
+
responseData
|
|
4897
|
+
});
|
|
4898
|
+
loginfo(`##browser cache matched response: ${requestUrl}`);
|
|
4899
|
+
}
|
|
4900
|
+
if (typeof handler === "function") {
|
|
4901
|
+
const pageData = { pageUrl, cookies: "" };
|
|
4902
|
+
await handler(response, handlerOptions, pageData);
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
return;
|
|
4906
|
+
} catch (err) {
|
|
4907
|
+
logerr(err);
|
|
4908
|
+
return;
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
async setResponseInterception(options) {
|
|
4912
|
+
if (!this.#page) {
|
|
4913
|
+
throw new Error("No valid page");
|
|
4914
|
+
}
|
|
4915
|
+
const actOptions = Array.isArray(options) ? options : [options];
|
|
4916
|
+
if (actOptions.length <= 0) {
|
|
4917
|
+
logwarn("Invalid paras in setResponseInterception");
|
|
4918
|
+
return false;
|
|
4919
|
+
}
|
|
4920
|
+
const firstResponseInterception = this.#responseInterceptionOptions.length <= 0;
|
|
4921
|
+
for (const option of actOptions) {
|
|
4922
|
+
if (option?.responseItems || option?.handler) {
|
|
4923
|
+
this.#responseInterceptionOptions.push(option);
|
|
4924
|
+
} else {
|
|
4925
|
+
throw new Error(`Invalid ResponseInterceptionOption`);
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
if (firstResponseInterception && this.#responseInterceptionOptions.length > 0) {
|
|
4929
|
+
this.#responseCb = this.#responseListener.bind(this);
|
|
4930
|
+
this.#page.on("response", this.#responseCb);
|
|
4931
|
+
}
|
|
4932
|
+
return true;
|
|
4933
|
+
}
|
|
4934
|
+
async setStateData(stateData) {
|
|
4935
|
+
return await this.#lsdBrowserContext.setStateData(stateData);
|
|
4936
|
+
}
|
|
4937
|
+
async setUserAgent(userAgent) {
|
|
4938
|
+
if (userAgent) {
|
|
4939
|
+
throw new Error(`Patchright does not support page.setUserAgent by now`);
|
|
4940
|
+
}
|
|
4941
|
+
return false;
|
|
4942
|
+
}
|
|
4943
|
+
async setViewportSize(viewPortSize) {
|
|
4944
|
+
if (!this.#page) {
|
|
4945
|
+
throw new Error("No valid page");
|
|
4946
|
+
}
|
|
4947
|
+
await this.#page.setViewportSize(viewPortSize);
|
|
4948
|
+
return true;
|
|
4949
|
+
}
|
|
4950
|
+
async stateData() {
|
|
4951
|
+
if (!this.#page) {
|
|
4952
|
+
throw new Error("No valid page");
|
|
4953
|
+
}
|
|
4954
|
+
const cookies = await this.#getCookies(this.#page);
|
|
4955
|
+
const localStorage = await this.#getLocalStorage(this.#page);
|
|
4956
|
+
return { cookies, localStorage };
|
|
4957
|
+
}
|
|
4958
|
+
status() {
|
|
4959
|
+
return this.#status;
|
|
4960
|
+
}
|
|
4961
|
+
async title() {
|
|
4962
|
+
if (!this.#page) {
|
|
4963
|
+
throw new Error("No valid page");
|
|
4964
|
+
}
|
|
4965
|
+
return await this.#page.title();
|
|
4966
|
+
}
|
|
4967
|
+
url() {
|
|
4968
|
+
if (!this.#page) {
|
|
4969
|
+
throw new Error("No valid page");
|
|
4970
|
+
}
|
|
4971
|
+
return this.#page.url();
|
|
4972
|
+
}
|
|
4973
|
+
use() {
|
|
4974
|
+
if (this.#status === "busy") {
|
|
4975
|
+
throw new Error(`Page ${this.#pageId} is already busy!!!`);
|
|
4976
|
+
}
|
|
4977
|
+
this.#status = "busy";
|
|
4978
|
+
return true;
|
|
4979
|
+
}
|
|
4980
|
+
async waitForElement(selector, options = {}) {
|
|
4981
|
+
if (!this.#page) {
|
|
4982
|
+
throw new Error("No valid page");
|
|
4983
|
+
}
|
|
4984
|
+
const locator = this.#page.locator(selector);
|
|
4985
|
+
const { timeout = 3e4, state = "visible" } = options;
|
|
4986
|
+
await locator.waitFor({ state, timeout });
|
|
4987
|
+
return true;
|
|
4988
|
+
}
|
|
4989
|
+
async waitForNavigation(options) {
|
|
4990
|
+
if (!this.#page) {
|
|
4991
|
+
throw new Error("No valid page");
|
|
4992
|
+
}
|
|
4993
|
+
const { url = "", timeout = 3e4, waitUntil = "load" } = options;
|
|
4994
|
+
const newWaitUntil = this.#getWaitUntil(waitUntil);
|
|
4995
|
+
if (url) {
|
|
4996
|
+
await this.#page.waitForURL(url, { timeout, waitUntil: newWaitUntil });
|
|
4997
|
+
} else if (newWaitUntil === "commit") {
|
|
4998
|
+
throw new Error("commit is not supported in PatchrightPage.waitForNavigation");
|
|
4999
|
+
} else {
|
|
5000
|
+
await this.#page.waitForLoadState(newWaitUntil, { timeout });
|
|
5001
|
+
}
|
|
5002
|
+
return true;
|
|
5003
|
+
}
|
|
5004
|
+
async windowMember(keys) {
|
|
5005
|
+
if (!this.#page) {
|
|
5006
|
+
throw new Error("No valid page");
|
|
5007
|
+
}
|
|
5008
|
+
if (!this.#page || !Array.isArray(keys) || keys.length <= 0 || keys.length > 20) {
|
|
5009
|
+
return "";
|
|
5010
|
+
}
|
|
5011
|
+
const content = await this.#page.evaluate(
|
|
5012
|
+
(keys2) => {
|
|
5013
|
+
let retObj = window;
|
|
5014
|
+
for (const key of keys2) {
|
|
5015
|
+
if (!key) {
|
|
5016
|
+
break;
|
|
5017
|
+
} else if (typeof retObj !== "object" || !retObj) {
|
|
5018
|
+
return "";
|
|
5019
|
+
} else {
|
|
5020
|
+
retObj = retObj[key];
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
5023
|
+
if (typeof retObj === "string") {
|
|
5024
|
+
return retObj;
|
|
5025
|
+
} else if (typeof retObj === "number") {
|
|
5026
|
+
return String(retObj);
|
|
5027
|
+
} else if (typeof retObj === "boolean") {
|
|
5028
|
+
return String(Number(retObj));
|
|
5029
|
+
} else if (!retObj) {
|
|
5030
|
+
return "";
|
|
5031
|
+
} else if (typeof retObj === "object") {
|
|
5032
|
+
try {
|
|
5033
|
+
return JSON.stringify(retObj);
|
|
5034
|
+
} catch (err) {
|
|
5035
|
+
return "";
|
|
5036
|
+
}
|
|
5037
|
+
} else if (typeof retObj === "bigint") {
|
|
5038
|
+
return String(retObj);
|
|
5039
|
+
} else {
|
|
5040
|
+
return "";
|
|
5041
|
+
}
|
|
5042
|
+
},
|
|
5043
|
+
keys
|
|
5044
|
+
);
|
|
5045
|
+
return content;
|
|
5046
|
+
}
|
|
5047
|
+
_origPage() {
|
|
5048
|
+
return this.#page;
|
|
5049
|
+
}
|
|
5050
|
+
};
|
|
5051
|
+
|
|
5052
|
+
// src/patchright/api.ts
|
|
5053
|
+
var PatchrightApiContext = class {
|
|
5054
|
+
#apiRequestContext;
|
|
5055
|
+
#status;
|
|
5056
|
+
constructor(apiRequestContext) {
|
|
5057
|
+
this.#apiRequestContext = apiRequestContext;
|
|
5058
|
+
this.#status = "normal";
|
|
5059
|
+
}
|
|
5060
|
+
async fetch(url, options = {}) {
|
|
5061
|
+
if (this.#status !== "normal") {
|
|
5062
|
+
throw new Error(`ApiContext has already been destroyed`);
|
|
5063
|
+
}
|
|
5064
|
+
const apiResponse = await this.#apiRequestContext.fetch(url, options);
|
|
5065
|
+
const headers = apiResponse.headers();
|
|
5066
|
+
const status = apiResponse.status();
|
|
5067
|
+
const statusText = apiResponse.statusText();
|
|
5068
|
+
const text = await apiResponse.text();
|
|
5069
|
+
const responseUrl = apiResponse.url();
|
|
5070
|
+
return { headers, status, statusText, text, url: responseUrl };
|
|
5071
|
+
}
|
|
5072
|
+
async stateData() {
|
|
5073
|
+
if (this.#status !== "normal") {
|
|
5074
|
+
throw new Error(`ApiContext has already been destroyed`);
|
|
5075
|
+
}
|
|
5076
|
+
const storageState = await this.#apiRequestContext.storageState();
|
|
5077
|
+
const { cookies, origins: localStorage } = storageState;
|
|
5078
|
+
return { cookies, localStorage };
|
|
5079
|
+
}
|
|
5080
|
+
async destroy() {
|
|
5081
|
+
await this.#apiRequestContext.dispose();
|
|
5082
|
+
this.#status = "destroyed";
|
|
5083
|
+
return true;
|
|
5084
|
+
}
|
|
5085
|
+
};
|
|
5086
|
+
|
|
5087
|
+
// src/patchright/context.ts
|
|
5088
|
+
var PatchrightBrowserContext = class extends EventEmitter9 {
|
|
5089
|
+
#lsdBrowser;
|
|
5090
|
+
#browserIdx;
|
|
5091
|
+
#browserContextIdx;
|
|
5092
|
+
#browserContext;
|
|
5093
|
+
#browserContextCreationMethod;
|
|
5094
|
+
#apiContext;
|
|
5095
|
+
#createTime;
|
|
5096
|
+
#lastStatusUpdateTime;
|
|
5097
|
+
#status;
|
|
5098
|
+
#incognito;
|
|
5099
|
+
#proxy;
|
|
5100
|
+
#maxPagesPerBrowserContext;
|
|
5101
|
+
#maxPageFreeSeconds;
|
|
5102
|
+
#maxViewportOfNewPage;
|
|
5103
|
+
#lsdPages;
|
|
5104
|
+
#nextPageIdx;
|
|
5105
|
+
#gettingPage;
|
|
5106
|
+
async #initPages() {
|
|
5107
|
+
if (!this.#browserContext) {
|
|
5108
|
+
throw new Error("Invalid browserContext");
|
|
5109
|
+
}
|
|
5110
|
+
const pages = this.#browserContext.pages();
|
|
5111
|
+
const openType = this.#lsdBrowser.browserCreationMethod();
|
|
5112
|
+
const lastStatusUpdateTime = getCurrentUnixTime8();
|
|
5113
|
+
for (const page of pages) {
|
|
5114
|
+
const pageInfo = { browserIdx: this.#browserIdx, browserContextIdx: this.#browserContextIdx, pageIdx: this.#nextPageIdx++, openType, openTime: this.#createTime, lastStatusUpdateTime, taskId: 0, relatedId: 0, misc: {} };
|
|
5115
|
+
const lsdPage = new PatchrightPage(this, page, pageInfo);
|
|
5116
|
+
if (this.#maxViewportOfNewPage) {
|
|
5117
|
+
await lsdPage.maximizeViewport();
|
|
5118
|
+
}
|
|
5119
|
+
this.#lsdPages.push(lsdPage);
|
|
5120
|
+
loginfo(`##browser ${lsdPage.id()} ${openType}ed`);
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
constructor(lsdBrowser, browserContext, browserContextCreationMethod, incognito = false, proxy = null, browserIdx = 0, browserContextIdx = 0, maxPagesPerBrowserContext = 20, maxPageFreeSeconds = 0, maxViewportOfNewPage = true) {
|
|
5124
|
+
if (!lsdBrowser || typeof lsdBrowser.browserContexts !== "function") {
|
|
5125
|
+
throw new Error(`Invalid lsdBrowser parameter`);
|
|
5126
|
+
}
|
|
5127
|
+
if (!browserContext || typeof browserContext.setOffline !== "function") {
|
|
5128
|
+
throw new Error(`Invalid playwright browserContext parameter`);
|
|
5129
|
+
}
|
|
5130
|
+
super();
|
|
5131
|
+
this.#lsdBrowser = lsdBrowser;
|
|
5132
|
+
this.#browserIdx = browserIdx;
|
|
5133
|
+
this.#browserContextIdx = browserContextIdx;
|
|
5134
|
+
this.#browserContext = browserContext;
|
|
5135
|
+
this.#browserContextCreationMethod = browserContextCreationMethod;
|
|
5136
|
+
const apiRequestContext = browserContext.request;
|
|
5137
|
+
this.#apiContext = new PatchrightApiContext(apiRequestContext);
|
|
5138
|
+
const currentTime = getCurrentUnixTime8();
|
|
5139
|
+
this.#createTime = currentTime;
|
|
5140
|
+
this.#lastStatusUpdateTime = currentTime;
|
|
5141
|
+
this.#status = "free";
|
|
5142
|
+
this.#incognito = incognito === false ? false : true;
|
|
5143
|
+
this.#proxy = proxy?.proxyUrl ? proxy : null;
|
|
5144
|
+
this.#maxPagesPerBrowserContext = maxPagesPerBrowserContext;
|
|
5145
|
+
this.#maxPageFreeSeconds = maxPageFreeSeconds;
|
|
5146
|
+
this.#maxViewportOfNewPage = maxViewportOfNewPage;
|
|
5147
|
+
this.#lsdPages = [];
|
|
5148
|
+
this.#nextPageIdx = 1;
|
|
5149
|
+
this.#gettingPage = false;
|
|
5150
|
+
this.#initPages();
|
|
5151
|
+
browserContext.on("page", async (page) => {
|
|
5152
|
+
const pageInfo = page.pageInfo;
|
|
5153
|
+
if (pageInfo) {
|
|
5154
|
+
const { browserIdx: browserIdx2, browserContextIdx: browserContextIdx2, pageIdx } = pageInfo;
|
|
5155
|
+
logwarn(`##browser page-${browserIdx2}-${browserContextIdx2}-${pageIdx} has been already created`);
|
|
5156
|
+
} else {
|
|
5157
|
+
const currentTime2 = getCurrentUnixTime8();
|
|
5158
|
+
const pageInfo2 = { browserIdx: this.#browserIdx, browserContextIdx: this.#browserContextIdx, pageIdx: this.#nextPageIdx++, openType: "other", openTime: currentTime2, lastStatusUpdateTime: currentTime2, taskId: 0, relatedId: 0, misc: {} };
|
|
5159
|
+
const lsdPage = new PatchrightPage(this, page, pageInfo2);
|
|
5160
|
+
if (this.#maxViewportOfNewPage) {
|
|
5161
|
+
await lsdPage.maximizeViewport();
|
|
5162
|
+
}
|
|
5163
|
+
this.#lsdPages.push(lsdPage);
|
|
5164
|
+
loginfo(`##page ${lsdPage.id()} created`);
|
|
5165
|
+
}
|
|
5166
|
+
});
|
|
5167
|
+
browserContext.on("close", (bc) => {
|
|
5168
|
+
if (browserContext !== bc) {
|
|
5169
|
+
logerr(`##browser different browserContext in browserContext.on("close")`);
|
|
5170
|
+
}
|
|
5171
|
+
this.#lsdBrowser.emit("browserContextClose", this);
|
|
5172
|
+
});
|
|
5173
|
+
this.on("pageClose", (lsdPage) => {
|
|
5174
|
+
if (!(lsdPage instanceof PatchrightPage)) {
|
|
5175
|
+
logerr(`Invalid data in LsdBrowserContext.on("pageClose)`);
|
|
5176
|
+
return;
|
|
5177
|
+
}
|
|
5178
|
+
const idx = this.#lsdPages.findIndex((p) => p === lsdPage);
|
|
5179
|
+
if (idx < 0) {
|
|
5180
|
+
logerr(`Invalid lsdPage in LsdBrowserContext.on("pageClose)`);
|
|
5181
|
+
return;
|
|
5182
|
+
}
|
|
5183
|
+
this.#lsdPages.splice(idx, 1);
|
|
5184
|
+
return;
|
|
5185
|
+
});
|
|
5186
|
+
}
|
|
5187
|
+
apiContext() {
|
|
5188
|
+
return this.#apiContext;
|
|
5189
|
+
}
|
|
5190
|
+
browser() {
|
|
5191
|
+
return this.#lsdBrowser;
|
|
5192
|
+
}
|
|
5193
|
+
async close() {
|
|
5194
|
+
if (this.#browserContext) {
|
|
5195
|
+
this.#status = "closed";
|
|
5196
|
+
this.#lastStatusUpdateTime = getCurrentUnixTime8();
|
|
5197
|
+
loginfo(`browserContext ${this.id()} closed at ${this.#lastStatusUpdateTime}`);
|
|
5198
|
+
await this.#browserContext.close();
|
|
5199
|
+
}
|
|
5200
|
+
return true;
|
|
5201
|
+
}
|
|
5202
|
+
async #tryToGetGettingLock() {
|
|
5203
|
+
let i = 0;
|
|
5204
|
+
for (i = 0; i < 50; i++) {
|
|
5205
|
+
if (!this.#gettingPage) {
|
|
5206
|
+
this.#gettingPage = true;
|
|
5207
|
+
return true;
|
|
5208
|
+
} else {
|
|
5209
|
+
await sleep3(200);
|
|
5210
|
+
}
|
|
5211
|
+
}
|
|
5212
|
+
logwarn(`Cannot get the gettingLock.`);
|
|
5213
|
+
return false;
|
|
5214
|
+
}
|
|
5215
|
+
#freeGettingLock() {
|
|
5216
|
+
if (!this.#gettingPage) {
|
|
5217
|
+
logwarn(`Getting lock is already free now.`);
|
|
5218
|
+
}
|
|
5219
|
+
this.#gettingPage = false;
|
|
5220
|
+
}
|
|
5221
|
+
async closeFreePages(maxPageFreeSeconds = 0) {
|
|
5222
|
+
if (maxPageFreeSeconds <= 0) {
|
|
5223
|
+
maxPageFreeSeconds = this.#maxPageFreeSeconds;
|
|
5224
|
+
}
|
|
5225
|
+
if (maxPageFreeSeconds <= 0) {
|
|
5226
|
+
logwarn(`Please set valid maxPageFreeSeconds to close free pages`);
|
|
5227
|
+
return false;
|
|
5228
|
+
}
|
|
5229
|
+
const gotLock = await this.#tryToGetGettingLock();
|
|
5230
|
+
if (!gotLock) {
|
|
5231
|
+
return false;
|
|
5232
|
+
}
|
|
5233
|
+
try {
|
|
5234
|
+
const maxUpdateTime = getCurrentUnixTime8() - this.#maxPageFreeSeconds;
|
|
5235
|
+
let freePages = this.#lsdPages.filter((p) => p.isFree() && p.pageInfo().lastStatusUpdateTime < maxUpdateTime);
|
|
5236
|
+
if (freePages.length === this.#lsdPages.length) {
|
|
5237
|
+
freePages = freePages.slice(1);
|
|
5238
|
+
}
|
|
5239
|
+
for (const lsdPage of freePages) {
|
|
5240
|
+
await lsdPage.close();
|
|
5241
|
+
}
|
|
5242
|
+
this.#freeGettingLock();
|
|
5243
|
+
return true;
|
|
5244
|
+
} catch (err) {
|
|
5245
|
+
logerr(err);
|
|
5246
|
+
this.#freeGettingLock();
|
|
5247
|
+
return false;
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5250
|
+
creationMethod() {
|
|
5251
|
+
return this.#browserContextCreationMethod;
|
|
5252
|
+
}
|
|
5253
|
+
doesMeetBrowserContextRequirements(browserContextRequirements) {
|
|
5254
|
+
if (!this.#lsdBrowser.doesMeetBrowserContextRequirements(browserContextRequirements)) {
|
|
5255
|
+
return false;
|
|
5256
|
+
}
|
|
5257
|
+
const { browserIncognitos: incognitos } = browserContextRequirements;
|
|
5258
|
+
return incognitos.length === 0 || incognitos.includes(this.#incognito);
|
|
5259
|
+
}
|
|
5260
|
+
async getPage(always = false) {
|
|
5261
|
+
if (!this.#browserContext) {
|
|
5262
|
+
throw new Error("Invalid browserContext");
|
|
5263
|
+
}
|
|
5264
|
+
const gotLock = await this.#tryToGetGettingLock();
|
|
5265
|
+
if (!gotLock) {
|
|
5266
|
+
return null;
|
|
5267
|
+
}
|
|
5268
|
+
try {
|
|
5269
|
+
let lsdPage = this.#lsdPages.find((p) => p.isFree());
|
|
5270
|
+
if (lsdPage) {
|
|
5271
|
+
lsdPage.use();
|
|
5272
|
+
this.#freeGettingLock();
|
|
5273
|
+
return lsdPage;
|
|
5274
|
+
}
|
|
5275
|
+
if (this.#lsdPages.length >= this.#maxPagesPerBrowserContext && !always) {
|
|
5276
|
+
this.#freeGettingLock();
|
|
5277
|
+
return null;
|
|
5278
|
+
}
|
|
5279
|
+
const page = await this.#browserContext.newPage();
|
|
5280
|
+
await sleep3(2e3);
|
|
5281
|
+
const pageInfo = page.pageInfo;
|
|
5282
|
+
if (!pageInfo) {
|
|
5283
|
+
throw new Error(`Logic error in getPage`);
|
|
5284
|
+
} else {
|
|
5285
|
+
pageInfo.openType = "newpage";
|
|
5286
|
+
}
|
|
5287
|
+
lsdPage = this.#lsdPages.find((p) => p.isFree());
|
|
5288
|
+
if (lsdPage) {
|
|
5289
|
+
lsdPage.use();
|
|
5290
|
+
this.#freeGettingLock();
|
|
5291
|
+
return lsdPage;
|
|
5292
|
+
} else {
|
|
5293
|
+
this.#freeGettingLock();
|
|
5294
|
+
return null;
|
|
5295
|
+
}
|
|
5296
|
+
} catch (err) {
|
|
5297
|
+
logerr(err);
|
|
5298
|
+
this.#freeGettingLock();
|
|
5299
|
+
return null;
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
free(clearStateData = false) {
|
|
5303
|
+
if (this.#status === "busy") {
|
|
5304
|
+
this.#status = "free";
|
|
5305
|
+
this.#lastStatusUpdateTime = getCurrentUnixTime8();
|
|
5306
|
+
if (clearStateData) {
|
|
5307
|
+
}
|
|
5308
|
+
return true;
|
|
5309
|
+
} else {
|
|
5310
|
+
return false;
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
hasFreePage(pageNum = 1) {
|
|
5314
|
+
if (this.#maxPagesPerBrowserContext - this.#lsdPages.length > pageNum) {
|
|
5315
|
+
return true;
|
|
5316
|
+
} else if (this.#maxPagesPerBrowserContext - this.#lsdPages.filter((p) => !p.isFree()).length > pageNum) {
|
|
5317
|
+
return true;
|
|
5318
|
+
} else {
|
|
5319
|
+
return false;
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
id() {
|
|
5323
|
+
return `browserContext-${this.#browserIdx}-${this.#browserContextIdx}`;
|
|
5324
|
+
}
|
|
5325
|
+
isFree() {
|
|
5326
|
+
return this.#status === "free";
|
|
5327
|
+
}
|
|
5328
|
+
isIncognito() {
|
|
5329
|
+
return this.#incognito;
|
|
5330
|
+
}
|
|
5331
|
+
page(pageIdx) {
|
|
5332
|
+
const lsdPage = this.#lsdPages.find((p) => p.pageInfo().pageIdx === pageIdx);
|
|
5333
|
+
return lsdPage ? lsdPage : null;
|
|
5334
|
+
}
|
|
5335
|
+
pages() {
|
|
5336
|
+
return this.#lsdPages;
|
|
5337
|
+
}
|
|
5338
|
+
proxy() {
|
|
5339
|
+
return this.#proxy;
|
|
5340
|
+
}
|
|
5341
|
+
async setStateData(stateData) {
|
|
5342
|
+
if (!this.#browserContext) {
|
|
5343
|
+
throw new Error("No valid browserContext");
|
|
5344
|
+
}
|
|
5345
|
+
try {
|
|
5346
|
+
const { cookies, localStorage: localStorageOrigins } = stateData;
|
|
5347
|
+
const page = await this.getPage();
|
|
5348
|
+
if (!page) {
|
|
5349
|
+
return false;
|
|
5350
|
+
}
|
|
5351
|
+
const origPage = page._origPage();
|
|
5352
|
+
if (cookies.length > 0) {
|
|
5353
|
+
await this.#browserContext.addCookies(cookies);
|
|
5354
|
+
}
|
|
5355
|
+
if (localStorageOrigins.length > 0) {
|
|
5356
|
+
await origPage.route("**/*", async (route) => {
|
|
5357
|
+
const request = route.request();
|
|
5358
|
+
await route.fulfill({
|
|
5359
|
+
status: 200,
|
|
5360
|
+
// contentType: "text/html; charset=utf-8", // "text/plain",
|
|
5361
|
+
body: `<html><body><h1>${request.url()}</h1></body></html>`
|
|
5362
|
+
});
|
|
5363
|
+
});
|
|
5364
|
+
for (const localStorageOrigin of localStorageOrigins) {
|
|
5365
|
+
const { origin, localStorage } = localStorageOrigin;
|
|
5366
|
+
await origPage.goto(origin);
|
|
5367
|
+
await origPage.evaluate((localStorageItems) => {
|
|
5368
|
+
for (const item of localStorageItems) {
|
|
5369
|
+
window.localStorage.setItem(item.name, item.value);
|
|
5370
|
+
}
|
|
5371
|
+
}, localStorage);
|
|
5372
|
+
}
|
|
5373
|
+
}
|
|
5374
|
+
await sleep3(2e3);
|
|
5375
|
+
await origPage.unrouteAll();
|
|
5376
|
+
await page.free();
|
|
5377
|
+
return true;
|
|
5378
|
+
} catch (err) {
|
|
5379
|
+
logerr(err);
|
|
5380
|
+
return false;
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
status() {
|
|
5384
|
+
return this.#status;
|
|
5385
|
+
}
|
|
5386
|
+
use() {
|
|
5387
|
+
if (this.#status === "free") {
|
|
5388
|
+
this.#status = "busy";
|
|
5389
|
+
this.#lastStatusUpdateTime = getCurrentUnixTime8();
|
|
5390
|
+
return true;
|
|
5391
|
+
} else {
|
|
5392
|
+
return false;
|
|
5393
|
+
}
|
|
5394
|
+
}
|
|
5395
|
+
_origBrowserContext() {
|
|
5396
|
+
return this.#browserContext;
|
|
5397
|
+
}
|
|
5398
|
+
};
|
|
5399
|
+
|
|
5400
|
+
// src/patchright/browser.ts
|
|
5401
|
+
var PatchrightBrowser = class _PatchrightBrowser extends EventEmitter10 {
|
|
5402
|
+
static #supportedBrowserTypes = ["chromium", "firefox", "webkit"];
|
|
5403
|
+
static doesSupport(browserType) {
|
|
5404
|
+
return _PatchrightBrowser.#supportedBrowserTypes.includes(browserType);
|
|
5405
|
+
}
|
|
5406
|
+
#browser;
|
|
5407
|
+
#browserIdx;
|
|
5408
|
+
#pid;
|
|
5409
|
+
#createTime;
|
|
5410
|
+
#lsdBrowserContexts;
|
|
5411
|
+
#browserControllerType;
|
|
5412
|
+
#browserType;
|
|
5413
|
+
#browserCreationMethod;
|
|
5414
|
+
#headless;
|
|
5415
|
+
#options;
|
|
5416
|
+
#proxy;
|
|
5417
|
+
/**
|
|
5418
|
+
* launch: actual path of executable app
|
|
5419
|
+
* connect: ""
|
|
5420
|
+
*/
|
|
5421
|
+
#executablePath;
|
|
5422
|
+
#nextBrowserContextIdx;
|
|
5423
|
+
#closeFreePagesIntervalId;
|
|
5424
|
+
#maxBrowserContextsPerBrowser() {
|
|
5425
|
+
return this.#options.maxBrowserContextsPerBrowser ? this.#options.maxBrowserContextsPerBrowser : 10;
|
|
5426
|
+
}
|
|
5427
|
+
#maxPagesPerBrowserContext() {
|
|
5428
|
+
return this.#options.maxPagesPerBrowserContext ? this.#options.maxPagesPerBrowserContext : 20;
|
|
5429
|
+
}
|
|
5430
|
+
#maxPageFreeSeconds() {
|
|
5431
|
+
return this.#options.maxPageFreeSeconds ? this.#options.maxPageFreeSeconds : 900;
|
|
5432
|
+
}
|
|
5433
|
+
// constructor: called only by LsdBrowserController.launch/connect
|
|
5434
|
+
constructor(browser, browserType, browserCreateMethod, options, browserIdx = 0, pid = 0) {
|
|
5435
|
+
if (!browser || typeof browser.contexts !== "function") {
|
|
5436
|
+
throw new Error(`Invalid playwright browser parameter`);
|
|
5437
|
+
}
|
|
5438
|
+
super();
|
|
5439
|
+
const { closeFreePagesIntervalSeconds = 300, maxPageFreeSeconds = 900, maxViewportOfNewPage = true, headless = false, executablePath = "" } = options;
|
|
5440
|
+
this.#browser = browser;
|
|
5441
|
+
this.#browserIdx = browserIdx;
|
|
5442
|
+
this.#pid = pid;
|
|
5443
|
+
this.#createTime = getCurrentUnixTime9();
|
|
5444
|
+
this.#lsdBrowserContexts = [];
|
|
5445
|
+
this.#browserControllerType = "playwright";
|
|
5446
|
+
this.#browserType = browserType;
|
|
5447
|
+
if (!_PatchrightBrowser.#supportedBrowserTypes.includes(browserType)) {
|
|
5448
|
+
throw new Error(`Browser controller ${this.#browserControllerType} doesnot support browserType ${browserType}`);
|
|
5449
|
+
}
|
|
5450
|
+
this.#browserCreationMethod = browserCreateMethod;
|
|
5451
|
+
this.#headless = headless;
|
|
5452
|
+
this.#proxy = options?.proxy ? Object.assign({}, options.proxy) : null;
|
|
5453
|
+
this.#options = Object.assign({}, options, { closeFreePagesIntervalSeconds, maxPageFreeSeconds, maxViewportOfNewPage, headless, executablePath, proxy: this.#proxy });
|
|
5454
|
+
this.#executablePath = executablePath;
|
|
5455
|
+
this.#nextBrowserContextIdx = 1;
|
|
5456
|
+
this.#closeFreePagesIntervalId = null;
|
|
5457
|
+
loginfo(`##browser ${this.id()} ${this.#browserCreationMethod}ed by ${this.#browserControllerType}`);
|
|
5458
|
+
const browserContexts = browser.contexts();
|
|
5459
|
+
if (browserContexts.length > 0) {
|
|
5460
|
+
logwarn(`There are ${browserContexts.length} new browserContexts when playwright launches new browser`);
|
|
5461
|
+
}
|
|
5462
|
+
const incognito = typeof options?.incognito === "boolean" ? options.incognito : true;
|
|
5463
|
+
for (const browserContext of browserContexts) {
|
|
5464
|
+
const lsdBrowserContext = new PatchrightBrowserContext(this, browserContext, "launch", incognito, this.#proxy, this.#browserIdx, this.#nextBrowserContextIdx++, this.#maxPagesPerBrowserContext(), this.#maxPageFreeSeconds(), maxViewportOfNewPage);
|
|
5465
|
+
this.#lsdBrowserContexts.push(lsdBrowserContext);
|
|
5466
|
+
loginfo(`##browserContext ${lsdBrowserContext.id()} ${this.#browserCreationMethod}ed`);
|
|
5467
|
+
}
|
|
5468
|
+
browser.on("disconnected", () => {
|
|
5469
|
+
loginfo(`##browser ${this.id()} disconnected`);
|
|
5470
|
+
if (this.#lsdBrowserContexts.length > 0) {
|
|
5471
|
+
logerr(`${this.id()} has browserContexts when disconnected`);
|
|
5472
|
+
}
|
|
5473
|
+
});
|
|
5474
|
+
this.on("browserContextClose", (lsdBrowserContext) => {
|
|
5475
|
+
if (!(lsdBrowserContext instanceof PatchrightBrowserContext)) {
|
|
5476
|
+
logerr(`Invalid data in LsdBrowser.on("browserContextClose)`);
|
|
5477
|
+
return;
|
|
5478
|
+
}
|
|
5479
|
+
const idx = this.#lsdBrowserContexts.findIndex((bc) => bc === lsdBrowserContext);
|
|
5480
|
+
if (idx < 0) {
|
|
5481
|
+
logerr(`Invalid lsdBrowserContext in LsdBrowser.on("browserContextClose)`);
|
|
5482
|
+
return;
|
|
5483
|
+
}
|
|
5484
|
+
loginfo(`##browserContext ${lsdBrowserContext.id()} closed
|
|
5485
|
+
`);
|
|
5486
|
+
this.#lsdBrowserContexts.splice(idx, 1);
|
|
5487
|
+
if (this.#lsdBrowserContexts.length === 0) {
|
|
5488
|
+
loginfo(`##browser ${this.id()} has no browserContexts now`);
|
|
5489
|
+
}
|
|
5490
|
+
return;
|
|
5491
|
+
});
|
|
5492
|
+
if (closeFreePagesIntervalSeconds > 0 && maxPageFreeSeconds > 0) {
|
|
5493
|
+
this.#closeFreePagesIntervalId = setInterval(async () => {
|
|
5494
|
+
await this.#closeFreePagesHandler();
|
|
5495
|
+
}, closeFreePagesIntervalSeconds * 1e3);
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
async #closeFreePagesHandler() {
|
|
5499
|
+
for (const lsdBrowserContext of this.#lsdBrowserContexts) {
|
|
5500
|
+
await lsdBrowserContext.closeFreePages();
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5503
|
+
async newBrowserContext(options) {
|
|
5504
|
+
if (this.#lsdBrowserContexts.length >= this.#maxBrowserContextsPerBrowser()) {
|
|
5505
|
+
logwarn(`##browser ${this.id()} can not create more new browserContext`);
|
|
5506
|
+
return null;
|
|
5507
|
+
}
|
|
5508
|
+
const browserContextOptions = {};
|
|
5509
|
+
if (this.#options.maxWindowSize) {
|
|
5510
|
+
browserContextOptions.viewport = null;
|
|
5511
|
+
}
|
|
5512
|
+
const proxy = options?.proxy ? Object.assign({}, options.proxy) : this.#proxy;
|
|
5513
|
+
if (proxy) {
|
|
5514
|
+
const { proxyUrl: server, username, password } = proxy;
|
|
5515
|
+
browserContextOptions.proxy = { server, username, password };
|
|
5516
|
+
}
|
|
5517
|
+
if (options?.userAgent) {
|
|
5518
|
+
browserContextOptions.userAgent = options.userAgent;
|
|
5519
|
+
}
|
|
5520
|
+
const browserContext = await this.#browser.newContext(browserContextOptions);
|
|
5521
|
+
const { maxViewportOfNewPage = this.#options.maxViewportOfNewPage } = options ? options : {};
|
|
5522
|
+
const lsdBrowserContext = new PatchrightBrowserContext(this, browserContext, "new", true, proxy, this.#browserIdx, this.#nextBrowserContextIdx++, this.#maxPagesPerBrowserContext(), this.#maxPageFreeSeconds(), maxViewportOfNewPage);
|
|
5523
|
+
this.#lsdBrowserContexts.push(lsdBrowserContext);
|
|
5524
|
+
loginfo(`##browser ${lsdBrowserContext.id()} created`);
|
|
5525
|
+
return lsdBrowserContext;
|
|
5526
|
+
}
|
|
5527
|
+
async close() {
|
|
5528
|
+
if (this.#closeFreePagesIntervalId) {
|
|
5529
|
+
clearInterval(this.#closeFreePagesIntervalId);
|
|
5530
|
+
}
|
|
5531
|
+
for (const lsdBrowserContext of this.#lsdBrowserContexts) {
|
|
5532
|
+
await lsdBrowserContext.close();
|
|
5533
|
+
}
|
|
5534
|
+
await this.#browser.close();
|
|
5535
|
+
return true;
|
|
5536
|
+
}
|
|
5537
|
+
browserContexts() {
|
|
5538
|
+
return this.#lsdBrowserContexts;
|
|
5539
|
+
}
|
|
5540
|
+
browserControllerType() {
|
|
5541
|
+
return this.#browserControllerType;
|
|
5542
|
+
}
|
|
5543
|
+
browserCreationMethod() {
|
|
5544
|
+
return this.#browserCreationMethod;
|
|
5545
|
+
}
|
|
5546
|
+
browserType() {
|
|
5547
|
+
return this.#browserType;
|
|
5548
|
+
}
|
|
5549
|
+
createTime() {
|
|
5550
|
+
return this.#createTime;
|
|
5551
|
+
}
|
|
5552
|
+
doesMeetBrowserContextRequirements(browserContextRequirements) {
|
|
5553
|
+
const { browserControllerTypes, browserTypes, browserHeadlesses } = browserContextRequirements;
|
|
5554
|
+
return (browserControllerTypes.length === 0 || browserControllerTypes.includes(this.#browserControllerType)) && (browserTypes.length === 0 || browserTypes.includes(this.#browserType)) && (browserHeadlesses.length === 0 || browserHeadlesses.includes(this.#headless));
|
|
5555
|
+
}
|
|
5556
|
+
executablePath() {
|
|
5557
|
+
return this.#executablePath;
|
|
5558
|
+
}
|
|
5559
|
+
id() {
|
|
5560
|
+
return `browser-${this.#browserType}-${this.#browserIdx}`;
|
|
5561
|
+
}
|
|
5562
|
+
isConnected() {
|
|
5563
|
+
return this.#browser.isConnected();
|
|
5564
|
+
}
|
|
5565
|
+
isHeadless() {
|
|
5566
|
+
return this.#headless;
|
|
5567
|
+
}
|
|
5568
|
+
options() {
|
|
5569
|
+
return this.#options;
|
|
5570
|
+
}
|
|
5571
|
+
pid() {
|
|
5572
|
+
return this.#pid;
|
|
5573
|
+
}
|
|
5574
|
+
async pidUsage() {
|
|
5575
|
+
if (this.#pid > 0) {
|
|
5576
|
+
const usage = await getPerformanceOfPidTree3(this.#pid, "MB");
|
|
5577
|
+
return usage;
|
|
5578
|
+
} else {
|
|
5579
|
+
return { cpu: 0, memory: 0 };
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
proxy() {
|
|
5583
|
+
return this.#proxy;
|
|
5584
|
+
}
|
|
5585
|
+
async version() {
|
|
5586
|
+
const version = await this.#browser.version();
|
|
5587
|
+
return version;
|
|
5588
|
+
}
|
|
5589
|
+
_origBrowser() {
|
|
5590
|
+
return this.#browser;
|
|
5591
|
+
}
|
|
5592
|
+
};
|
|
5593
|
+
|
|
5594
|
+
// src/controller/controller.ts
|
|
5595
|
+
import { getPidsListeningOnPort, unreachable as unreachable7 } from "@letsscrapedata/utils";
|
|
5596
|
+
var LsdBrowserController = class _LsdBrowserController {
|
|
5597
|
+
static #forbidConstructor = false;
|
|
5598
|
+
#puppeteer;
|
|
5599
|
+
#playwrightBrowserTypes;
|
|
5600
|
+
#patchrightBrowserTypes;
|
|
5601
|
+
#nextBrowserIdx;
|
|
5602
|
+
/**
|
|
5603
|
+
* Possible values are 'aix', 'darwin', 'freebsd','linux', 'openbsd', 'sunos', and 'win32'.
|
|
5604
|
+
*/
|
|
5605
|
+
#osPlatform;
|
|
5606
|
+
constructor() {
|
|
5607
|
+
if (_LsdBrowserController.#forbidConstructor) {
|
|
5608
|
+
throw new Error("Only one LsdBrowserController instance can be created!");
|
|
5609
|
+
}
|
|
5610
|
+
this.#puppeteer = puppeteer;
|
|
5611
|
+
this.#playwrightBrowserTypes = {
|
|
5612
|
+
chromium: playwright.chromium,
|
|
5613
|
+
firefox: playwright.firefox,
|
|
5614
|
+
webkit: playwright.webkit
|
|
5615
|
+
};
|
|
5616
|
+
this.#patchrightBrowserTypes = {
|
|
5617
|
+
chromium: patchright.chromium,
|
|
5618
|
+
firefox: patchright.firefox,
|
|
5619
|
+
webkit: patchright.webkit
|
|
5620
|
+
};
|
|
5621
|
+
this.#osPlatform = os.platform();
|
|
5622
|
+
this.#nextBrowserIdx = 1;
|
|
5623
|
+
_LsdBrowserController.#forbidConstructor = true;
|
|
5624
|
+
}
|
|
5625
|
+
#playwrightBrowserType(browserType, connectFlag = false) {
|
|
5626
|
+
if (browserType === "chromium") {
|
|
5627
|
+
return this.#playwrightBrowserTypes.chromium;
|
|
5628
|
+
} else if (connectFlag) {
|
|
5629
|
+
throw new Error(`playwright only can connect to chromium browser, not support ${browserType} browser`);
|
|
5630
|
+
} else if (browserType === "firefox") {
|
|
5631
|
+
return this.#playwrightBrowserTypes.firefox;
|
|
5632
|
+
} else if (browserType === "webkit") {
|
|
5633
|
+
return this.#playwrightBrowserTypes.webkit;
|
|
5634
|
+
} else {
|
|
5635
|
+
throw new Error(`Invalid playwright browserType ${browserType}`);
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
#patchrightBrowserType(browserType, connectFlag = false) {
|
|
5639
|
+
if (browserType === "chromium") {
|
|
5640
|
+
return this.#patchrightBrowserTypes.chromium;
|
|
5641
|
+
} else if (connectFlag) {
|
|
5642
|
+
throw new Error(`patchright only can connect to chromium browser, not support ${browserType} browser`);
|
|
5643
|
+
} else if (browserType === "firefox") {
|
|
5644
|
+
return this.#patchrightBrowserTypes.firefox;
|
|
5645
|
+
} else if (browserType === "webkit") {
|
|
5646
|
+
return this.#patchrightBrowserTypes.webkit;
|
|
5647
|
+
} else {
|
|
5648
|
+
throw new Error(`Invalid patchright browserType ${browserType}`);
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
#puppeteerProduct(browserType) {
|
|
5652
|
+
if (browserType === "chromium") {
|
|
5653
|
+
return "chrome";
|
|
5654
|
+
} else {
|
|
5655
|
+
throw new Error(`Invalid puppeteer product ${browserType}`);
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5658
|
+
setBrowserPlugin(browserControllerType, browserType, plugin) {
|
|
5659
|
+
if (browserControllerType === "puppeteer") {
|
|
5660
|
+
if (!PuppeteerBrowser.doesSupport(browserType)) {
|
|
5661
|
+
throw new Error(`BrowserControllerType ${browserControllerType} doesnot support browserType ${browserType}`);
|
|
5662
|
+
}
|
|
5663
|
+
this.#puppeteer = plugin;
|
|
5664
|
+
} else if (browserControllerType === "playwright") {
|
|
5665
|
+
if (!PlaywrightBrowser.doesSupport(browserType)) {
|
|
5666
|
+
throw new Error(`BrowserControllerType ${browserControllerType} doesnot support browserType ${browserType}`);
|
|
5667
|
+
}
|
|
5668
|
+
switch (browserType) {
|
|
5669
|
+
case "chromium":
|
|
5670
|
+
case "firefox":
|
|
5671
|
+
case "webkit":
|
|
5672
|
+
this.#playwrightBrowserTypes[browserType] = plugin;
|
|
5673
|
+
break;
|
|
5674
|
+
default:
|
|
5675
|
+
unreachable7(browserType);
|
|
5676
|
+
}
|
|
5677
|
+
} else if (browserControllerType === "patchright") {
|
|
5678
|
+
if (!PatchrightBrowser.doesSupport(browserType)) {
|
|
5679
|
+
throw new Error(`BrowserControllerType ${browserControllerType} doesnot support browserType ${browserType}`);
|
|
5680
|
+
}
|
|
5681
|
+
switch (browserType) {
|
|
5682
|
+
case "chromium":
|
|
5683
|
+
case "firefox":
|
|
5684
|
+
case "webkit":
|
|
5685
|
+
this.#patchrightBrowserTypes[browserType] = plugin;
|
|
5686
|
+
break;
|
|
5687
|
+
default:
|
|
5688
|
+
unreachable7(browserType);
|
|
5689
|
+
}
|
|
5690
|
+
} else {
|
|
5691
|
+
unreachable7(browserControllerType);
|
|
5692
|
+
}
|
|
5693
|
+
return true;
|
|
5694
|
+
}
|
|
5695
|
+
async launch(browserControllerType, browserType, options) {
|
|
5696
|
+
let {
|
|
3686
5697
|
closeFreePagesIntervalSeconds = 300,
|
|
3687
5698
|
maxBrowserContextsPerBrowser = 10,
|
|
3688
5699
|
maxPagesPerBrowserContext = 20,
|
|
@@ -3765,6 +5776,47 @@ var LsdBrowserController = class _LsdBrowserController {
|
|
|
3765
5776
|
const browser = await playwrightBrowserType.launch(launchOptions);
|
|
3766
5777
|
lsdBrowser = new PlaywrightBrowser(browser, browserType, "launch", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
3767
5778
|
}
|
|
5779
|
+
} else if (browserControllerType === "patchright") {
|
|
5780
|
+
const launchOptions = { headless, timeout };
|
|
5781
|
+
if (executablePath) {
|
|
5782
|
+
launchOptions.executablePath = executablePath;
|
|
5783
|
+
}
|
|
5784
|
+
if (maxWindowSize) {
|
|
5785
|
+
args.push("--start-maximized");
|
|
5786
|
+
}
|
|
5787
|
+
if (proxy?.proxyUrl && proxy.proxyUrl !== "local") {
|
|
5788
|
+
const { proxyUrl: server, username, password } = proxy;
|
|
5789
|
+
launchOptions.proxy = { server, username, password };
|
|
5790
|
+
} else if (proxyPerBrowserContext && browserType === "chromium" && this.#osPlatform.startsWith("win")) {
|
|
5791
|
+
launchOptions.proxy = { server: "proxyPerBrowserContext" };
|
|
5792
|
+
}
|
|
5793
|
+
if (browserType === "chromium") {
|
|
5794
|
+
if (incognito) {
|
|
5795
|
+
args.push("--incognito");
|
|
5796
|
+
} else if (userDataDir) {
|
|
5797
|
+
args.push(`--user-data-dir=${userDataDir}`);
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
if (args.length > 0) {
|
|
5801
|
+
launchOptions.args = args;
|
|
5802
|
+
}
|
|
5803
|
+
const patchrightBrowserType = this.#patchrightBrowserType(browserType);
|
|
5804
|
+
if (!actOptions.executablePath) {
|
|
5805
|
+
actOptions.executablePath = patchrightBrowserType.executablePath();
|
|
5806
|
+
}
|
|
5807
|
+
if (options.launchMethod === "launchServer") {
|
|
5808
|
+
const browserServer = await patchrightBrowserType.launchServer(launchOptions);
|
|
5809
|
+
const process = browserServer.process();
|
|
5810
|
+
if (process?.pid) {
|
|
5811
|
+
browserPid = process.pid;
|
|
5812
|
+
}
|
|
5813
|
+
const wsEndpoint = browserServer.wsEndpoint();
|
|
5814
|
+
const browser = await patchrightBrowserType.connect(wsEndpoint);
|
|
5815
|
+
lsdBrowser = new PatchrightBrowser(browser, browserType, "launch", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
5816
|
+
} else {
|
|
5817
|
+
const browser = await patchrightBrowserType.launch(launchOptions);
|
|
5818
|
+
lsdBrowser = new PatchrightBrowser(browser, browserType, "launch", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
5819
|
+
}
|
|
3768
5820
|
} else if (browserControllerType === "puppeteer") {
|
|
3769
5821
|
const product = this.#puppeteerProduct(browserType);
|
|
3770
5822
|
const launchOptions = { headless, timeout, product };
|
|
@@ -3843,6 +5895,11 @@ var LsdBrowserController = class _LsdBrowserController {
|
|
|
3843
5895
|
const browser = await playwrightBrowserType.connectOverCDP(browserUrl);
|
|
3844
5896
|
const lsdBrowser = new PlaywrightBrowser(browser, browserType, "connect", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
3845
5897
|
return lsdBrowser;
|
|
5898
|
+
} else if (browserControllerType === "patchright") {
|
|
5899
|
+
const patchrightBrowserType = this.#patchrightBrowserType(browserType, true);
|
|
5900
|
+
const browser = await patchrightBrowserType.connectOverCDP(browserUrl);
|
|
5901
|
+
const lsdBrowser = new PatchrightBrowser(browser, browserType, "connect", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
5902
|
+
return lsdBrowser;
|
|
3846
5903
|
} else if (browserControllerType === "puppeteer") {
|
|
3847
5904
|
this.#puppeteerProduct(browserType);
|
|
3848
5905
|
const browser = await this.#puppeteer.connect({ browserURL: browserUrl });
|
|
@@ -3855,7 +5912,7 @@ var LsdBrowserController = class _LsdBrowserController {
|
|
|
3855
5912
|
}
|
|
3856
5913
|
async newApiContext(options = {}) {
|
|
3857
5914
|
const { proxy, stateData, userAgent, timeout, ignoreHTTPSErrors, extraHTTPHeaders, httpCredentials } = options;
|
|
3858
|
-
const apiRequestContext = await
|
|
5915
|
+
const apiRequestContext = await apiRequestInPlaywright.newContext({
|
|
3859
5916
|
proxy: proxy ? { server: proxy.proxyUrl, username: proxy.username, password: proxy.password } : void 0,
|
|
3860
5917
|
storageState: stateData ? { cookies: stateData.cookies, origins: stateData.localStorage } : void 0,
|
|
3861
5918
|
userAgent,
|