@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.cjs
CHANGED
|
@@ -78,6 +78,32 @@ var import_utils4 = require("@letsscrapedata/utils");
|
|
|
78
78
|
var import_node_events = __toESM(require("events"), 1);
|
|
79
79
|
var import_utils3 = require("@letsscrapedata/utils");
|
|
80
80
|
|
|
81
|
+
// src/utils/common.ts
|
|
82
|
+
function convertDataAttributeName(attr) {
|
|
83
|
+
if (!attr.startsWith("data-")) {
|
|
84
|
+
return "";
|
|
85
|
+
}
|
|
86
|
+
const parts = attr.split("-");
|
|
87
|
+
let name = parts[1];
|
|
88
|
+
for (const part of parts.slice(2)) {
|
|
89
|
+
if (!part) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
name = `${name}${part[1].toUpperCase()}${part.slice(1).toLowerCase()}`;
|
|
93
|
+
}
|
|
94
|
+
return name;
|
|
95
|
+
}
|
|
96
|
+
function getIframeSelector(iframeOption) {
|
|
97
|
+
const { src = "", id = "", selector = "" } = iframeOption;
|
|
98
|
+
if (typeof src === "string" && src) {
|
|
99
|
+
return `iframe[src^="${src}"]`;
|
|
100
|
+
} else if (typeof id === "string" && id) {
|
|
101
|
+
return `iframe[id="${id}"]`;
|
|
102
|
+
} else {
|
|
103
|
+
return selector;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
81
107
|
// src/playwright/element.ts
|
|
82
108
|
var import_utils2 = require("@letsscrapedata/utils");
|
|
83
109
|
var PlaywrightElement = class _PlaywrightElement {
|
|
@@ -98,6 +124,29 @@ var PlaywrightElement = class _PlaywrightElement {
|
|
|
98
124
|
const names = await this.#locator.evaluate((node) => node.getAttributeNames());
|
|
99
125
|
return names;
|
|
100
126
|
}
|
|
127
|
+
async dataset() {
|
|
128
|
+
try {
|
|
129
|
+
const dataset = await this.#locator.evaluate((node) => node.dataset);
|
|
130
|
+
return dataset;
|
|
131
|
+
} catch (err) {
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async evaluate(func, args) {
|
|
136
|
+
try {
|
|
137
|
+
const frame = this.#frame;
|
|
138
|
+
;
|
|
139
|
+
if (typeof frame.parentFrame === "function") {
|
|
140
|
+
return await frame.evaluate(func, args);
|
|
141
|
+
} else {
|
|
142
|
+
const locator = this.#frame.owner();
|
|
143
|
+
return await locator.evaluate(func, args);
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
logerr(err);
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
}
|
|
101
150
|
/*
|
|
102
151
|
async #getChildFrame(parentFrame: Frame, iframeOption: IframeOption): Promise<Frame | null> {
|
|
103
152
|
if (!parentFrame) {
|
|
@@ -131,22 +180,15 @@ var PlaywrightElement = class _PlaywrightElement {
|
|
|
131
180
|
return null;
|
|
132
181
|
}
|
|
133
182
|
*/
|
|
134
|
-
#getIframeSelector(iframeOption) {
|
|
135
|
-
const { src = "", selector = "" } = iframeOption;
|
|
136
|
-
if (!src && !selector) {
|
|
137
|
-
throw new Error("Invalid parent frame");
|
|
138
|
-
}
|
|
139
|
-
return selector ? selector : `iframe[src^="${src}"]`;
|
|
140
|
-
}
|
|
141
183
|
async #getChildFrameLocator(parent, iframeOption) {
|
|
142
|
-
return parent.frameLocator(
|
|
184
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
143
185
|
}
|
|
144
186
|
async #getDescendantFrame(parent, iframeOptions) {
|
|
145
187
|
try {
|
|
146
188
|
if (iframeOptions.length <= 0) {
|
|
147
189
|
return null;
|
|
148
190
|
}
|
|
149
|
-
let frameLocator = parent.frameLocator(
|
|
191
|
+
let frameLocator = parent.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
150
192
|
for (const iframeOption of iframeOptions.slice(1)) {
|
|
151
193
|
if (!frameLocator) {
|
|
152
194
|
return null;
|
|
@@ -163,12 +205,12 @@ var PlaywrightElement = class _PlaywrightElement {
|
|
|
163
205
|
let frame = this.#frame;
|
|
164
206
|
const retObj = { frame, locators: [] };
|
|
165
207
|
if (iframeOptions.length > 0) {
|
|
166
|
-
|
|
167
|
-
if (!
|
|
208
|
+
const childFrame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
209
|
+
if (!childFrame) {
|
|
168
210
|
return retObj;
|
|
169
211
|
}
|
|
170
|
-
retObj.frame =
|
|
171
|
-
parent =
|
|
212
|
+
retObj.frame = childFrame;
|
|
213
|
+
parent = childFrame;
|
|
172
214
|
}
|
|
173
215
|
try {
|
|
174
216
|
let locators = [];
|
|
@@ -353,6 +395,7 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
353
395
|
#page;
|
|
354
396
|
#status;
|
|
355
397
|
#pageId;
|
|
398
|
+
#closeWhenFree;
|
|
356
399
|
#resquestInterceptionOptions;
|
|
357
400
|
#responseInterceptionOptions;
|
|
358
401
|
#client;
|
|
@@ -483,29 +526,57 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
483
526
|
return null;
|
|
484
527
|
}
|
|
485
528
|
*/
|
|
486
|
-
#
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
throw new Error("Invalid parent frame");
|
|
529
|
+
async #findDescendantFrame(src, id) {
|
|
530
|
+
if (!this.#page) {
|
|
531
|
+
throw new Error("No valid page");
|
|
490
532
|
}
|
|
491
|
-
|
|
533
|
+
const frames = this.#page.frames();
|
|
534
|
+
for (const frame of frames) {
|
|
535
|
+
const url = frame.url();
|
|
536
|
+
if (typeof src === "string" && src) {
|
|
537
|
+
if (url.startsWith(src)) {
|
|
538
|
+
return frame;
|
|
539
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
540
|
+
return frame;
|
|
541
|
+
}
|
|
542
|
+
} else if (src instanceof RegExp) {
|
|
543
|
+
if (url.match(src)) {
|
|
544
|
+
return frame;
|
|
545
|
+
}
|
|
546
|
+
} else if (id) {
|
|
547
|
+
const element = await frame.frameElement();
|
|
548
|
+
if (element) {
|
|
549
|
+
const frameId = await frame.evaluate(([ele, attr]) => ele.getAttribute(attr), [element, "id"]);
|
|
550
|
+
if (frameId === id) {
|
|
551
|
+
return frame;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return null;
|
|
492
557
|
}
|
|
493
558
|
async #getChildFrameLocator(parent, iframeOption) {
|
|
494
|
-
return parent.frameLocator(
|
|
559
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
495
560
|
}
|
|
496
|
-
async #
|
|
561
|
+
async #getDescendantFrame(mainFrame, iframeOptions) {
|
|
497
562
|
try {
|
|
498
563
|
if (iframeOptions.length <= 0) {
|
|
499
564
|
return null;
|
|
500
565
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
566
|
+
if (iframeOptions.length === 1 && !iframeOptions[0].selector) {
|
|
567
|
+
const { src = "", id = "" } = iframeOptions[0];
|
|
568
|
+
const frame = await this.#findDescendantFrame(src, id);
|
|
569
|
+
return frame;
|
|
570
|
+
} else {
|
|
571
|
+
let frameLocator = mainFrame.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
572
|
+
for (const iframeOption of iframeOptions.slice(1)) {
|
|
573
|
+
if (!frameLocator) {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
frameLocator = await this.#getChildFrameLocator(frameLocator, iframeOption);
|
|
505
577
|
}
|
|
506
|
-
|
|
578
|
+
return frameLocator;
|
|
507
579
|
}
|
|
508
|
-
return frameLocator;
|
|
509
580
|
} catch (err) {
|
|
510
581
|
throw new Error(`No child iframe: ${JSON.stringify(iframeOptions)}`);
|
|
511
582
|
}
|
|
@@ -517,7 +588,7 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
517
588
|
let frame = this.#page.mainFrame();
|
|
518
589
|
const retObj = { frame, locators: [] };
|
|
519
590
|
if (iframeOptions.length > 0) {
|
|
520
|
-
frame = await this.#
|
|
591
|
+
frame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
521
592
|
if (!frame) {
|
|
522
593
|
return retObj;
|
|
523
594
|
}
|
|
@@ -590,12 +661,32 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
590
661
|
const { browserIdx = 0, browserContextIdx = 0, pageIdx = 0, openType = "other", openTime = currentTime, lastStatusUpdateTime = currentTime, taskId = 0, relatedId = 0, misc = {} } = pageInfo ? pageInfo : {};
|
|
591
662
|
this.#page.pageInfo = { browserIdx, browserContextIdx, pageIdx, openType, openTime, lastStatusUpdateTime, taskId, relatedId, misc };
|
|
592
663
|
this.#pageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
664
|
+
this.#closeWhenFree = false;
|
|
593
665
|
this.#resquestInterceptionOptions = [];
|
|
594
666
|
this.#responseInterceptionOptions = [];
|
|
595
667
|
this.#client = null;
|
|
596
668
|
this.#responseCb = null;
|
|
597
669
|
this.#addPageOn();
|
|
598
670
|
}
|
|
671
|
+
async addPreloadScript(scriptOrFunc, arg) {
|
|
672
|
+
if (!this.#page) {
|
|
673
|
+
throw new Error("No valid page");
|
|
674
|
+
}
|
|
675
|
+
if (typeof scriptOrFunc === "string") {
|
|
676
|
+
await this.#page.addInitScript({ content: scriptOrFunc });
|
|
677
|
+
} else if (typeof scriptOrFunc === "function") {
|
|
678
|
+
await this.#page.addInitScript(scriptOrFunc, arg);
|
|
679
|
+
} else {
|
|
680
|
+
throw new Error(`Invalid type of scriptOrFunc ${typeof scriptOrFunc}`);
|
|
681
|
+
}
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
async addScriptTag(options) {
|
|
685
|
+
if (!this.#page) {
|
|
686
|
+
throw new Error("No valid page");
|
|
687
|
+
}
|
|
688
|
+
return this.#page.addScriptTag(options);
|
|
689
|
+
}
|
|
599
690
|
apiContext() {
|
|
600
691
|
return this.browserContext().apiContext();
|
|
601
692
|
}
|
|
@@ -655,7 +746,7 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
655
746
|
}
|
|
656
747
|
async close() {
|
|
657
748
|
if (this.#status === "closed") {
|
|
658
|
-
|
|
749
|
+
logwarn(`Page ${this.#pageId} is already closed.`);
|
|
659
750
|
return true;
|
|
660
751
|
} else if (this.#status === "busy") {
|
|
661
752
|
throw new Error(`Page ${this.#pageId} cannot be closed because it is busy.`);
|
|
@@ -668,13 +759,16 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
668
759
|
this.#status = "closed";
|
|
669
760
|
return true;
|
|
670
761
|
}
|
|
762
|
+
closeWhenFree() {
|
|
763
|
+
return this.#closeWhenFree;
|
|
764
|
+
}
|
|
671
765
|
async content(iframeOptions = []) {
|
|
672
766
|
if (!this.#page) {
|
|
673
767
|
throw new Error("No valid page");
|
|
674
768
|
}
|
|
675
769
|
let content = "";
|
|
676
770
|
if (iframeOptions.length > 0) {
|
|
677
|
-
const frameLocator = await this.#
|
|
771
|
+
const frameLocator = await this.#getDescendantFrame(this.#page.mainFrame(), iframeOptions);
|
|
678
772
|
if (frameLocator) {
|
|
679
773
|
content = await frameLocator.locator(":root").evaluate(() => document.documentElement.outerHTML);
|
|
680
774
|
}
|
|
@@ -696,11 +790,18 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
696
790
|
const height = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
697
791
|
return height;
|
|
698
792
|
}
|
|
699
|
-
async
|
|
793
|
+
async evaluate(func, args) {
|
|
794
|
+
if (!this.#page) {
|
|
795
|
+
throw new Error("No valid page");
|
|
796
|
+
}
|
|
797
|
+
return this.#page.evaluate(func, args);
|
|
798
|
+
}
|
|
799
|
+
async exposeFunction(name, callbackFunction) {
|
|
700
800
|
if (!this.#page) {
|
|
701
801
|
throw new Error("No valid page");
|
|
702
802
|
}
|
|
703
|
-
|
|
803
|
+
await this.#page.exposeFunction(name, callbackFunction);
|
|
804
|
+
return;
|
|
704
805
|
}
|
|
705
806
|
async findElement(selectorOrXpath, iframeOptions = []) {
|
|
706
807
|
if (!this.#page) {
|
|
@@ -738,7 +839,7 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
738
839
|
}
|
|
739
840
|
async free() {
|
|
740
841
|
if (this.#status === "free") {
|
|
741
|
-
|
|
842
|
+
logwarn(`Page ${this.#pageId} is already free.`);
|
|
742
843
|
}
|
|
743
844
|
this.#status = "free";
|
|
744
845
|
await this.clearRequestInterceptions();
|
|
@@ -878,6 +979,10 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
878
979
|
}
|
|
879
980
|
return response;
|
|
880
981
|
}
|
|
982
|
+
setCloseWhenFree(closeWhenFree) {
|
|
983
|
+
this.#closeWhenFree = closeWhenFree;
|
|
984
|
+
return true;
|
|
985
|
+
}
|
|
881
986
|
async setCookies(cookies) {
|
|
882
987
|
if (!this.#page) {
|
|
883
988
|
throw new Error("No valid page");
|
|
@@ -1061,7 +1166,7 @@ var PlaywrightPage = class extends import_node_events.default {
|
|
|
1061
1166
|
}
|
|
1062
1167
|
const actOptions = Array.isArray(options) ? options : [options];
|
|
1063
1168
|
if (actOptions.length <= 0) {
|
|
1064
|
-
|
|
1169
|
+
logwarn("Invalid paras in setResponseInterception");
|
|
1065
1170
|
return false;
|
|
1066
1171
|
}
|
|
1067
1172
|
const firstResponseInterception = this.#responseInterceptionOptions.length <= 0;
|
|
@@ -1545,7 +1650,11 @@ var PlaywrightBrowserContext = class extends import_node_events2.default {
|
|
|
1545
1650
|
};
|
|
1546
1651
|
|
|
1547
1652
|
// src/playwright/browser.ts
|
|
1548
|
-
var PlaywrightBrowser = class extends import_node_events3.default {
|
|
1653
|
+
var PlaywrightBrowser = class _PlaywrightBrowser extends import_node_events3.default {
|
|
1654
|
+
static #supportedBrowserTypes = ["chromium", "firefox", "webkit"];
|
|
1655
|
+
static doesSupport(browserType) {
|
|
1656
|
+
return _PlaywrightBrowser.#supportedBrowserTypes.includes(browserType);
|
|
1657
|
+
}
|
|
1549
1658
|
#browser;
|
|
1550
1659
|
#browserIdx;
|
|
1551
1660
|
#pid;
|
|
@@ -1574,7 +1683,7 @@ var PlaywrightBrowser = class extends import_node_events3.default {
|
|
|
1574
1683
|
return this.#options.maxPageFreeSeconds ? this.#options.maxPageFreeSeconds : 900;
|
|
1575
1684
|
}
|
|
1576
1685
|
// constructor: called only by LsdBrowserController.launch/connect
|
|
1577
|
-
constructor(browser,
|
|
1686
|
+
constructor(browser, browserType, browserCreateMethod, options, browserIdx = 0, pid = 0) {
|
|
1578
1687
|
if (!browser || typeof browser.contexts !== "function") {
|
|
1579
1688
|
throw new Error(`Invalid playwright browser parameter`);
|
|
1580
1689
|
}
|
|
@@ -1586,7 +1695,10 @@ var PlaywrightBrowser = class extends import_node_events3.default {
|
|
|
1586
1695
|
this.#createTime = (0, import_utils5.getCurrentUnixTime)();
|
|
1587
1696
|
this.#lsdBrowserContexts = [];
|
|
1588
1697
|
this.#browserControllerType = "playwright";
|
|
1589
|
-
this.#browserType =
|
|
1698
|
+
this.#browserType = browserType;
|
|
1699
|
+
if (!_PlaywrightBrowser.#supportedBrowserTypes.includes(browserType)) {
|
|
1700
|
+
throw new Error(`Browser controller ${this.#browserControllerType} doesnot support browserType ${browserType}`);
|
|
1701
|
+
}
|
|
1590
1702
|
this.#browserCreationMethod = browserCreateMethod;
|
|
1591
1703
|
this.#headless = headless;
|
|
1592
1704
|
this.#proxy = options?.proxy ? Object.assign({}, options.proxy) : null;
|
|
@@ -1763,6 +1875,26 @@ var PuppeteerElement = class _PuppeteerElement {
|
|
|
1763
1875
|
const names = await this.#frame.evaluate((ele) => ele.getAttributeNames(), this.#$ele);
|
|
1764
1876
|
return names;
|
|
1765
1877
|
}
|
|
1878
|
+
async dataset() {
|
|
1879
|
+
try {
|
|
1880
|
+
const attributeNames = await this.attributeNames();
|
|
1881
|
+
const dataset = {};
|
|
1882
|
+
for (const attributeName of attributeNames) {
|
|
1883
|
+
if (!attributeName.startsWith("data-")) {
|
|
1884
|
+
continue;
|
|
1885
|
+
}
|
|
1886
|
+
const val = await this.attribute(attributeName);
|
|
1887
|
+
const key = convertDataAttributeName(attributeName);
|
|
1888
|
+
dataset[key] = val;
|
|
1889
|
+
}
|
|
1890
|
+
return dataset;
|
|
1891
|
+
} catch (err) {
|
|
1892
|
+
return {};
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
async evaluate(func, args) {
|
|
1896
|
+
return await this.#frame.evaluate(func, args);
|
|
1897
|
+
}
|
|
1766
1898
|
async #getChildFrame(parentFrame, iframeOption) {
|
|
1767
1899
|
if (!parentFrame) {
|
|
1768
1900
|
throw new Error("Invalid parent frame");
|
|
@@ -1812,13 +1944,13 @@ var PuppeteerElement = class _PuppeteerElement {
|
|
|
1812
1944
|
let frame = this.#frame;
|
|
1813
1945
|
const retObj = { frame, elementHandles: [] };
|
|
1814
1946
|
if (iframeOptions.length > 0) {
|
|
1815
|
-
|
|
1816
|
-
if (!
|
|
1947
|
+
const childFrame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
1948
|
+
if (!childFrame) {
|
|
1817
1949
|
return retObj;
|
|
1818
1950
|
}
|
|
1819
|
-
retObj.frame =
|
|
1951
|
+
retObj.frame = childFrame;
|
|
1820
1952
|
absolute = true;
|
|
1821
|
-
parent =
|
|
1953
|
+
parent = childFrame;
|
|
1822
1954
|
}
|
|
1823
1955
|
try {
|
|
1824
1956
|
if (selector.startsWith("./") || selector.startsWith("/") || selector.startsWith("..")) {
|
|
@@ -2020,6 +2152,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2020
2152
|
#page;
|
|
2021
2153
|
#status;
|
|
2022
2154
|
#pageId;
|
|
2155
|
+
#closeWhenFree;
|
|
2023
2156
|
#requestInterceptionNum;
|
|
2024
2157
|
#responseInterceptionNum;
|
|
2025
2158
|
#client;
|
|
@@ -2094,15 +2227,41 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2094
2227
|
});
|
|
2095
2228
|
return true;
|
|
2096
2229
|
}
|
|
2230
|
+
async #findDescendantFrame(src, id) {
|
|
2231
|
+
if (!this.#page) {
|
|
2232
|
+
throw new Error("No valid page");
|
|
2233
|
+
}
|
|
2234
|
+
const frames = this.#page.frames();
|
|
2235
|
+
for (const frame of frames) {
|
|
2236
|
+
const url = frame.url();
|
|
2237
|
+
if (typeof src === "string" && src) {
|
|
2238
|
+
if (url.startsWith(src)) {
|
|
2239
|
+
return frame;
|
|
2240
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
2241
|
+
return frame;
|
|
2242
|
+
}
|
|
2243
|
+
} else if (src instanceof RegExp) {
|
|
2244
|
+
if (url.match(src)) {
|
|
2245
|
+
return frame;
|
|
2246
|
+
}
|
|
2247
|
+
} else if (id) {
|
|
2248
|
+
const element = await frame.frameElement();
|
|
2249
|
+
if (element) {
|
|
2250
|
+
const frameId = await frame.evaluate((ele, attr) => ele.getAttribute(attr), element, "id");
|
|
2251
|
+
if (frameId === id) {
|
|
2252
|
+
return frame;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
return null;
|
|
2258
|
+
}
|
|
2097
2259
|
async #getChildFrame(parentFrame, iframeOption) {
|
|
2098
2260
|
if (!parentFrame) {
|
|
2099
2261
|
throw new Error("Invalid parent frame");
|
|
2100
2262
|
}
|
|
2101
2263
|
let iframe = null;
|
|
2102
|
-
let { src = ""
|
|
2103
|
-
if (!src && !selector) {
|
|
2104
|
-
throw new Error("Invalid IframeOption");
|
|
2105
|
-
}
|
|
2264
|
+
let { src = "" } = iframeOption;
|
|
2106
2265
|
if (src) {
|
|
2107
2266
|
const childFrames = parentFrame.childFrames();
|
|
2108
2267
|
for (const childFrame of childFrames) {
|
|
@@ -2120,7 +2279,8 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2120
2279
|
}
|
|
2121
2280
|
}
|
|
2122
2281
|
} else {
|
|
2123
|
-
const
|
|
2282
|
+
const frameSelector = getIframeSelector(iframeOption);
|
|
2283
|
+
const $eleIframe = await parentFrame.$(frameSelector);
|
|
2124
2284
|
if ($eleIframe) {
|
|
2125
2285
|
iframe = await $eleIframe.contentFrame();
|
|
2126
2286
|
return iframe;
|
|
@@ -2130,11 +2290,16 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2130
2290
|
}
|
|
2131
2291
|
async #getDescendantFrame(parentFrame, iframeOptions) {
|
|
2132
2292
|
let iframe = parentFrame;
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2293
|
+
if (iframeOptions.length === 1 && !iframeOptions[0].selector) {
|
|
2294
|
+
const { src = "", id = "" } = iframeOptions[0];
|
|
2295
|
+
iframe = await this.#findDescendantFrame(src, id);
|
|
2296
|
+
} else {
|
|
2297
|
+
for (const iframeOption of iframeOptions) {
|
|
2298
|
+
if (!iframe) {
|
|
2299
|
+
return null;
|
|
2300
|
+
}
|
|
2301
|
+
iframe = await this.#getChildFrame(iframe, iframeOption);
|
|
2136
2302
|
}
|
|
2137
|
-
iframe = await this.#getChildFrame(iframe, iframeOption);
|
|
2138
2303
|
}
|
|
2139
2304
|
return iframe;
|
|
2140
2305
|
}
|
|
@@ -2163,7 +2328,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2163
2328
|
}
|
|
2164
2329
|
return retObj;
|
|
2165
2330
|
} catch (err) {
|
|
2166
|
-
|
|
2331
|
+
loginfo(err);
|
|
2167
2332
|
return retObj;
|
|
2168
2333
|
}
|
|
2169
2334
|
}
|
|
@@ -2216,11 +2381,31 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2216
2381
|
const { browserIdx = 0, browserContextIdx = 0, pageIdx = 0, openType = "other", openTime = currentTime, lastStatusUpdateTime = currentTime, taskId = 0, relatedId = 0, misc = {} } = pageInfo ? pageInfo : {};
|
|
2217
2382
|
this.#page.pageInfo = { browserIdx, browserContextIdx, pageIdx, openType, openTime, lastStatusUpdateTime, taskId, relatedId, misc };
|
|
2218
2383
|
this.#pageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
2384
|
+
this.#closeWhenFree = false;
|
|
2219
2385
|
this.#requestInterceptionNum = 0;
|
|
2220
2386
|
this.#responseInterceptionNum = 0;
|
|
2221
2387
|
this.#client = null;
|
|
2222
2388
|
this.#addPageOn();
|
|
2223
2389
|
}
|
|
2390
|
+
async addPreloadScript(scriptOrFunc, arg) {
|
|
2391
|
+
if (!this.#page) {
|
|
2392
|
+
throw new Error("No valid page");
|
|
2393
|
+
}
|
|
2394
|
+
if (typeof scriptOrFunc === "string") {
|
|
2395
|
+
await this.#page.evaluateOnNewDocument(scriptOrFunc);
|
|
2396
|
+
} else if (typeof scriptOrFunc === "function") {
|
|
2397
|
+
await this.#page.evaluateOnNewDocument(scriptOrFunc, arg);
|
|
2398
|
+
} else {
|
|
2399
|
+
throw new Error(`Invalid type of scriptOrFunc ${typeof scriptOrFunc}`);
|
|
2400
|
+
}
|
|
2401
|
+
return true;
|
|
2402
|
+
}
|
|
2403
|
+
async addScriptTag(options) {
|
|
2404
|
+
if (!this.#page) {
|
|
2405
|
+
throw new Error("No valid page");
|
|
2406
|
+
}
|
|
2407
|
+
return this.#page.addScriptTag(options);
|
|
2408
|
+
}
|
|
2224
2409
|
apiContext() {
|
|
2225
2410
|
throw new Error("Not supported in PuppeteerPage.");
|
|
2226
2411
|
}
|
|
@@ -2277,7 +2462,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2277
2462
|
}
|
|
2278
2463
|
async close() {
|
|
2279
2464
|
if (this.#status === "closed") {
|
|
2280
|
-
|
|
2465
|
+
logwarn(`Page ${this.#pageId} is already closed.`);
|
|
2281
2466
|
return true;
|
|
2282
2467
|
} else if (this.#status === "busy") {
|
|
2283
2468
|
throw new Error(`Page ${this.#pageId} cannot be closed because it is busy.`);
|
|
@@ -2290,6 +2475,9 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2290
2475
|
this.#status = "closed";
|
|
2291
2476
|
return true;
|
|
2292
2477
|
}
|
|
2478
|
+
closeWhenFree() {
|
|
2479
|
+
return this.#closeWhenFree;
|
|
2480
|
+
}
|
|
2293
2481
|
async content(iframeOptions = []) {
|
|
2294
2482
|
if (!this.#page) {
|
|
2295
2483
|
throw new Error("No valid page");
|
|
@@ -2318,11 +2506,18 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2318
2506
|
const height = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
2319
2507
|
return height;
|
|
2320
2508
|
}
|
|
2321
|
-
async
|
|
2509
|
+
async evaluate(func, args) {
|
|
2510
|
+
if (!this.#page) {
|
|
2511
|
+
throw new Error("No valid page");
|
|
2512
|
+
}
|
|
2513
|
+
return this.#page.evaluate(func, args);
|
|
2514
|
+
}
|
|
2515
|
+
async exposeFunction(name, callbackFunction) {
|
|
2322
2516
|
if (!this.#page) {
|
|
2323
2517
|
throw new Error("No valid page");
|
|
2324
2518
|
}
|
|
2325
|
-
|
|
2519
|
+
await this.#page.exposeFunction(name, callbackFunction);
|
|
2520
|
+
return;
|
|
2326
2521
|
}
|
|
2327
2522
|
async findElement(selectorOrXpath, iframeOptions = []) {
|
|
2328
2523
|
if (!this.#page) {
|
|
@@ -2360,7 +2555,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2360
2555
|
}
|
|
2361
2556
|
async free() {
|
|
2362
2557
|
if (this.#status === "free") {
|
|
2363
|
-
|
|
2558
|
+
logwarn(`Page ${this.#pageId} is already free.`);
|
|
2364
2559
|
}
|
|
2365
2560
|
this.#status = "free";
|
|
2366
2561
|
await this.clearRequestInterceptions();
|
|
@@ -2503,6 +2698,10 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2503
2698
|
}
|
|
2504
2699
|
return response;
|
|
2505
2700
|
}
|
|
2701
|
+
setCloseWhenFree(closeWhenFree) {
|
|
2702
|
+
this.#closeWhenFree = closeWhenFree;
|
|
2703
|
+
return true;
|
|
2704
|
+
}
|
|
2506
2705
|
async setCookies(cookies) {
|
|
2507
2706
|
if (!this.#page) {
|
|
2508
2707
|
throw new Error("No valid page");
|
|
@@ -2578,7 +2777,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2578
2777
|
}
|
|
2579
2778
|
const actOptions = Array.isArray(options) ? options : [options];
|
|
2580
2779
|
if (actOptions.length <= 0) {
|
|
2581
|
-
|
|
2780
|
+
logwarn("Invalid paras in setRequestInterception");
|
|
2582
2781
|
return false;
|
|
2583
2782
|
}
|
|
2584
2783
|
if (this.#requestInterceptionNum <= 0) {
|
|
@@ -2614,7 +2813,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2614
2813
|
await request.continue();
|
|
2615
2814
|
return true;
|
|
2616
2815
|
} catch (err) {
|
|
2617
|
-
|
|
2816
|
+
logerr(err);
|
|
2618
2817
|
return false;
|
|
2619
2818
|
}
|
|
2620
2819
|
});
|
|
@@ -2626,7 +2825,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2626
2825
|
}
|
|
2627
2826
|
const actOptions = Array.isArray(options) ? options : [options];
|
|
2628
2827
|
if (actOptions.length <= 0) {
|
|
2629
|
-
|
|
2828
|
+
logwarn("Invalid paras in setResponseInterception");
|
|
2630
2829
|
return false;
|
|
2631
2830
|
}
|
|
2632
2831
|
this.#responseInterceptionNum++;
|
|
@@ -2681,7 +2880,7 @@ var PuppeteerPage = class extends import_node_events4.default {
|
|
|
2681
2880
|
}
|
|
2682
2881
|
return true;
|
|
2683
2882
|
} catch (err) {
|
|
2684
|
-
|
|
2883
|
+
logerr(err);
|
|
2685
2884
|
return false;
|
|
2686
2885
|
}
|
|
2687
2886
|
});
|
|
@@ -3139,7 +3338,11 @@ var PuppeteerBrowserContext = class extends import_node_events5.default {
|
|
|
3139
3338
|
};
|
|
3140
3339
|
|
|
3141
3340
|
// src/puppeteer/browser.ts
|
|
3142
|
-
var PuppeteerBrowser = class extends import_node_events6.default {
|
|
3341
|
+
var PuppeteerBrowser = class _PuppeteerBrowser extends import_node_events6.default {
|
|
3342
|
+
static #supportedBrowserTypes = ["chromium"];
|
|
3343
|
+
static doesSupport(browserType) {
|
|
3344
|
+
return _PuppeteerBrowser.#supportedBrowserTypes.includes(browserType);
|
|
3345
|
+
}
|
|
3143
3346
|
#browser;
|
|
3144
3347
|
#browserIdx;
|
|
3145
3348
|
#pid;
|
|
@@ -3171,7 +3374,7 @@ var PuppeteerBrowser = class extends import_node_events6.default {
|
|
|
3171
3374
|
return this.#options.userAgent ? this.#options.userAgent : "";
|
|
3172
3375
|
}
|
|
3173
3376
|
// constructor: called only by LsdBrowserController.launch/connect
|
|
3174
|
-
constructor(browser,
|
|
3377
|
+
constructor(browser, browserType, browserCreateMethod, options, browserIdx = 0, pid = 0) {
|
|
3175
3378
|
if (!browser || typeof browser.browserContexts !== "function") {
|
|
3176
3379
|
throw new Error(`Invalid puppeteer browser parameter`);
|
|
3177
3380
|
}
|
|
@@ -3183,7 +3386,10 @@ var PuppeteerBrowser = class extends import_node_events6.default {
|
|
|
3183
3386
|
this.#createTime = (0, import_utils9.getCurrentUnixTime)();
|
|
3184
3387
|
this.#lsdBrowserContexts = [];
|
|
3185
3388
|
this.#browserControllerType = "puppeteer";
|
|
3186
|
-
this.#browserType =
|
|
3389
|
+
this.#browserType = browserType;
|
|
3390
|
+
if (!_PuppeteerBrowser.#supportedBrowserTypes.includes(browserType)) {
|
|
3391
|
+
throw new Error(`Browser controller ${this.#browserControllerType} doesnot support browserType ${browserType}`);
|
|
3392
|
+
}
|
|
3187
3393
|
this.#browserCreationMethod = browserCreateMethod;
|
|
3188
3394
|
this.#headless = headless;
|
|
3189
3395
|
this.#proxy = options?.proxy ? Object.assign({}, options.proxy) : null;
|
|
@@ -3341,6 +3547,22 @@ var CheerioElement = class _CheerioElement {
|
|
|
3341
3547
|
return Array.from(Object.keys(element.attribs));
|
|
3342
3548
|
}
|
|
3343
3549
|
}
|
|
3550
|
+
async dataset() {
|
|
3551
|
+
const attributeNames = await this.attributeNames();
|
|
3552
|
+
const dataset = {};
|
|
3553
|
+
for (const attributeName of attributeNames) {
|
|
3554
|
+
if (!attributeName.startsWith("data-")) {
|
|
3555
|
+
continue;
|
|
3556
|
+
}
|
|
3557
|
+
const val = await this.attribute(attributeName);
|
|
3558
|
+
const key = convertDataAttributeName(attributeName);
|
|
3559
|
+
dataset[key] = val;
|
|
3560
|
+
}
|
|
3561
|
+
return dataset;
|
|
3562
|
+
}
|
|
3563
|
+
async evaluate() {
|
|
3564
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3565
|
+
}
|
|
3344
3566
|
#findNodes(selector, absolute) {
|
|
3345
3567
|
if (selector.startsWith("./") || selector.startsWith("/")) {
|
|
3346
3568
|
throw new Error("Do not support XPath in cheerio.");
|
|
@@ -3461,6 +3683,12 @@ var CheerioPage = class extends import_node_events7.default {
|
|
|
3461
3683
|
_origPage() {
|
|
3462
3684
|
throw new Error("Method not implemented.");
|
|
3463
3685
|
}
|
|
3686
|
+
async addPreloadScript() {
|
|
3687
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3688
|
+
}
|
|
3689
|
+
addScriptTag() {
|
|
3690
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3691
|
+
}
|
|
3464
3692
|
apiContext() {
|
|
3465
3693
|
throw new Error("Not supported in CheerioPage.");
|
|
3466
3694
|
}
|
|
@@ -3488,13 +3716,19 @@ var CheerioPage = class extends import_node_events7.default {
|
|
|
3488
3716
|
async close() {
|
|
3489
3717
|
throw new Error("Not supported in CheerioPage.");
|
|
3490
3718
|
}
|
|
3719
|
+
closeWhenFree() {
|
|
3720
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3721
|
+
}
|
|
3491
3722
|
async content() {
|
|
3492
3723
|
throw new Error("Not supported in CheerioPage.");
|
|
3493
3724
|
}
|
|
3494
3725
|
async cookies() {
|
|
3495
3726
|
throw new Error("Not supported in CheerioPage.");
|
|
3496
3727
|
}
|
|
3497
|
-
async
|
|
3728
|
+
async evaluate() {
|
|
3729
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3730
|
+
}
|
|
3731
|
+
exposeFunction() {
|
|
3498
3732
|
throw new Error("Not supported in CheerioPage.");
|
|
3499
3733
|
}
|
|
3500
3734
|
#findNodes(selector) {
|
|
@@ -3591,6 +3825,9 @@ var CheerioPage = class extends import_node_events7.default {
|
|
|
3591
3825
|
async sendCDPMessage() {
|
|
3592
3826
|
throw new Error("Method not implemented.");
|
|
3593
3827
|
}
|
|
3828
|
+
setCloseWhenFree() {
|
|
3829
|
+
throw new Error("Not supported in CheerioPage.");
|
|
3830
|
+
}
|
|
3594
3831
|
async setCookies() {
|
|
3595
3832
|
throw new Error("Not supported in CheerioPage.");
|
|
3596
3833
|
}
|
|
@@ -3648,88 +3885,1862 @@ var CheerioPage = class extends import_node_events7.default {
|
|
|
3648
3885
|
var import_os = __toESM(require("os"), 1);
|
|
3649
3886
|
var import_puppeteer = __toESM(require("puppeteer"), 1);
|
|
3650
3887
|
var import_playwright = __toESM(require("playwright"), 1);
|
|
3651
|
-
var
|
|
3652
|
-
|
|
3653
|
-
|
|
3888
|
+
var import_patchright = __toESM(require("patchright"), 1);
|
|
3889
|
+
|
|
3890
|
+
// src/patchright/browser.ts
|
|
3891
|
+
var import_node_events10 = __toESM(require("events"), 1);
|
|
3892
|
+
var import_utils13 = require("@letsscrapedata/utils");
|
|
3893
|
+
|
|
3894
|
+
// src/patchright/context.ts
|
|
3895
|
+
var import_node_events9 = __toESM(require("events"), 1);
|
|
3896
|
+
var import_utils12 = require("@letsscrapedata/utils");
|
|
3897
|
+
|
|
3898
|
+
// src/patchright/page.ts
|
|
3899
|
+
var import_node_events8 = __toESM(require("events"), 1);
|
|
3900
|
+
var import_utils11 = require("@letsscrapedata/utils");
|
|
3901
|
+
|
|
3902
|
+
// src/patchright/element.ts
|
|
3654
3903
|
var import_utils10 = require("@letsscrapedata/utils");
|
|
3655
|
-
var
|
|
3656
|
-
|
|
3657
|
-
#
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
* Possible values are 'aix', 'darwin', 'freebsd','linux', 'openbsd', 'sunos', and 'win32'.
|
|
3662
|
-
*/
|
|
3663
|
-
#osPlatform;
|
|
3664
|
-
constructor() {
|
|
3665
|
-
if (_LsdBrowserController.#forbidConstructor) {
|
|
3666
|
-
throw new Error("Only one LsdBrowserController instance can be created!");
|
|
3904
|
+
var PatchrightElement = class _PatchrightElement {
|
|
3905
|
+
#frame;
|
|
3906
|
+
#locator;
|
|
3907
|
+
constructor(locator, frame) {
|
|
3908
|
+
if (!frame.locator || !locator.click) {
|
|
3909
|
+
throw new Error("Invalid paras in new PatchrightElement");
|
|
3667
3910
|
}
|
|
3668
|
-
this.#
|
|
3669
|
-
|
|
3670
|
-
this.#playwrightBrowserTypes = { chromium: chromium2, firefox: firefox2, webkit: webkit2 };
|
|
3671
|
-
this.#osPlatform = import_os.default.platform();
|
|
3672
|
-
this.#nextBrowserIdx = 1;
|
|
3673
|
-
_LsdBrowserController.#forbidConstructor = true;
|
|
3911
|
+
this.#frame = frame;
|
|
3912
|
+
this.#locator = locator;
|
|
3674
3913
|
}
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
} else if (connectFlag) {
|
|
3679
|
-
throw new Error(`playwright only can connect to chromium browser, not support ${browserType} browser`);
|
|
3680
|
-
} else if (browserType === "firefox") {
|
|
3681
|
-
return this.#playwrightBrowserTypes.firefox;
|
|
3682
|
-
} else if (browserType === "webkit") {
|
|
3683
|
-
return this.#playwrightBrowserTypes.webkit;
|
|
3684
|
-
} else {
|
|
3685
|
-
throw new Error(`Invalid playwright browserType ${browserType}`);
|
|
3686
|
-
}
|
|
3914
|
+
async attribute(attributeName) {
|
|
3915
|
+
const attributeValue = await this.#locator.getAttribute(attributeName);
|
|
3916
|
+
return attributeValue ? attributeValue : "";
|
|
3687
3917
|
}
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3918
|
+
async attributeNames() {
|
|
3919
|
+
const names = await this.#locator.evaluate((node) => node.getAttributeNames());
|
|
3920
|
+
return names;
|
|
3921
|
+
}
|
|
3922
|
+
async dataset() {
|
|
3923
|
+
try {
|
|
3924
|
+
const dataset = await this.#locator.evaluate((node) => node.dataset);
|
|
3925
|
+
return dataset;
|
|
3926
|
+
} catch (err) {
|
|
3927
|
+
return {};
|
|
3693
3928
|
}
|
|
3694
3929
|
}
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3930
|
+
async evaluate(func, args) {
|
|
3931
|
+
try {
|
|
3932
|
+
const frame = this.#frame;
|
|
3933
|
+
;
|
|
3934
|
+
if (typeof frame.parentFrame === "function") {
|
|
3935
|
+
return await frame.evaluate(func, args);
|
|
3936
|
+
} else {
|
|
3937
|
+
const locator = this.#frame.owner();
|
|
3938
|
+
return await locator.evaluate(func, args);
|
|
3939
|
+
}
|
|
3940
|
+
} catch (err) {
|
|
3941
|
+
logerr(err);
|
|
3942
|
+
return "";
|
|
3700
3943
|
}
|
|
3701
|
-
return true;
|
|
3702
3944
|
}
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3945
|
+
/*
|
|
3946
|
+
async #getChildFrame(parentFrame: Frame, iframeOption: IframeOption): Promise<Frame | null> {
|
|
3947
|
+
if (!parentFrame) {
|
|
3948
|
+
throw new Error("Invalid parent frame");
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
let { src = "" } = iframeOption;
|
|
3952
|
+
if (!src) {
|
|
3953
|
+
throw new Error("Invalid src in IframeOption");
|
|
3954
|
+
}
|
|
3955
|
+
|
|
3956
|
+
// src: use childFrames()
|
|
3957
|
+
const childFrames = parentFrame.childFrames();
|
|
3958
|
+
for (const childFrame of childFrames) {
|
|
3959
|
+
const url = childFrame.url();
|
|
3960
|
+
if (typeof src === "string") {
|
|
3961
|
+
// src: string
|
|
3962
|
+
if (url.startsWith(src)) {
|
|
3963
|
+
return childFrame;
|
|
3964
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
3965
|
+
return childFrame;
|
|
3966
|
+
}
|
|
3715
3967
|
} else {
|
|
3716
|
-
|
|
3968
|
+
// src: RegExp
|
|
3969
|
+
if (url.match(src)) {
|
|
3970
|
+
return childFrame;
|
|
3971
|
+
}
|
|
3717
3972
|
}
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3975
|
+
return null;
|
|
3976
|
+
}
|
|
3977
|
+
*/
|
|
3978
|
+
async #getChildFrameLocator(parent, iframeOption) {
|
|
3979
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
3980
|
+
}
|
|
3981
|
+
async #getDescendantFrame(parent, iframeOptions) {
|
|
3982
|
+
try {
|
|
3983
|
+
if (iframeOptions.length <= 0) {
|
|
3984
|
+
return null;
|
|
3985
|
+
}
|
|
3986
|
+
let frameLocator = parent.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
3987
|
+
for (const iframeOption of iframeOptions.slice(1)) {
|
|
3988
|
+
if (!frameLocator) {
|
|
3989
|
+
return null;
|
|
3724
3990
|
}
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3991
|
+
frameLocator = await this.#getChildFrameLocator(frameLocator, iframeOption);
|
|
3992
|
+
}
|
|
3993
|
+
return frameLocator;
|
|
3994
|
+
} catch (err) {
|
|
3995
|
+
throw new Error(`No child iframe: ${JSON.stringify(iframeOptions)}`);
|
|
3728
3996
|
}
|
|
3729
|
-
return true;
|
|
3730
3997
|
}
|
|
3731
|
-
async
|
|
3732
|
-
let
|
|
3998
|
+
async #findElementHandles(selector, absolute = false, iframeOptions = []) {
|
|
3999
|
+
let parent = absolute ? this.#frame : this.#locator;
|
|
4000
|
+
let frame = this.#frame;
|
|
4001
|
+
const retObj = { frame, locators: [] };
|
|
4002
|
+
if (iframeOptions.length > 0) {
|
|
4003
|
+
const childFrame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
4004
|
+
if (!childFrame) {
|
|
4005
|
+
return retObj;
|
|
4006
|
+
}
|
|
4007
|
+
retObj.frame = childFrame;
|
|
4008
|
+
parent = childFrame;
|
|
4009
|
+
}
|
|
4010
|
+
try {
|
|
4011
|
+
let locators = [];
|
|
4012
|
+
if (selector.startsWith("./") || selector.startsWith("/") || selector.startsWith("..")) {
|
|
4013
|
+
locators = await parent.locator(`xpath=${selector}`).all();
|
|
4014
|
+
} else {
|
|
4015
|
+
if (selector !== ".") {
|
|
4016
|
+
locators = await parent.locator(selector).all();
|
|
4017
|
+
} else {
|
|
4018
|
+
locators = [this.#locator];
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
retObj.locators = locators;
|
|
4022
|
+
return retObj;
|
|
4023
|
+
} catch (err) {
|
|
4024
|
+
loginfo(err);
|
|
4025
|
+
return retObj;
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
async findElement(selectorOrXpath, iframeOptions = [], absolute = false) {
|
|
4029
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
4030
|
+
if (!Array.isArray(selectors)) {
|
|
4031
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElement`);
|
|
4032
|
+
}
|
|
4033
|
+
for (const selector of selectors) {
|
|
4034
|
+
const { frame, locators } = await this.#findElementHandles(selector, absolute, iframeOptions);
|
|
4035
|
+
if (locators.length > 0) {
|
|
4036
|
+
const playwrightElement = new _PatchrightElement(locators[0], frame);
|
|
4037
|
+
return playwrightElement;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
return null;
|
|
4041
|
+
}
|
|
4042
|
+
async findElements(selectorOrXpath, iframeOptions = [], absolute = false) {
|
|
4043
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
4044
|
+
if (!Array.isArray(selectors)) {
|
|
4045
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElements`);
|
|
4046
|
+
}
|
|
4047
|
+
for (const selector of selectors) {
|
|
4048
|
+
const { frame, locators } = await this.#findElementHandles(selector, absolute, iframeOptions);
|
|
4049
|
+
if (locators.length > 0) {
|
|
4050
|
+
const playwrightElements = locators.map((locator) => new _PatchrightElement(locator, frame));
|
|
4051
|
+
return playwrightElements;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
return [];
|
|
4055
|
+
}
|
|
4056
|
+
async hasAttribute(attributeName) {
|
|
4057
|
+
const hasFlag = await this.#locator.evaluate((node, attr) => node.hasAttribute(attr), attributeName);
|
|
4058
|
+
return hasFlag;
|
|
4059
|
+
}
|
|
4060
|
+
async innerHtml() {
|
|
4061
|
+
const html = await this.#locator.innerHTML();
|
|
4062
|
+
return html;
|
|
4063
|
+
}
|
|
4064
|
+
async innerText(onlyChild = false) {
|
|
4065
|
+
let text = "";
|
|
4066
|
+
if (onlyChild) {
|
|
4067
|
+
text = await this.#locator.evaluate((node) => {
|
|
4068
|
+
let child = node.firstChild;
|
|
4069
|
+
let texts = [];
|
|
4070
|
+
while (child) {
|
|
4071
|
+
if (child.nodeType == 3) {
|
|
4072
|
+
texts.push(child.data);
|
|
4073
|
+
}
|
|
4074
|
+
child = child.nextSibling;
|
|
4075
|
+
}
|
|
4076
|
+
return texts.join(" ");
|
|
4077
|
+
});
|
|
4078
|
+
} else {
|
|
4079
|
+
text = await this.#locator.innerText();
|
|
4080
|
+
}
|
|
4081
|
+
return text;
|
|
4082
|
+
}
|
|
4083
|
+
async outerHtml() {
|
|
4084
|
+
const html = await this.#locator.evaluate((node) => node.outerHTML);
|
|
4085
|
+
return html;
|
|
4086
|
+
}
|
|
4087
|
+
async textContent() {
|
|
4088
|
+
const text = await this.#locator.textContent();
|
|
4089
|
+
return text ? text : "";
|
|
4090
|
+
}
|
|
4091
|
+
async click(options = {}) {
|
|
4092
|
+
const { button, clickCount: count, delay, position: offset, clickType = "click" } = options;
|
|
4093
|
+
const actOptions = { button, count, delay, offset };
|
|
4094
|
+
if (clickType === "click") {
|
|
4095
|
+
await this.#locator.click(actOptions);
|
|
4096
|
+
} else if (clickType === "evaluate") {
|
|
4097
|
+
await this.#locator.evaluate(async (ev) => await ev.click());
|
|
4098
|
+
} else {
|
|
4099
|
+
(0, import_utils10.unreachable)(clickType);
|
|
4100
|
+
}
|
|
4101
|
+
return true;
|
|
4102
|
+
}
|
|
4103
|
+
async focus() {
|
|
4104
|
+
await this.#locator.focus();
|
|
4105
|
+
return true;
|
|
4106
|
+
}
|
|
4107
|
+
async hover() {
|
|
4108
|
+
await this.#locator.hover();
|
|
4109
|
+
return true;
|
|
4110
|
+
}
|
|
4111
|
+
async input(value, options = {}) {
|
|
4112
|
+
const { delay = 0, replace = false, enter = false } = options;
|
|
4113
|
+
if (replace) {
|
|
4114
|
+
await this.#locator.click({ button: "left", clickCount: 3 });
|
|
4115
|
+
}
|
|
4116
|
+
if (delay > 0) {
|
|
4117
|
+
await this.#locator.fill(value);
|
|
4118
|
+
} else {
|
|
4119
|
+
await this.#locator.fill(value);
|
|
4120
|
+
}
|
|
4121
|
+
if (enter) {
|
|
4122
|
+
await this.#locator.press("Enter");
|
|
4123
|
+
}
|
|
4124
|
+
return true;
|
|
4125
|
+
}
|
|
4126
|
+
async press(key, options = {}) {
|
|
4127
|
+
await this.#locator.press(key, options);
|
|
4128
|
+
return true;
|
|
4129
|
+
}
|
|
4130
|
+
async screenshot(options) {
|
|
4131
|
+
return await this.#locator.screenshot(options);
|
|
4132
|
+
}
|
|
4133
|
+
async scrollIntoView() {
|
|
4134
|
+
await this.#locator.scrollIntoViewIfNeeded();
|
|
4135
|
+
return true;
|
|
4136
|
+
}
|
|
4137
|
+
async select(options) {
|
|
4138
|
+
const { type, values = [], labels = [], indexes = [] } = options;
|
|
4139
|
+
switch (type) {
|
|
4140
|
+
case "value":
|
|
4141
|
+
if (values.length > 0) {
|
|
4142
|
+
await this.#locator.selectOption(values);
|
|
4143
|
+
}
|
|
4144
|
+
break;
|
|
4145
|
+
case "label":
|
|
4146
|
+
if (labels.length > 0) {
|
|
4147
|
+
await this.#locator.selectOption(labels.map((label) => {
|
|
4148
|
+
return { label };
|
|
4149
|
+
}));
|
|
4150
|
+
}
|
|
4151
|
+
break;
|
|
4152
|
+
case "index":
|
|
4153
|
+
if (indexes.length > 0) {
|
|
4154
|
+
const indexValues = await this.#locator.evaluate(
|
|
4155
|
+
(node, indexes2) => {
|
|
4156
|
+
const options2 = node.options;
|
|
4157
|
+
const len = options2.length;
|
|
4158
|
+
const vals = [];
|
|
4159
|
+
for (const index of indexes2.filter((i) => i >= 0 && i < len)) {
|
|
4160
|
+
vals.push(options2[index].value);
|
|
4161
|
+
}
|
|
4162
|
+
return vals;
|
|
4163
|
+
},
|
|
4164
|
+
indexes
|
|
4165
|
+
);
|
|
4166
|
+
if (indexValues.length > 0) {
|
|
4167
|
+
await this.#locator.selectOption(indexValues);
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
break;
|
|
4171
|
+
default:
|
|
4172
|
+
(0, import_utils10.unreachable)(type);
|
|
4173
|
+
}
|
|
4174
|
+
return true;
|
|
4175
|
+
}
|
|
4176
|
+
async setAttribute(attributeName, newValue) {
|
|
4177
|
+
await this.#locator.evaluate((node, argvs) => {
|
|
4178
|
+
node.setAttribute(argvs[0], argvs[1]);
|
|
4179
|
+
}, [attributeName, newValue]);
|
|
4180
|
+
return true;
|
|
4181
|
+
}
|
|
4182
|
+
_origElement() {
|
|
4183
|
+
return this.#locator;
|
|
4184
|
+
}
|
|
4185
|
+
};
|
|
4186
|
+
|
|
4187
|
+
// src/patchright/page.ts
|
|
4188
|
+
var PatchrightPage = class extends import_node_events8.default {
|
|
4189
|
+
#lsdBrowserContext;
|
|
4190
|
+
#page;
|
|
4191
|
+
#status;
|
|
4192
|
+
#pageId;
|
|
4193
|
+
#closeWhenFree;
|
|
4194
|
+
#resquestInterceptionOptions;
|
|
4195
|
+
#responseInterceptionOptions;
|
|
4196
|
+
#client;
|
|
4197
|
+
#responseCb;
|
|
4198
|
+
#hasValidUrl(page) {
|
|
4199
|
+
const url = page.url();
|
|
4200
|
+
return url.toLowerCase().startsWith("http");
|
|
4201
|
+
}
|
|
4202
|
+
async #clearCookies(page) {
|
|
4203
|
+
if (!this.#hasValidUrl(page)) {
|
|
4204
|
+
throw new Error("Please open related url before clearing cookies");
|
|
4205
|
+
}
|
|
4206
|
+
const browserContext = this.#lsdBrowserContext._origBrowserContext();
|
|
4207
|
+
if (!browserContext) {
|
|
4208
|
+
throw new Error(`Invalid LsdBrowserContext`);
|
|
4209
|
+
}
|
|
4210
|
+
const cookieItems = await this.#getCookies(page);
|
|
4211
|
+
const domainSet = new Set(cookieItems.map((c) => c.domain));
|
|
4212
|
+
if (domainSet.size !== 1) {
|
|
4213
|
+
logwarn(`Domains in clearCookies: ${Array.from(domainSet.values())}`);
|
|
4214
|
+
}
|
|
4215
|
+
for (const domain of domainSet.values()) {
|
|
4216
|
+
await browserContext.clearCookies({ domain });
|
|
4217
|
+
}
|
|
4218
|
+
return true;
|
|
4219
|
+
}
|
|
4220
|
+
async #getCookies(page) {
|
|
4221
|
+
if (!this.#hasValidUrl(page)) {
|
|
4222
|
+
throw new Error("Please open related url before getting cookies");
|
|
4223
|
+
}
|
|
4224
|
+
const browserContext = this.#lsdBrowserContext._origBrowserContext();
|
|
4225
|
+
if (!browserContext) {
|
|
4226
|
+
throw new Error(`Invalid LsdBrowserContext`);
|
|
4227
|
+
}
|
|
4228
|
+
const url = page.url();
|
|
4229
|
+
const origCookies = await browserContext.cookies(url);
|
|
4230
|
+
const cookies = origCookies.map((origCookie) => {
|
|
4231
|
+
const { name, value, domain, path, expires, httpOnly, secure, sameSite = "Lax" } = origCookie;
|
|
4232
|
+
return { name, value, domain, path, expires, httpOnly, secure, sameSite };
|
|
4233
|
+
});
|
|
4234
|
+
return cookies;
|
|
4235
|
+
}
|
|
4236
|
+
async #setCookies(page, cookies) {
|
|
4237
|
+
if (!page) {
|
|
4238
|
+
throw new Error("No valid page");
|
|
4239
|
+
}
|
|
4240
|
+
if (Array.isArray(cookies) && cookies.length > 0 && cookies.every((c) => typeof c.name === "string")) {
|
|
4241
|
+
const browserContext = this.#lsdBrowserContext._origBrowserContext();
|
|
4242
|
+
if (!browserContext) {
|
|
4243
|
+
throw new Error(`Invalid LsdBrowserContext`);
|
|
4244
|
+
}
|
|
4245
|
+
await browserContext.addCookies(cookies);
|
|
4246
|
+
return true;
|
|
4247
|
+
} else {
|
|
4248
|
+
return false;
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
async #clearLocalStorage(page) {
|
|
4252
|
+
if (!this.#hasValidUrl(page)) {
|
|
4253
|
+
throw new Error("Please open related url before clearing localStorage");
|
|
4254
|
+
}
|
|
4255
|
+
await page.evaluate(() => window.localStorage.clear());
|
|
4256
|
+
return true;
|
|
4257
|
+
}
|
|
4258
|
+
async #getLocalStorage(page) {
|
|
4259
|
+
if (!this.#hasValidUrl(page)) {
|
|
4260
|
+
throw new Error("Please open related url before getting localStorage");
|
|
4261
|
+
}
|
|
4262
|
+
const localStorageStr = await page.evaluate(() => JSON.stringify(window.localStorage));
|
|
4263
|
+
const localStorageObj = JSON.parse(localStorageStr);
|
|
4264
|
+
const localStorageItems = Object.keys(localStorageObj).map((name) => ({ name, value: localStorageObj[name] }));
|
|
4265
|
+
const url = new URL(page.url());
|
|
4266
|
+
return [{ origin: url.origin, localStorage: localStorageItems }];
|
|
4267
|
+
}
|
|
4268
|
+
async #setLocalStorage(page, localStorageItems) {
|
|
4269
|
+
if (!this.#hasValidUrl(page)) {
|
|
4270
|
+
throw new Error("Please open related url before setting localStorage");
|
|
4271
|
+
}
|
|
4272
|
+
await page.evaluate((items) => {
|
|
4273
|
+
for (const item of items) {
|
|
4274
|
+
window.localStorage.setItem(item.name, item.value);
|
|
4275
|
+
}
|
|
4276
|
+
}, localStorageItems);
|
|
4277
|
+
return true;
|
|
4278
|
+
}
|
|
4279
|
+
async #clearIndexedDB(page) {
|
|
4280
|
+
if (!this.#hasValidUrl(page)) {
|
|
4281
|
+
throw new Error("Please open related url before clearing indexedDB");
|
|
4282
|
+
}
|
|
4283
|
+
await page.evaluate(async () => {
|
|
4284
|
+
for (const db of await indexedDB.databases?.() || []) {
|
|
4285
|
+
if (db.name)
|
|
4286
|
+
indexedDB.deleteDatabase(db.name);
|
|
4287
|
+
}
|
|
4288
|
+
});
|
|
4289
|
+
return true;
|
|
4290
|
+
}
|
|
4291
|
+
/*
|
|
4292
|
+
async #getChildFrame(parentFrame: Frame, iframeOption: IframeOption): Promise<Frame | null> {
|
|
4293
|
+
if (!parentFrame) {
|
|
4294
|
+
throw new Error("Invalid parent frame");
|
|
4295
|
+
}
|
|
4296
|
+
|
|
4297
|
+
let { src = "" } = iframeOption;
|
|
4298
|
+
if (!src) {
|
|
4299
|
+
throw new Error("Invalid src in IframeOption");
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
// src: use childFrames()
|
|
4303
|
+
const childFrames = parentFrame.childFrames();
|
|
4304
|
+
for (const childFrame of childFrames) {
|
|
4305
|
+
const url = childFrame.url();
|
|
4306
|
+
if (typeof src === "string") {
|
|
4307
|
+
// src: string
|
|
4308
|
+
if (url.startsWith(src)) {
|
|
4309
|
+
return childFrame;
|
|
4310
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
4311
|
+
return childFrame;
|
|
4312
|
+
}
|
|
4313
|
+
} else {
|
|
4314
|
+
// src: RegExp
|
|
4315
|
+
if (url.match(src)) {
|
|
4316
|
+
return childFrame;
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
return null;
|
|
4322
|
+
}
|
|
4323
|
+
*/
|
|
4324
|
+
async #findDescendantFrame(src, id) {
|
|
4325
|
+
if (!this.#page) {
|
|
4326
|
+
throw new Error("No valid page");
|
|
4327
|
+
}
|
|
4328
|
+
const frames = this.#page.frames();
|
|
4329
|
+
for (const frame of frames) {
|
|
4330
|
+
const url = frame.url();
|
|
4331
|
+
if (typeof src === "string" && src) {
|
|
4332
|
+
if (url.startsWith(src)) {
|
|
4333
|
+
return frame;
|
|
4334
|
+
} else if (url.toLowerCase().startsWith(src)) {
|
|
4335
|
+
return frame;
|
|
4336
|
+
}
|
|
4337
|
+
} else if (src instanceof RegExp) {
|
|
4338
|
+
if (url.match(src)) {
|
|
4339
|
+
return frame;
|
|
4340
|
+
}
|
|
4341
|
+
} else if (id) {
|
|
4342
|
+
const element = await frame.frameElement();
|
|
4343
|
+
if (element) {
|
|
4344
|
+
const frameId = await frame.evaluate(([ele, attr]) => ele.getAttribute(attr), [element, "id"]);
|
|
4345
|
+
if (frameId === id) {
|
|
4346
|
+
return frame;
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
return null;
|
|
4352
|
+
}
|
|
4353
|
+
async #getChildFrameLocator(parent, iframeOption) {
|
|
4354
|
+
return parent.frameLocator(getIframeSelector(iframeOption));
|
|
4355
|
+
}
|
|
4356
|
+
async #getDescendantFrame(mainFrame, iframeOptions) {
|
|
4357
|
+
try {
|
|
4358
|
+
if (iframeOptions.length <= 0) {
|
|
4359
|
+
return null;
|
|
4360
|
+
}
|
|
4361
|
+
if (iframeOptions.length === 1 && !iframeOptions[0].selector) {
|
|
4362
|
+
const { src = "", id = "" } = iframeOptions[0];
|
|
4363
|
+
const frame = await this.#findDescendantFrame(src, id);
|
|
4364
|
+
return frame;
|
|
4365
|
+
} else {
|
|
4366
|
+
let frameLocator = mainFrame.frameLocator(getIframeSelector(iframeOptions[0]));
|
|
4367
|
+
for (const iframeOption of iframeOptions.slice(1)) {
|
|
4368
|
+
if (!frameLocator) {
|
|
4369
|
+
return null;
|
|
4370
|
+
}
|
|
4371
|
+
frameLocator = await this.#getChildFrameLocator(frameLocator, iframeOption);
|
|
4372
|
+
}
|
|
4373
|
+
return frameLocator;
|
|
4374
|
+
}
|
|
4375
|
+
} catch (err) {
|
|
4376
|
+
throw new Error(`No child iframe: ${JSON.stringify(iframeOptions)}`);
|
|
4377
|
+
}
|
|
4378
|
+
}
|
|
4379
|
+
async #findElementHandles(selector, iframeOptions = []) {
|
|
4380
|
+
if (!this.#page) {
|
|
4381
|
+
throw new Error("No valid page");
|
|
4382
|
+
}
|
|
4383
|
+
let frame = this.#page.mainFrame();
|
|
4384
|
+
const retObj = { frame, locators: [] };
|
|
4385
|
+
if (iframeOptions.length > 0) {
|
|
4386
|
+
frame = await this.#getDescendantFrame(frame, iframeOptions);
|
|
4387
|
+
if (!frame) {
|
|
4388
|
+
return retObj;
|
|
4389
|
+
}
|
|
4390
|
+
retObj.frame = frame;
|
|
4391
|
+
}
|
|
4392
|
+
try {
|
|
4393
|
+
let locators = [];
|
|
4394
|
+
if (selector.startsWith("./") || selector.startsWith("/") || selector.startsWith("..")) {
|
|
4395
|
+
locators = await frame.locator(`xpath=${selector}`).all();
|
|
4396
|
+
} else {
|
|
4397
|
+
if (selector !== ".") {
|
|
4398
|
+
locators = await frame.locator(selector).all();
|
|
4399
|
+
} else {
|
|
4400
|
+
throw new Error("Cannot use selector '.' on page");
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
retObj.locators = locators;
|
|
4404
|
+
return retObj;
|
|
4405
|
+
} catch (err) {
|
|
4406
|
+
loginfo(err);
|
|
4407
|
+
return retObj;
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
#addPageOn() {
|
|
4411
|
+
if (!this.#page) {
|
|
4412
|
+
throw new Error("No valid page");
|
|
4413
|
+
}
|
|
4414
|
+
const page = this.#page;
|
|
4415
|
+
const pageId = this.#pageId;
|
|
4416
|
+
page.on("close", async () => {
|
|
4417
|
+
loginfo(`##browser ${pageId} closed`);
|
|
4418
|
+
if (!page.pageInfo) {
|
|
4419
|
+
logerr(`Logic error in page.on("close")`);
|
|
4420
|
+
}
|
|
4421
|
+
this.emit("pageClose");
|
|
4422
|
+
this.#lsdBrowserContext.emit("pageClose", this);
|
|
4423
|
+
});
|
|
4424
|
+
page.on("popup", (p) => {
|
|
4425
|
+
if (p) {
|
|
4426
|
+
let evtData = null;
|
|
4427
|
+
const pageInfo = p.pageInfo;
|
|
4428
|
+
let popupPageId = "page";
|
|
4429
|
+
if (pageInfo) {
|
|
4430
|
+
const { browserIdx, browserContextIdx, pageIdx } = pageInfo;
|
|
4431
|
+
popupPageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
4432
|
+
pageInfo.openType = "popup";
|
|
4433
|
+
evtData = this.browserContext().page(pageIdx);
|
|
4434
|
+
if (evtData && page.pageInfo?.taskId) {
|
|
4435
|
+
pageInfo.relatedId = page.pageInfo.taskId;
|
|
4436
|
+
}
|
|
4437
|
+
} else {
|
|
4438
|
+
logerr(`##browser ${pageId} has popup without page.pageInfo`);
|
|
4439
|
+
}
|
|
4440
|
+
loginfo(`##browser ${pageId} has popup ${popupPageId}`);
|
|
4441
|
+
this.emit("pagePopup", evtData);
|
|
4442
|
+
} else {
|
|
4443
|
+
logerr(`##browser ${pageId} has popup page with null page`);
|
|
4444
|
+
}
|
|
4445
|
+
});
|
|
4446
|
+
}
|
|
4447
|
+
constructor(browserContext, page, pageInfo) {
|
|
4448
|
+
if (!browserContext.pages || !page?.goto) {
|
|
4449
|
+
throw new Error("Invalid paras in new LsdPage");
|
|
4450
|
+
}
|
|
4451
|
+
super();
|
|
4452
|
+
this.#lsdBrowserContext = browserContext;
|
|
4453
|
+
this.#page = page;
|
|
4454
|
+
this.#status = "free";
|
|
4455
|
+
const currentTime = (0, import_utils11.getCurrentUnixTime)();
|
|
4456
|
+
const { browserIdx = 0, browserContextIdx = 0, pageIdx = 0, openType = "other", openTime = currentTime, lastStatusUpdateTime = currentTime, taskId = 0, relatedId = 0, misc = {} } = pageInfo ? pageInfo : {};
|
|
4457
|
+
this.#page.pageInfo = { browserIdx, browserContextIdx, pageIdx, openType, openTime, lastStatusUpdateTime, taskId, relatedId, misc };
|
|
4458
|
+
this.#pageId = `page-${browserIdx}-${browserContextIdx}-${pageIdx}`;
|
|
4459
|
+
this.#closeWhenFree = false;
|
|
4460
|
+
this.#resquestInterceptionOptions = [];
|
|
4461
|
+
this.#responseInterceptionOptions = [];
|
|
4462
|
+
this.#client = null;
|
|
4463
|
+
this.#responseCb = null;
|
|
4464
|
+
this.#addPageOn();
|
|
4465
|
+
}
|
|
4466
|
+
async addPreloadScript(scriptOrFunc, arg) {
|
|
4467
|
+
if (!this.#page) {
|
|
4468
|
+
throw new Error("No valid page");
|
|
4469
|
+
}
|
|
4470
|
+
if (typeof scriptOrFunc === "string") {
|
|
4471
|
+
await this.#page.addInitScript({ content: scriptOrFunc });
|
|
4472
|
+
} else if (typeof scriptOrFunc === "function") {
|
|
4473
|
+
await this.#page.addInitScript(scriptOrFunc, arg);
|
|
4474
|
+
} else {
|
|
4475
|
+
throw new Error(`Invalid type of scriptOrFunc ${typeof scriptOrFunc}`);
|
|
4476
|
+
}
|
|
4477
|
+
return true;
|
|
4478
|
+
}
|
|
4479
|
+
async addScriptTag(options) {
|
|
4480
|
+
if (!this.#page) {
|
|
4481
|
+
throw new Error("No valid page");
|
|
4482
|
+
}
|
|
4483
|
+
return this.#page.addScriptTag(options);
|
|
4484
|
+
}
|
|
4485
|
+
apiContext() {
|
|
4486
|
+
return this.browserContext().apiContext();
|
|
4487
|
+
}
|
|
4488
|
+
async bringToFront() {
|
|
4489
|
+
if (!this.#page) {
|
|
4490
|
+
throw new Error("No valid page");
|
|
4491
|
+
}
|
|
4492
|
+
await this.#page.bringToFront();
|
|
4493
|
+
return true;
|
|
4494
|
+
}
|
|
4495
|
+
browserContext() {
|
|
4496
|
+
return this.#lsdBrowserContext;
|
|
4497
|
+
}
|
|
4498
|
+
async clearCookies() {
|
|
4499
|
+
if (!this.#page) {
|
|
4500
|
+
throw new Error("No valid page");
|
|
4501
|
+
}
|
|
4502
|
+
return await this.#clearCookies(this.#page);
|
|
4503
|
+
}
|
|
4504
|
+
async clearLocalStorage() {
|
|
4505
|
+
if (!this.#page) {
|
|
4506
|
+
throw new Error("No valid page");
|
|
4507
|
+
}
|
|
4508
|
+
return await this.#clearLocalStorage(this.#page);
|
|
4509
|
+
}
|
|
4510
|
+
async clearRequestInterceptions() {
|
|
4511
|
+
if (!this.#page) {
|
|
4512
|
+
throw new Error("No valid page");
|
|
4513
|
+
}
|
|
4514
|
+
await this.#page.unrouteAll();
|
|
4515
|
+
return true;
|
|
4516
|
+
}
|
|
4517
|
+
async clearResponseInterceptions() {
|
|
4518
|
+
if (!this.#page) {
|
|
4519
|
+
throw new Error("No valid page");
|
|
4520
|
+
}
|
|
4521
|
+
try {
|
|
4522
|
+
if (this.#responseInterceptionOptions.length > 0) {
|
|
4523
|
+
if (this.#responseCb) {
|
|
4524
|
+
this.#page.removeListener("response", this.#responseCb);
|
|
4525
|
+
}
|
|
4526
|
+
this.#responseInterceptionOptions = [];
|
|
4527
|
+
}
|
|
4528
|
+
return true;
|
|
4529
|
+
} catch (err) {
|
|
4530
|
+
logerr(err);
|
|
4531
|
+
return false;
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
async clearStateData() {
|
|
4535
|
+
if (!this.#page) {
|
|
4536
|
+
throw new Error("No valid page");
|
|
4537
|
+
}
|
|
4538
|
+
await this.#clearCookies(this.#page);
|
|
4539
|
+
await this.#clearIndexedDB(this.#page);
|
|
4540
|
+
return await this.#clearLocalStorage(this.#page);
|
|
4541
|
+
}
|
|
4542
|
+
async close() {
|
|
4543
|
+
if (this.#status === "closed") {
|
|
4544
|
+
logwarn(`Page ${this.#pageId} is already closed.`);
|
|
4545
|
+
return true;
|
|
4546
|
+
} else if (this.#status === "busy") {
|
|
4547
|
+
throw new Error(`Page ${this.#pageId} cannot be closed because it is busy.`);
|
|
4548
|
+
}
|
|
4549
|
+
if (!this.#page) {
|
|
4550
|
+
throw new Error("No valid page");
|
|
4551
|
+
}
|
|
4552
|
+
await this.#page.close();
|
|
4553
|
+
this.#page = null;
|
|
4554
|
+
this.#status = "closed";
|
|
4555
|
+
return true;
|
|
4556
|
+
}
|
|
4557
|
+
closeWhenFree() {
|
|
4558
|
+
return this.#closeWhenFree;
|
|
4559
|
+
}
|
|
4560
|
+
async content(iframeOptions = []) {
|
|
4561
|
+
if (!this.#page) {
|
|
4562
|
+
throw new Error("No valid page");
|
|
4563
|
+
}
|
|
4564
|
+
let content = "";
|
|
4565
|
+
if (iframeOptions.length > 0) {
|
|
4566
|
+
const frameLocator = await this.#getDescendantFrame(this.#page.mainFrame(), iframeOptions);
|
|
4567
|
+
if (frameLocator) {
|
|
4568
|
+
content = await frameLocator.locator(":root").evaluate(() => document.documentElement.outerHTML);
|
|
4569
|
+
}
|
|
4570
|
+
} else {
|
|
4571
|
+
content = await this.#page.content();
|
|
4572
|
+
}
|
|
4573
|
+
return content;
|
|
4574
|
+
}
|
|
4575
|
+
async cookies() {
|
|
4576
|
+
if (!this.#page) {
|
|
4577
|
+
throw new Error("No valid page");
|
|
4578
|
+
}
|
|
4579
|
+
return this.#getCookies(this.#page);
|
|
4580
|
+
}
|
|
4581
|
+
async documentHeight() {
|
|
4582
|
+
if (!this.#page) {
|
|
4583
|
+
throw new Error("No valid page");
|
|
4584
|
+
}
|
|
4585
|
+
const height = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
4586
|
+
return height;
|
|
4587
|
+
}
|
|
4588
|
+
async evaluate(func, args) {
|
|
4589
|
+
if (!this.#page) {
|
|
4590
|
+
throw new Error("No valid page");
|
|
4591
|
+
}
|
|
4592
|
+
return this.#page.evaluate(func, args);
|
|
4593
|
+
}
|
|
4594
|
+
async exposeFunction(name, callbackFunction) {
|
|
4595
|
+
if (!this.#page) {
|
|
4596
|
+
throw new Error("No valid page");
|
|
4597
|
+
}
|
|
4598
|
+
await this.#page.exposeFunction(name, callbackFunction);
|
|
4599
|
+
return;
|
|
4600
|
+
}
|
|
4601
|
+
async findElement(selectorOrXpath, iframeOptions = []) {
|
|
4602
|
+
if (!this.#page) {
|
|
4603
|
+
throw new Error("No valid page");
|
|
4604
|
+
}
|
|
4605
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
4606
|
+
if (!Array.isArray(selectors)) {
|
|
4607
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElement`);
|
|
4608
|
+
}
|
|
4609
|
+
for (const selector of selectors) {
|
|
4610
|
+
const { frame, locators } = await this.#findElementHandles(selector, iframeOptions);
|
|
4611
|
+
if (locators.length > 0) {
|
|
4612
|
+
const playwrightElement = new PatchrightElement(locators[0], frame);
|
|
4613
|
+
return playwrightElement;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
return null;
|
|
4617
|
+
}
|
|
4618
|
+
async findElements(selectorOrXpath, iframeOptions = []) {
|
|
4619
|
+
if (!this.#page) {
|
|
4620
|
+
throw new Error("No valid page");
|
|
4621
|
+
}
|
|
4622
|
+
const selectors = typeof selectorOrXpath === "string" ? [selectorOrXpath] : selectorOrXpath;
|
|
4623
|
+
if (!Array.isArray(selectors)) {
|
|
4624
|
+
throw new Error(`Invalid selectorOrXpath ${selectorOrXpath} in findElements`);
|
|
4625
|
+
}
|
|
4626
|
+
for (const selector of selectors) {
|
|
4627
|
+
const { frame, locators } = await this.#findElementHandles(selector, iframeOptions);
|
|
4628
|
+
if (locators.length > 0) {
|
|
4629
|
+
const playwrightElements = locators.map((locator) => new PatchrightElement(locator, frame));
|
|
4630
|
+
return playwrightElements;
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
return [];
|
|
4634
|
+
}
|
|
4635
|
+
async free() {
|
|
4636
|
+
if (this.#status === "free") {
|
|
4637
|
+
logwarn(`Page ${this.#pageId} is already free.`);
|
|
4638
|
+
}
|
|
4639
|
+
this.#status = "free";
|
|
4640
|
+
await this.clearRequestInterceptions();
|
|
4641
|
+
await this.clearResponseInterceptions();
|
|
4642
|
+
return true;
|
|
4643
|
+
}
|
|
4644
|
+
#getWaitUntil(origWaitUntil) {
|
|
4645
|
+
if (origWaitUntil === "networkidle0" || origWaitUntil === "networkidle2") {
|
|
4646
|
+
return "networkidle";
|
|
4647
|
+
} else {
|
|
4648
|
+
return origWaitUntil;
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
async goto(url, options) {
|
|
4652
|
+
if (!this.#page) {
|
|
4653
|
+
throw new Error("No valid page");
|
|
4654
|
+
}
|
|
4655
|
+
if (options) {
|
|
4656
|
+
const { referer, timeout, waitUntil = "load" } = options;
|
|
4657
|
+
const newOptions = {};
|
|
4658
|
+
if (referer) {
|
|
4659
|
+
newOptions.referer = referer;
|
|
4660
|
+
}
|
|
4661
|
+
if (timeout) {
|
|
4662
|
+
newOptions.timeout = timeout;
|
|
4663
|
+
}
|
|
4664
|
+
newOptions.waitUntil = this.#getWaitUntil(waitUntil);
|
|
4665
|
+
await this.#page.goto(url, newOptions);
|
|
4666
|
+
} else {
|
|
4667
|
+
await this.#page.goto(url);
|
|
4668
|
+
}
|
|
4669
|
+
return true;
|
|
4670
|
+
}
|
|
4671
|
+
id() {
|
|
4672
|
+
return this.#pageId;
|
|
4673
|
+
}
|
|
4674
|
+
isFree() {
|
|
4675
|
+
return this.#status === "free";
|
|
4676
|
+
}
|
|
4677
|
+
async localStroage() {
|
|
4678
|
+
if (!this.#page) {
|
|
4679
|
+
throw new Error("No valid page");
|
|
4680
|
+
}
|
|
4681
|
+
return this.#getLocalStorage(this.#page);
|
|
4682
|
+
}
|
|
4683
|
+
load() {
|
|
4684
|
+
throw new Error("Not supported in PatchrightPage.");
|
|
4685
|
+
}
|
|
4686
|
+
mainFrame() {
|
|
4687
|
+
if (!this.#page) {
|
|
4688
|
+
throw new Error("No valid page");
|
|
4689
|
+
}
|
|
4690
|
+
return this.#page.mainFrame();
|
|
4691
|
+
}
|
|
4692
|
+
async maximizeViewport() {
|
|
4693
|
+
const height = await this.pageHeight();
|
|
4694
|
+
const width = await this.pageWidth();
|
|
4695
|
+
return await this.setViewportSize({ height, width });
|
|
4696
|
+
}
|
|
4697
|
+
async pageHeight() {
|
|
4698
|
+
if (!this.#page) {
|
|
4699
|
+
throw new Error("No valid page");
|
|
4700
|
+
}
|
|
4701
|
+
const bodyHeight = await this.#page.evaluate(() => document.body.scrollHeight);
|
|
4702
|
+
const documentHeight = await this.#page.evaluate(() => document.documentElement.scrollHeight);
|
|
4703
|
+
const windowHeight = await this.#page.evaluate(() => window.outerHeight);
|
|
4704
|
+
const pageHeight = Math.max(bodyHeight, documentHeight, windowHeight);
|
|
4705
|
+
return pageHeight;
|
|
4706
|
+
}
|
|
4707
|
+
pageInfo() {
|
|
4708
|
+
if (!this.#page) {
|
|
4709
|
+
throw new Error("No valid page");
|
|
4710
|
+
}
|
|
4711
|
+
return Object.assign({}, this.#page.pageInfo);
|
|
4712
|
+
}
|
|
4713
|
+
async pageWidth() {
|
|
4714
|
+
if (!this.#page) {
|
|
4715
|
+
throw new Error("No valid page");
|
|
4716
|
+
}
|
|
4717
|
+
const offsetWidth = await this.#page.evaluate(() => document.documentElement.offsetWidth);
|
|
4718
|
+
const windowWidth = await this.#page.evaluate(() => window.outerWidth);
|
|
4719
|
+
const pageWidth = Math.max(offsetWidth, windowWidth);
|
|
4720
|
+
return pageWidth;
|
|
4721
|
+
}
|
|
4722
|
+
async pdf(options) {
|
|
4723
|
+
if (!this.#page) {
|
|
4724
|
+
throw new Error("No valid page");
|
|
4725
|
+
}
|
|
4726
|
+
const buffer = await this.#page.pdf(options);
|
|
4727
|
+
return buffer;
|
|
4728
|
+
}
|
|
4729
|
+
async screenshot(options) {
|
|
4730
|
+
if (!this.#page) {
|
|
4731
|
+
throw new Error("No valid page");
|
|
4732
|
+
}
|
|
4733
|
+
return await this.#page.screenshot(options);
|
|
4734
|
+
}
|
|
4735
|
+
async scrollBy(x, y) {
|
|
4736
|
+
if (!this.#page) {
|
|
4737
|
+
throw new Error("No valid page");
|
|
4738
|
+
}
|
|
4739
|
+
await this.#page.evaluate(
|
|
4740
|
+
([x2, y2]) => {
|
|
4741
|
+
window.scrollBy(x2, y2);
|
|
4742
|
+
},
|
|
4743
|
+
[x, y]
|
|
4744
|
+
);
|
|
4745
|
+
return true;
|
|
4746
|
+
}
|
|
4747
|
+
async scrollTo(x, y) {
|
|
4748
|
+
if (!this.#page) {
|
|
4749
|
+
throw new Error("No valid page");
|
|
4750
|
+
}
|
|
4751
|
+
await this.#page.evaluate(
|
|
4752
|
+
([x2, y2]) => {
|
|
4753
|
+
window.scrollTo(x2, y2);
|
|
4754
|
+
},
|
|
4755
|
+
[x, y]
|
|
4756
|
+
);
|
|
4757
|
+
return true;
|
|
4758
|
+
}
|
|
4759
|
+
async sendCDPMessage(method, params = null, detach = true) {
|
|
4760
|
+
if (!this.#client) {
|
|
4761
|
+
const origContext = this.browserContext()._origBrowserContext();
|
|
4762
|
+
if (!origContext) {
|
|
4763
|
+
throw new Error(`Invalid playwright browserContext`);
|
|
4764
|
+
}
|
|
4765
|
+
this.#client = await origContext.newCDPSession(this.#page);
|
|
4766
|
+
}
|
|
4767
|
+
if (!this.#client) {
|
|
4768
|
+
throw new Error("No valid CDP session to send message");
|
|
4769
|
+
}
|
|
4770
|
+
const response = params ? await this.#client.send(method, params) : await this.#client.send(method);
|
|
4771
|
+
if (detach) {
|
|
4772
|
+
await this.#client.detach();
|
|
4773
|
+
this.#client = null;
|
|
4774
|
+
}
|
|
4775
|
+
return response;
|
|
4776
|
+
}
|
|
4777
|
+
setCloseWhenFree(closeWhenFree) {
|
|
4778
|
+
this.#closeWhenFree = closeWhenFree;
|
|
4779
|
+
return true;
|
|
4780
|
+
}
|
|
4781
|
+
async setCookies(cookies) {
|
|
4782
|
+
if (!this.#page) {
|
|
4783
|
+
throw new Error("No valid page");
|
|
4784
|
+
}
|
|
4785
|
+
return await this.#setCookies(this.#page, cookies);
|
|
4786
|
+
}
|
|
4787
|
+
async setExtraHTTPHeaders(headers) {
|
|
4788
|
+
if (!this.#page) {
|
|
4789
|
+
throw new Error("No valid page");
|
|
4790
|
+
}
|
|
4791
|
+
await this.#page.setExtraHTTPHeaders(headers);
|
|
4792
|
+
return true;
|
|
4793
|
+
}
|
|
4794
|
+
async setLocalStroage(localStorageItems) {
|
|
4795
|
+
if (!this.#page) {
|
|
4796
|
+
throw new Error("No valid page");
|
|
4797
|
+
}
|
|
4798
|
+
return await this.#setLocalStorage(this.#page, localStorageItems);
|
|
4799
|
+
}
|
|
4800
|
+
setPageInfo(pageInfo) {
|
|
4801
|
+
if (!this.#page?.pageInfo) {
|
|
4802
|
+
throw new Error("No valid page or pageInfo");
|
|
4803
|
+
}
|
|
4804
|
+
if (!pageInfo) {
|
|
4805
|
+
throw new Error("Invalid paras in setPageInfo");
|
|
4806
|
+
}
|
|
4807
|
+
const actPageInfo = this.#page.pageInfo;
|
|
4808
|
+
const { lastStatusUpdateTime, taskId, relatedId, misc } = pageInfo;
|
|
4809
|
+
if (typeof lastStatusUpdateTime === "number") {
|
|
4810
|
+
actPageInfo.lastStatusUpdateTime = lastStatusUpdateTime;
|
|
4811
|
+
}
|
|
4812
|
+
if (typeof taskId === "number") {
|
|
4813
|
+
actPageInfo.taskId = taskId;
|
|
4814
|
+
}
|
|
4815
|
+
if (typeof relatedId === "number") {
|
|
4816
|
+
actPageInfo.relatedId = relatedId;
|
|
4817
|
+
}
|
|
4818
|
+
if (misc && typeof misc === "object") {
|
|
4819
|
+
for (const key of Object.keys(misc)) {
|
|
4820
|
+
actPageInfo.misc[key] = misc[key];
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
return true;
|
|
4824
|
+
}
|
|
4825
|
+
#checkRequestMatch(request, requestMatch) {
|
|
4826
|
+
try {
|
|
4827
|
+
if (!request) {
|
|
4828
|
+
return false;
|
|
4829
|
+
}
|
|
4830
|
+
const { methods, postData, resourceTypes, url } = requestMatch;
|
|
4831
|
+
if (methods && !methods.includes(request.method().toUpperCase())) {
|
|
4832
|
+
return false;
|
|
4833
|
+
}
|
|
4834
|
+
if (resourceTypes && !resourceTypes.includes(request.resourceType())) {
|
|
4835
|
+
return false;
|
|
4836
|
+
}
|
|
4837
|
+
if (url && !request.url().match(url)) {
|
|
4838
|
+
return false;
|
|
4839
|
+
}
|
|
4840
|
+
const origData = request.postData();
|
|
4841
|
+
const data = origData ? origData : "";
|
|
4842
|
+
if (postData && !data.match(postData)) {
|
|
4843
|
+
return false;
|
|
4844
|
+
}
|
|
4845
|
+
return true;
|
|
4846
|
+
} catch (err) {
|
|
4847
|
+
logerr(err);
|
|
4848
|
+
return false;
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
async setRequestInterception(options) {
|
|
4852
|
+
if (!this.#page) {
|
|
4853
|
+
throw new Error("No valid page");
|
|
4854
|
+
}
|
|
4855
|
+
const actOptions = Array.isArray(options) ? options : [options];
|
|
4856
|
+
if (actOptions.length <= 0) {
|
|
4857
|
+
logwarn("Invalid paras in setRequestInterception");
|
|
4858
|
+
return false;
|
|
4859
|
+
}
|
|
4860
|
+
const firstRequestInterception = this.#resquestInterceptionOptions.length <= 0;
|
|
4861
|
+
for (const option of actOptions) {
|
|
4862
|
+
switch (option.action) {
|
|
4863
|
+
case "abort":
|
|
4864
|
+
case "fulfill":
|
|
4865
|
+
this.#resquestInterceptionOptions.push(option);
|
|
4866
|
+
break;
|
|
4867
|
+
default:
|
|
4868
|
+
(0, import_utils11.unreachable)(option.action);
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
if (firstRequestInterception && this.#resquestInterceptionOptions.length > 0) {
|
|
4872
|
+
this.#page.route("**", async (route) => {
|
|
4873
|
+
try {
|
|
4874
|
+
for (const option of actOptions) {
|
|
4875
|
+
const { requestMatch, action, fulfill } = option;
|
|
4876
|
+
const request = route.request();
|
|
4877
|
+
const matchedFlag = !requestMatch || this.#checkRequestMatch(request, requestMatch);
|
|
4878
|
+
if (matchedFlag) {
|
|
4879
|
+
switch (action) {
|
|
4880
|
+
case "abort":
|
|
4881
|
+
await route.abort();
|
|
4882
|
+
break;
|
|
4883
|
+
case "fulfill":
|
|
4884
|
+
const body = fulfill ? fulfill : `<html><body><h1>${request.url()}</h1></body></html>`;
|
|
4885
|
+
route.fulfill({
|
|
4886
|
+
status: 200,
|
|
4887
|
+
// contentType: "text/html; charset=utf-8", // "text/plain",
|
|
4888
|
+
body
|
|
4889
|
+
});
|
|
4890
|
+
break;
|
|
4891
|
+
default:
|
|
4892
|
+
(0, import_utils11.unreachable)(action);
|
|
4893
|
+
}
|
|
4894
|
+
return true;
|
|
4895
|
+
} else {
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
await route.continue();
|
|
4899
|
+
return true;
|
|
4900
|
+
} catch (err) {
|
|
4901
|
+
logerr(err);
|
|
4902
|
+
return false;
|
|
4903
|
+
}
|
|
4904
|
+
});
|
|
4905
|
+
}
|
|
4906
|
+
return true;
|
|
4907
|
+
}
|
|
4908
|
+
async #responseListener(response) {
|
|
4909
|
+
try {
|
|
4910
|
+
const pageUrl = this.#page ? this.#page.url() : "";
|
|
4911
|
+
if (!response.ok()) {
|
|
4912
|
+
return;
|
|
4913
|
+
}
|
|
4914
|
+
const request = response.request();
|
|
4915
|
+
if (!request) {
|
|
4916
|
+
return;
|
|
4917
|
+
}
|
|
4918
|
+
for (const option of this.#responseInterceptionOptions) {
|
|
4919
|
+
const { requestMatch, responseMatch, responseItems, handler, handlerOptions = {} } = option;
|
|
4920
|
+
let matchedFlag = !requestMatch || this.#checkRequestMatch(request, requestMatch);
|
|
4921
|
+
if (matchedFlag && responseMatch) {
|
|
4922
|
+
const { minLength, maxLength } = responseMatch;
|
|
4923
|
+
const text = await response.text();
|
|
4924
|
+
const len = text.length;
|
|
4925
|
+
if (minLength && minLength > 0 && len < minLength || maxLength && maxLength > 0 && len > maxLength) {
|
|
4926
|
+
matchedFlag = false;
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
if (!matchedFlag) {
|
|
4930
|
+
continue;
|
|
4931
|
+
}
|
|
4932
|
+
if (Array.isArray(responseItems)) {
|
|
4933
|
+
const requestMethod = request.method();
|
|
4934
|
+
const requestUrl = request.url();
|
|
4935
|
+
const reqData2 = request.postData();
|
|
4936
|
+
const requestData = reqData2 ? reqData2 : "";
|
|
4937
|
+
const responseData = await response.text();
|
|
4938
|
+
responseItems.push({
|
|
4939
|
+
pageUrl,
|
|
4940
|
+
requestMethod,
|
|
4941
|
+
requestUrl,
|
|
4942
|
+
requestData,
|
|
4943
|
+
responseData
|
|
4944
|
+
});
|
|
4945
|
+
loginfo(`##browser cache matched response: ${requestUrl}`);
|
|
4946
|
+
}
|
|
4947
|
+
if (typeof handler === "function") {
|
|
4948
|
+
const pageData = { pageUrl, cookies: "" };
|
|
4949
|
+
await handler(response, handlerOptions, pageData);
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
return;
|
|
4953
|
+
} catch (err) {
|
|
4954
|
+
logerr(err);
|
|
4955
|
+
return;
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
async setResponseInterception(options) {
|
|
4959
|
+
if (!this.#page) {
|
|
4960
|
+
throw new Error("No valid page");
|
|
4961
|
+
}
|
|
4962
|
+
const actOptions = Array.isArray(options) ? options : [options];
|
|
4963
|
+
if (actOptions.length <= 0) {
|
|
4964
|
+
logwarn("Invalid paras in setResponseInterception");
|
|
4965
|
+
return false;
|
|
4966
|
+
}
|
|
4967
|
+
const firstResponseInterception = this.#responseInterceptionOptions.length <= 0;
|
|
4968
|
+
for (const option of actOptions) {
|
|
4969
|
+
if (option?.responseItems || option?.handler) {
|
|
4970
|
+
this.#responseInterceptionOptions.push(option);
|
|
4971
|
+
} else {
|
|
4972
|
+
throw new Error(`Invalid ResponseInterceptionOption`);
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
if (firstResponseInterception && this.#responseInterceptionOptions.length > 0) {
|
|
4976
|
+
this.#responseCb = this.#responseListener.bind(this);
|
|
4977
|
+
this.#page.on("response", this.#responseCb);
|
|
4978
|
+
}
|
|
4979
|
+
return true;
|
|
4980
|
+
}
|
|
4981
|
+
async setStateData(stateData) {
|
|
4982
|
+
return await this.#lsdBrowserContext.setStateData(stateData);
|
|
4983
|
+
}
|
|
4984
|
+
async setUserAgent(userAgent) {
|
|
4985
|
+
if (userAgent) {
|
|
4986
|
+
throw new Error(`Patchright does not support page.setUserAgent by now`);
|
|
4987
|
+
}
|
|
4988
|
+
return false;
|
|
4989
|
+
}
|
|
4990
|
+
async setViewportSize(viewPortSize) {
|
|
4991
|
+
if (!this.#page) {
|
|
4992
|
+
throw new Error("No valid page");
|
|
4993
|
+
}
|
|
4994
|
+
await this.#page.setViewportSize(viewPortSize);
|
|
4995
|
+
return true;
|
|
4996
|
+
}
|
|
4997
|
+
async stateData() {
|
|
4998
|
+
if (!this.#page) {
|
|
4999
|
+
throw new Error("No valid page");
|
|
5000
|
+
}
|
|
5001
|
+
const cookies = await this.#getCookies(this.#page);
|
|
5002
|
+
const localStorage = await this.#getLocalStorage(this.#page);
|
|
5003
|
+
return { cookies, localStorage };
|
|
5004
|
+
}
|
|
5005
|
+
status() {
|
|
5006
|
+
return this.#status;
|
|
5007
|
+
}
|
|
5008
|
+
async title() {
|
|
5009
|
+
if (!this.#page) {
|
|
5010
|
+
throw new Error("No valid page");
|
|
5011
|
+
}
|
|
5012
|
+
return await this.#page.title();
|
|
5013
|
+
}
|
|
5014
|
+
url() {
|
|
5015
|
+
if (!this.#page) {
|
|
5016
|
+
throw new Error("No valid page");
|
|
5017
|
+
}
|
|
5018
|
+
return this.#page.url();
|
|
5019
|
+
}
|
|
5020
|
+
use() {
|
|
5021
|
+
if (this.#status === "busy") {
|
|
5022
|
+
throw new Error(`Page ${this.#pageId} is already busy!!!`);
|
|
5023
|
+
}
|
|
5024
|
+
this.#status = "busy";
|
|
5025
|
+
return true;
|
|
5026
|
+
}
|
|
5027
|
+
async waitForElement(selector, options = {}) {
|
|
5028
|
+
if (!this.#page) {
|
|
5029
|
+
throw new Error("No valid page");
|
|
5030
|
+
}
|
|
5031
|
+
const locator = this.#page.locator(selector);
|
|
5032
|
+
const { timeout = 3e4, state = "visible" } = options;
|
|
5033
|
+
await locator.waitFor({ state, timeout });
|
|
5034
|
+
return true;
|
|
5035
|
+
}
|
|
5036
|
+
async waitForNavigation(options) {
|
|
5037
|
+
if (!this.#page) {
|
|
5038
|
+
throw new Error("No valid page");
|
|
5039
|
+
}
|
|
5040
|
+
const { url = "", timeout = 3e4, waitUntil = "load" } = options;
|
|
5041
|
+
const newWaitUntil = this.#getWaitUntil(waitUntil);
|
|
5042
|
+
if (url) {
|
|
5043
|
+
await this.#page.waitForURL(url, { timeout, waitUntil: newWaitUntil });
|
|
5044
|
+
} else if (newWaitUntil === "commit") {
|
|
5045
|
+
throw new Error("commit is not supported in PatchrightPage.waitForNavigation");
|
|
5046
|
+
} else {
|
|
5047
|
+
await this.#page.waitForLoadState(newWaitUntil, { timeout });
|
|
5048
|
+
}
|
|
5049
|
+
return true;
|
|
5050
|
+
}
|
|
5051
|
+
async windowMember(keys) {
|
|
5052
|
+
if (!this.#page) {
|
|
5053
|
+
throw new Error("No valid page");
|
|
5054
|
+
}
|
|
5055
|
+
if (!this.#page || !Array.isArray(keys) || keys.length <= 0 || keys.length > 20) {
|
|
5056
|
+
return "";
|
|
5057
|
+
}
|
|
5058
|
+
const content = await this.#page.evaluate(
|
|
5059
|
+
(keys2) => {
|
|
5060
|
+
let retObj = window;
|
|
5061
|
+
for (const key of keys2) {
|
|
5062
|
+
if (!key) {
|
|
5063
|
+
break;
|
|
5064
|
+
} else if (typeof retObj !== "object" || !retObj) {
|
|
5065
|
+
return "";
|
|
5066
|
+
} else {
|
|
5067
|
+
retObj = retObj[key];
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
if (typeof retObj === "string") {
|
|
5071
|
+
return retObj;
|
|
5072
|
+
} else if (typeof retObj === "number") {
|
|
5073
|
+
return String(retObj);
|
|
5074
|
+
} else if (typeof retObj === "boolean") {
|
|
5075
|
+
return String(Number(retObj));
|
|
5076
|
+
} else if (!retObj) {
|
|
5077
|
+
return "";
|
|
5078
|
+
} else if (typeof retObj === "object") {
|
|
5079
|
+
try {
|
|
5080
|
+
return JSON.stringify(retObj);
|
|
5081
|
+
} catch (err) {
|
|
5082
|
+
return "";
|
|
5083
|
+
}
|
|
5084
|
+
} else if (typeof retObj === "bigint") {
|
|
5085
|
+
return String(retObj);
|
|
5086
|
+
} else {
|
|
5087
|
+
return "";
|
|
5088
|
+
}
|
|
5089
|
+
},
|
|
5090
|
+
keys
|
|
5091
|
+
);
|
|
5092
|
+
return content;
|
|
5093
|
+
}
|
|
5094
|
+
_origPage() {
|
|
5095
|
+
return this.#page;
|
|
5096
|
+
}
|
|
5097
|
+
};
|
|
5098
|
+
|
|
5099
|
+
// src/patchright/api.ts
|
|
5100
|
+
var PatchrightApiContext = class {
|
|
5101
|
+
#apiRequestContext;
|
|
5102
|
+
#status;
|
|
5103
|
+
constructor(apiRequestContext) {
|
|
5104
|
+
this.#apiRequestContext = apiRequestContext;
|
|
5105
|
+
this.#status = "normal";
|
|
5106
|
+
}
|
|
5107
|
+
async fetch(url, options = {}) {
|
|
5108
|
+
if (this.#status !== "normal") {
|
|
5109
|
+
throw new Error(`ApiContext has already been destroyed`);
|
|
5110
|
+
}
|
|
5111
|
+
const apiResponse = await this.#apiRequestContext.fetch(url, options);
|
|
5112
|
+
const headers = apiResponse.headers();
|
|
5113
|
+
const status = apiResponse.status();
|
|
5114
|
+
const statusText = apiResponse.statusText();
|
|
5115
|
+
const text = await apiResponse.text();
|
|
5116
|
+
const responseUrl = apiResponse.url();
|
|
5117
|
+
return { headers, status, statusText, text, url: responseUrl };
|
|
5118
|
+
}
|
|
5119
|
+
async stateData() {
|
|
5120
|
+
if (this.#status !== "normal") {
|
|
5121
|
+
throw new Error(`ApiContext has already been destroyed`);
|
|
5122
|
+
}
|
|
5123
|
+
const storageState = await this.#apiRequestContext.storageState();
|
|
5124
|
+
const { cookies, origins: localStorage } = storageState;
|
|
5125
|
+
return { cookies, localStorage };
|
|
5126
|
+
}
|
|
5127
|
+
async destroy() {
|
|
5128
|
+
await this.#apiRequestContext.dispose();
|
|
5129
|
+
this.#status = "destroyed";
|
|
5130
|
+
return true;
|
|
5131
|
+
}
|
|
5132
|
+
};
|
|
5133
|
+
|
|
5134
|
+
// src/patchright/context.ts
|
|
5135
|
+
var PatchrightBrowserContext = class extends import_node_events9.default {
|
|
5136
|
+
#lsdBrowser;
|
|
5137
|
+
#browserIdx;
|
|
5138
|
+
#browserContextIdx;
|
|
5139
|
+
#browserContext;
|
|
5140
|
+
#browserContextCreationMethod;
|
|
5141
|
+
#apiContext;
|
|
5142
|
+
#createTime;
|
|
5143
|
+
#lastStatusUpdateTime;
|
|
5144
|
+
#status;
|
|
5145
|
+
#incognito;
|
|
5146
|
+
#proxy;
|
|
5147
|
+
#maxPagesPerBrowserContext;
|
|
5148
|
+
#maxPageFreeSeconds;
|
|
5149
|
+
#maxViewportOfNewPage;
|
|
5150
|
+
#lsdPages;
|
|
5151
|
+
#nextPageIdx;
|
|
5152
|
+
#gettingPage;
|
|
5153
|
+
async #initPages() {
|
|
5154
|
+
if (!this.#browserContext) {
|
|
5155
|
+
throw new Error("Invalid browserContext");
|
|
5156
|
+
}
|
|
5157
|
+
const pages = this.#browserContext.pages();
|
|
5158
|
+
const openType = this.#lsdBrowser.browserCreationMethod();
|
|
5159
|
+
const lastStatusUpdateTime = (0, import_utils12.getCurrentUnixTime)();
|
|
5160
|
+
for (const page of pages) {
|
|
5161
|
+
const pageInfo = { browserIdx: this.#browserIdx, browserContextIdx: this.#browserContextIdx, pageIdx: this.#nextPageIdx++, openType, openTime: this.#createTime, lastStatusUpdateTime, taskId: 0, relatedId: 0, misc: {} };
|
|
5162
|
+
const lsdPage = new PatchrightPage(this, page, pageInfo);
|
|
5163
|
+
if (this.#maxViewportOfNewPage) {
|
|
5164
|
+
await lsdPage.maximizeViewport();
|
|
5165
|
+
}
|
|
5166
|
+
this.#lsdPages.push(lsdPage);
|
|
5167
|
+
loginfo(`##browser ${lsdPage.id()} ${openType}ed`);
|
|
5168
|
+
}
|
|
5169
|
+
}
|
|
5170
|
+
constructor(lsdBrowser, browserContext, browserContextCreationMethod, incognito = false, proxy = null, browserIdx = 0, browserContextIdx = 0, maxPagesPerBrowserContext = 20, maxPageFreeSeconds = 0, maxViewportOfNewPage = true) {
|
|
5171
|
+
if (!lsdBrowser || typeof lsdBrowser.browserContexts !== "function") {
|
|
5172
|
+
throw new Error(`Invalid lsdBrowser parameter`);
|
|
5173
|
+
}
|
|
5174
|
+
if (!browserContext || typeof browserContext.setOffline !== "function") {
|
|
5175
|
+
throw new Error(`Invalid playwright browserContext parameter`);
|
|
5176
|
+
}
|
|
5177
|
+
super();
|
|
5178
|
+
this.#lsdBrowser = lsdBrowser;
|
|
5179
|
+
this.#browserIdx = browserIdx;
|
|
5180
|
+
this.#browserContextIdx = browserContextIdx;
|
|
5181
|
+
this.#browserContext = browserContext;
|
|
5182
|
+
this.#browserContextCreationMethod = browserContextCreationMethod;
|
|
5183
|
+
const apiRequestContext = browserContext.request;
|
|
5184
|
+
this.#apiContext = new PatchrightApiContext(apiRequestContext);
|
|
5185
|
+
const currentTime = (0, import_utils12.getCurrentUnixTime)();
|
|
5186
|
+
this.#createTime = currentTime;
|
|
5187
|
+
this.#lastStatusUpdateTime = currentTime;
|
|
5188
|
+
this.#status = "free";
|
|
5189
|
+
this.#incognito = incognito === false ? false : true;
|
|
5190
|
+
this.#proxy = proxy?.proxyUrl ? proxy : null;
|
|
5191
|
+
this.#maxPagesPerBrowserContext = maxPagesPerBrowserContext;
|
|
5192
|
+
this.#maxPageFreeSeconds = maxPageFreeSeconds;
|
|
5193
|
+
this.#maxViewportOfNewPage = maxViewportOfNewPage;
|
|
5194
|
+
this.#lsdPages = [];
|
|
5195
|
+
this.#nextPageIdx = 1;
|
|
5196
|
+
this.#gettingPage = false;
|
|
5197
|
+
this.#initPages();
|
|
5198
|
+
browserContext.on("page", async (page) => {
|
|
5199
|
+
const pageInfo = page.pageInfo;
|
|
5200
|
+
if (pageInfo) {
|
|
5201
|
+
const { browserIdx: browserIdx2, browserContextIdx: browserContextIdx2, pageIdx } = pageInfo;
|
|
5202
|
+
logwarn(`##browser page-${browserIdx2}-${browserContextIdx2}-${pageIdx} has been already created`);
|
|
5203
|
+
} else {
|
|
5204
|
+
const currentTime2 = (0, import_utils12.getCurrentUnixTime)();
|
|
5205
|
+
const pageInfo2 = { browserIdx: this.#browserIdx, browserContextIdx: this.#browserContextIdx, pageIdx: this.#nextPageIdx++, openType: "other", openTime: currentTime2, lastStatusUpdateTime: currentTime2, taskId: 0, relatedId: 0, misc: {} };
|
|
5206
|
+
const lsdPage = new PatchrightPage(this, page, pageInfo2);
|
|
5207
|
+
if (this.#maxViewportOfNewPage) {
|
|
5208
|
+
await lsdPage.maximizeViewport();
|
|
5209
|
+
}
|
|
5210
|
+
this.#lsdPages.push(lsdPage);
|
|
5211
|
+
loginfo(`##page ${lsdPage.id()} created`);
|
|
5212
|
+
}
|
|
5213
|
+
});
|
|
5214
|
+
browserContext.on("close", (bc) => {
|
|
5215
|
+
if (browserContext !== bc) {
|
|
5216
|
+
logerr(`##browser different browserContext in browserContext.on("close")`);
|
|
5217
|
+
}
|
|
5218
|
+
this.#lsdBrowser.emit("browserContextClose", this);
|
|
5219
|
+
});
|
|
5220
|
+
this.on("pageClose", (lsdPage) => {
|
|
5221
|
+
if (!(lsdPage instanceof PatchrightPage)) {
|
|
5222
|
+
logerr(`Invalid data in LsdBrowserContext.on("pageClose)`);
|
|
5223
|
+
return;
|
|
5224
|
+
}
|
|
5225
|
+
const idx = this.#lsdPages.findIndex((p) => p === lsdPage);
|
|
5226
|
+
if (idx < 0) {
|
|
5227
|
+
logerr(`Invalid lsdPage in LsdBrowserContext.on("pageClose)`);
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
this.#lsdPages.splice(idx, 1);
|
|
5231
|
+
return;
|
|
5232
|
+
});
|
|
5233
|
+
}
|
|
5234
|
+
apiContext() {
|
|
5235
|
+
return this.#apiContext;
|
|
5236
|
+
}
|
|
5237
|
+
browser() {
|
|
5238
|
+
return this.#lsdBrowser;
|
|
5239
|
+
}
|
|
5240
|
+
async close() {
|
|
5241
|
+
if (this.#browserContext) {
|
|
5242
|
+
this.#status = "closed";
|
|
5243
|
+
this.#lastStatusUpdateTime = (0, import_utils12.getCurrentUnixTime)();
|
|
5244
|
+
loginfo(`browserContext ${this.id()} closed at ${this.#lastStatusUpdateTime}`);
|
|
5245
|
+
await this.#browserContext.close();
|
|
5246
|
+
}
|
|
5247
|
+
return true;
|
|
5248
|
+
}
|
|
5249
|
+
async #tryToGetGettingLock() {
|
|
5250
|
+
let i = 0;
|
|
5251
|
+
for (i = 0; i < 50; i++) {
|
|
5252
|
+
if (!this.#gettingPage) {
|
|
5253
|
+
this.#gettingPage = true;
|
|
5254
|
+
return true;
|
|
5255
|
+
} else {
|
|
5256
|
+
await (0, import_utils12.sleep)(200);
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
logwarn(`Cannot get the gettingLock.`);
|
|
5260
|
+
return false;
|
|
5261
|
+
}
|
|
5262
|
+
#freeGettingLock() {
|
|
5263
|
+
if (!this.#gettingPage) {
|
|
5264
|
+
logwarn(`Getting lock is already free now.`);
|
|
5265
|
+
}
|
|
5266
|
+
this.#gettingPage = false;
|
|
5267
|
+
}
|
|
5268
|
+
async closeFreePages(maxPageFreeSeconds = 0) {
|
|
5269
|
+
if (maxPageFreeSeconds <= 0) {
|
|
5270
|
+
maxPageFreeSeconds = this.#maxPageFreeSeconds;
|
|
5271
|
+
}
|
|
5272
|
+
if (maxPageFreeSeconds <= 0) {
|
|
5273
|
+
logwarn(`Please set valid maxPageFreeSeconds to close free pages`);
|
|
5274
|
+
return false;
|
|
5275
|
+
}
|
|
5276
|
+
const gotLock = await this.#tryToGetGettingLock();
|
|
5277
|
+
if (!gotLock) {
|
|
5278
|
+
return false;
|
|
5279
|
+
}
|
|
5280
|
+
try {
|
|
5281
|
+
const maxUpdateTime = (0, import_utils12.getCurrentUnixTime)() - this.#maxPageFreeSeconds;
|
|
5282
|
+
let freePages = this.#lsdPages.filter((p) => p.isFree() && p.pageInfo().lastStatusUpdateTime < maxUpdateTime);
|
|
5283
|
+
if (freePages.length === this.#lsdPages.length) {
|
|
5284
|
+
freePages = freePages.slice(1);
|
|
5285
|
+
}
|
|
5286
|
+
for (const lsdPage of freePages) {
|
|
5287
|
+
await lsdPage.close();
|
|
5288
|
+
}
|
|
5289
|
+
this.#freeGettingLock();
|
|
5290
|
+
return true;
|
|
5291
|
+
} catch (err) {
|
|
5292
|
+
logerr(err);
|
|
5293
|
+
this.#freeGettingLock();
|
|
5294
|
+
return false;
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
creationMethod() {
|
|
5298
|
+
return this.#browserContextCreationMethod;
|
|
5299
|
+
}
|
|
5300
|
+
doesMeetBrowserContextRequirements(browserContextRequirements) {
|
|
5301
|
+
if (!this.#lsdBrowser.doesMeetBrowserContextRequirements(browserContextRequirements)) {
|
|
5302
|
+
return false;
|
|
5303
|
+
}
|
|
5304
|
+
const { browserIncognitos: incognitos } = browserContextRequirements;
|
|
5305
|
+
return incognitos.length === 0 || incognitos.includes(this.#incognito);
|
|
5306
|
+
}
|
|
5307
|
+
async getPage(always = false) {
|
|
5308
|
+
if (!this.#browserContext) {
|
|
5309
|
+
throw new Error("Invalid browserContext");
|
|
5310
|
+
}
|
|
5311
|
+
const gotLock = await this.#tryToGetGettingLock();
|
|
5312
|
+
if (!gotLock) {
|
|
5313
|
+
return null;
|
|
5314
|
+
}
|
|
5315
|
+
try {
|
|
5316
|
+
let lsdPage = this.#lsdPages.find((p) => p.isFree());
|
|
5317
|
+
if (lsdPage) {
|
|
5318
|
+
lsdPage.use();
|
|
5319
|
+
this.#freeGettingLock();
|
|
5320
|
+
return lsdPage;
|
|
5321
|
+
}
|
|
5322
|
+
if (this.#lsdPages.length >= this.#maxPagesPerBrowserContext && !always) {
|
|
5323
|
+
this.#freeGettingLock();
|
|
5324
|
+
return null;
|
|
5325
|
+
}
|
|
5326
|
+
const page = await this.#browserContext.newPage();
|
|
5327
|
+
await (0, import_utils12.sleep)(2e3);
|
|
5328
|
+
const pageInfo = page.pageInfo;
|
|
5329
|
+
if (!pageInfo) {
|
|
5330
|
+
throw new Error(`Logic error in getPage`);
|
|
5331
|
+
} else {
|
|
5332
|
+
pageInfo.openType = "newpage";
|
|
5333
|
+
}
|
|
5334
|
+
lsdPage = this.#lsdPages.find((p) => p.isFree());
|
|
5335
|
+
if (lsdPage) {
|
|
5336
|
+
lsdPage.use();
|
|
5337
|
+
this.#freeGettingLock();
|
|
5338
|
+
return lsdPage;
|
|
5339
|
+
} else {
|
|
5340
|
+
this.#freeGettingLock();
|
|
5341
|
+
return null;
|
|
5342
|
+
}
|
|
5343
|
+
} catch (err) {
|
|
5344
|
+
logerr(err);
|
|
5345
|
+
this.#freeGettingLock();
|
|
5346
|
+
return null;
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
free(clearStateData = false) {
|
|
5350
|
+
if (this.#status === "busy") {
|
|
5351
|
+
this.#status = "free";
|
|
5352
|
+
this.#lastStatusUpdateTime = (0, import_utils12.getCurrentUnixTime)();
|
|
5353
|
+
if (clearStateData) {
|
|
5354
|
+
}
|
|
5355
|
+
return true;
|
|
5356
|
+
} else {
|
|
5357
|
+
return false;
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
hasFreePage(pageNum = 1) {
|
|
5361
|
+
if (this.#maxPagesPerBrowserContext - this.#lsdPages.length > pageNum) {
|
|
5362
|
+
return true;
|
|
5363
|
+
} else if (this.#maxPagesPerBrowserContext - this.#lsdPages.filter((p) => !p.isFree()).length > pageNum) {
|
|
5364
|
+
return true;
|
|
5365
|
+
} else {
|
|
5366
|
+
return false;
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
5369
|
+
id() {
|
|
5370
|
+
return `browserContext-${this.#browserIdx}-${this.#browserContextIdx}`;
|
|
5371
|
+
}
|
|
5372
|
+
isFree() {
|
|
5373
|
+
return this.#status === "free";
|
|
5374
|
+
}
|
|
5375
|
+
isIncognito() {
|
|
5376
|
+
return this.#incognito;
|
|
5377
|
+
}
|
|
5378
|
+
page(pageIdx) {
|
|
5379
|
+
const lsdPage = this.#lsdPages.find((p) => p.pageInfo().pageIdx === pageIdx);
|
|
5380
|
+
return lsdPage ? lsdPage : null;
|
|
5381
|
+
}
|
|
5382
|
+
pages() {
|
|
5383
|
+
return this.#lsdPages;
|
|
5384
|
+
}
|
|
5385
|
+
proxy() {
|
|
5386
|
+
return this.#proxy;
|
|
5387
|
+
}
|
|
5388
|
+
async setStateData(stateData) {
|
|
5389
|
+
if (!this.#browserContext) {
|
|
5390
|
+
throw new Error("No valid browserContext");
|
|
5391
|
+
}
|
|
5392
|
+
try {
|
|
5393
|
+
const { cookies, localStorage: localStorageOrigins } = stateData;
|
|
5394
|
+
const page = await this.getPage();
|
|
5395
|
+
if (!page) {
|
|
5396
|
+
return false;
|
|
5397
|
+
}
|
|
5398
|
+
const origPage = page._origPage();
|
|
5399
|
+
if (cookies.length > 0) {
|
|
5400
|
+
await this.#browserContext.addCookies(cookies);
|
|
5401
|
+
}
|
|
5402
|
+
if (localStorageOrigins.length > 0) {
|
|
5403
|
+
await origPage.route("**/*", async (route) => {
|
|
5404
|
+
const request = route.request();
|
|
5405
|
+
await route.fulfill({
|
|
5406
|
+
status: 200,
|
|
5407
|
+
// contentType: "text/html; charset=utf-8", // "text/plain",
|
|
5408
|
+
body: `<html><body><h1>${request.url()}</h1></body></html>`
|
|
5409
|
+
});
|
|
5410
|
+
});
|
|
5411
|
+
for (const localStorageOrigin of localStorageOrigins) {
|
|
5412
|
+
const { origin, localStorage } = localStorageOrigin;
|
|
5413
|
+
await origPage.goto(origin);
|
|
5414
|
+
await origPage.evaluate((localStorageItems) => {
|
|
5415
|
+
for (const item of localStorageItems) {
|
|
5416
|
+
window.localStorage.setItem(item.name, item.value);
|
|
5417
|
+
}
|
|
5418
|
+
}, localStorage);
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
await (0, import_utils12.sleep)(2e3);
|
|
5422
|
+
await origPage.unrouteAll();
|
|
5423
|
+
await page.free();
|
|
5424
|
+
return true;
|
|
5425
|
+
} catch (err) {
|
|
5426
|
+
logerr(err);
|
|
5427
|
+
return false;
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
status() {
|
|
5431
|
+
return this.#status;
|
|
5432
|
+
}
|
|
5433
|
+
use() {
|
|
5434
|
+
if (this.#status === "free") {
|
|
5435
|
+
this.#status = "busy";
|
|
5436
|
+
this.#lastStatusUpdateTime = (0, import_utils12.getCurrentUnixTime)();
|
|
5437
|
+
return true;
|
|
5438
|
+
} else {
|
|
5439
|
+
return false;
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
_origBrowserContext() {
|
|
5443
|
+
return this.#browserContext;
|
|
5444
|
+
}
|
|
5445
|
+
};
|
|
5446
|
+
|
|
5447
|
+
// src/patchright/browser.ts
|
|
5448
|
+
var PatchrightBrowser = class _PatchrightBrowser extends import_node_events10.default {
|
|
5449
|
+
static #supportedBrowserTypes = ["chromium", "firefox", "webkit"];
|
|
5450
|
+
static doesSupport(browserType) {
|
|
5451
|
+
return _PatchrightBrowser.#supportedBrowserTypes.includes(browserType);
|
|
5452
|
+
}
|
|
5453
|
+
#browser;
|
|
5454
|
+
#browserIdx;
|
|
5455
|
+
#pid;
|
|
5456
|
+
#createTime;
|
|
5457
|
+
#lsdBrowserContexts;
|
|
5458
|
+
#browserControllerType;
|
|
5459
|
+
#browserType;
|
|
5460
|
+
#browserCreationMethod;
|
|
5461
|
+
#headless;
|
|
5462
|
+
#options;
|
|
5463
|
+
#proxy;
|
|
5464
|
+
/**
|
|
5465
|
+
* launch: actual path of executable app
|
|
5466
|
+
* connect: ""
|
|
5467
|
+
*/
|
|
5468
|
+
#executablePath;
|
|
5469
|
+
#nextBrowserContextIdx;
|
|
5470
|
+
#closeFreePagesIntervalId;
|
|
5471
|
+
#maxBrowserContextsPerBrowser() {
|
|
5472
|
+
return this.#options.maxBrowserContextsPerBrowser ? this.#options.maxBrowserContextsPerBrowser : 10;
|
|
5473
|
+
}
|
|
5474
|
+
#maxPagesPerBrowserContext() {
|
|
5475
|
+
return this.#options.maxPagesPerBrowserContext ? this.#options.maxPagesPerBrowserContext : 20;
|
|
5476
|
+
}
|
|
5477
|
+
#maxPageFreeSeconds() {
|
|
5478
|
+
return this.#options.maxPageFreeSeconds ? this.#options.maxPageFreeSeconds : 900;
|
|
5479
|
+
}
|
|
5480
|
+
// constructor: called only by LsdBrowserController.launch/connect
|
|
5481
|
+
constructor(browser, browserType, browserCreateMethod, options, browserIdx = 0, pid = 0) {
|
|
5482
|
+
if (!browser || typeof browser.contexts !== "function") {
|
|
5483
|
+
throw new Error(`Invalid playwright browser parameter`);
|
|
5484
|
+
}
|
|
5485
|
+
super();
|
|
5486
|
+
const { closeFreePagesIntervalSeconds = 300, maxPageFreeSeconds = 900, maxViewportOfNewPage = true, headless = false, executablePath = "" } = options;
|
|
5487
|
+
this.#browser = browser;
|
|
5488
|
+
this.#browserIdx = browserIdx;
|
|
5489
|
+
this.#pid = pid;
|
|
5490
|
+
this.#createTime = (0, import_utils13.getCurrentUnixTime)();
|
|
5491
|
+
this.#lsdBrowserContexts = [];
|
|
5492
|
+
this.#browserControllerType = "playwright";
|
|
5493
|
+
this.#browserType = browserType;
|
|
5494
|
+
if (!_PatchrightBrowser.#supportedBrowserTypes.includes(browserType)) {
|
|
5495
|
+
throw new Error(`Browser controller ${this.#browserControllerType} doesnot support browserType ${browserType}`);
|
|
5496
|
+
}
|
|
5497
|
+
this.#browserCreationMethod = browserCreateMethod;
|
|
5498
|
+
this.#headless = headless;
|
|
5499
|
+
this.#proxy = options?.proxy ? Object.assign({}, options.proxy) : null;
|
|
5500
|
+
this.#options = Object.assign({}, options, { closeFreePagesIntervalSeconds, maxPageFreeSeconds, maxViewportOfNewPage, headless, executablePath, proxy: this.#proxy });
|
|
5501
|
+
this.#executablePath = executablePath;
|
|
5502
|
+
this.#nextBrowserContextIdx = 1;
|
|
5503
|
+
this.#closeFreePagesIntervalId = null;
|
|
5504
|
+
loginfo(`##browser ${this.id()} ${this.#browserCreationMethod}ed by ${this.#browserControllerType}`);
|
|
5505
|
+
const browserContexts = browser.contexts();
|
|
5506
|
+
if (browserContexts.length > 0) {
|
|
5507
|
+
logwarn(`There are ${browserContexts.length} new browserContexts when playwright launches new browser`);
|
|
5508
|
+
}
|
|
5509
|
+
const incognito = typeof options?.incognito === "boolean" ? options.incognito : true;
|
|
5510
|
+
for (const browserContext of browserContexts) {
|
|
5511
|
+
const lsdBrowserContext = new PatchrightBrowserContext(this, browserContext, "launch", incognito, this.#proxy, this.#browserIdx, this.#nextBrowserContextIdx++, this.#maxPagesPerBrowserContext(), this.#maxPageFreeSeconds(), maxViewportOfNewPage);
|
|
5512
|
+
this.#lsdBrowserContexts.push(lsdBrowserContext);
|
|
5513
|
+
loginfo(`##browserContext ${lsdBrowserContext.id()} ${this.#browserCreationMethod}ed`);
|
|
5514
|
+
}
|
|
5515
|
+
browser.on("disconnected", () => {
|
|
5516
|
+
loginfo(`##browser ${this.id()} disconnected`);
|
|
5517
|
+
if (this.#lsdBrowserContexts.length > 0) {
|
|
5518
|
+
logerr(`${this.id()} has browserContexts when disconnected`);
|
|
5519
|
+
}
|
|
5520
|
+
});
|
|
5521
|
+
this.on("browserContextClose", (lsdBrowserContext) => {
|
|
5522
|
+
if (!(lsdBrowserContext instanceof PatchrightBrowserContext)) {
|
|
5523
|
+
logerr(`Invalid data in LsdBrowser.on("browserContextClose)`);
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
const idx = this.#lsdBrowserContexts.findIndex((bc) => bc === lsdBrowserContext);
|
|
5527
|
+
if (idx < 0) {
|
|
5528
|
+
logerr(`Invalid lsdBrowserContext in LsdBrowser.on("browserContextClose)`);
|
|
5529
|
+
return;
|
|
5530
|
+
}
|
|
5531
|
+
loginfo(`##browserContext ${lsdBrowserContext.id()} closed
|
|
5532
|
+
`);
|
|
5533
|
+
this.#lsdBrowserContexts.splice(idx, 1);
|
|
5534
|
+
if (this.#lsdBrowserContexts.length === 0) {
|
|
5535
|
+
loginfo(`##browser ${this.id()} has no browserContexts now`);
|
|
5536
|
+
}
|
|
5537
|
+
return;
|
|
5538
|
+
});
|
|
5539
|
+
if (closeFreePagesIntervalSeconds > 0 && maxPageFreeSeconds > 0) {
|
|
5540
|
+
this.#closeFreePagesIntervalId = setInterval(async () => {
|
|
5541
|
+
await this.#closeFreePagesHandler();
|
|
5542
|
+
}, closeFreePagesIntervalSeconds * 1e3);
|
|
5543
|
+
}
|
|
5544
|
+
}
|
|
5545
|
+
async #closeFreePagesHandler() {
|
|
5546
|
+
for (const lsdBrowserContext of this.#lsdBrowserContexts) {
|
|
5547
|
+
await lsdBrowserContext.closeFreePages();
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
async newBrowserContext(options) {
|
|
5551
|
+
if (this.#lsdBrowserContexts.length >= this.#maxBrowserContextsPerBrowser()) {
|
|
5552
|
+
logwarn(`##browser ${this.id()} can not create more new browserContext`);
|
|
5553
|
+
return null;
|
|
5554
|
+
}
|
|
5555
|
+
const browserContextOptions = {};
|
|
5556
|
+
if (this.#options.maxWindowSize) {
|
|
5557
|
+
browserContextOptions.viewport = null;
|
|
5558
|
+
}
|
|
5559
|
+
const proxy = options?.proxy ? Object.assign({}, options.proxy) : this.#proxy;
|
|
5560
|
+
if (proxy) {
|
|
5561
|
+
const { proxyUrl: server, username, password } = proxy;
|
|
5562
|
+
browserContextOptions.proxy = { server, username, password };
|
|
5563
|
+
}
|
|
5564
|
+
if (options?.userAgent) {
|
|
5565
|
+
browserContextOptions.userAgent = options.userAgent;
|
|
5566
|
+
}
|
|
5567
|
+
const browserContext = await this.#browser.newContext(browserContextOptions);
|
|
5568
|
+
const { maxViewportOfNewPage = this.#options.maxViewportOfNewPage } = options ? options : {};
|
|
5569
|
+
const lsdBrowserContext = new PatchrightBrowserContext(this, browserContext, "new", true, proxy, this.#browserIdx, this.#nextBrowserContextIdx++, this.#maxPagesPerBrowserContext(), this.#maxPageFreeSeconds(), maxViewportOfNewPage);
|
|
5570
|
+
this.#lsdBrowserContexts.push(lsdBrowserContext);
|
|
5571
|
+
loginfo(`##browser ${lsdBrowserContext.id()} created`);
|
|
5572
|
+
return lsdBrowserContext;
|
|
5573
|
+
}
|
|
5574
|
+
async close() {
|
|
5575
|
+
if (this.#closeFreePagesIntervalId) {
|
|
5576
|
+
clearInterval(this.#closeFreePagesIntervalId);
|
|
5577
|
+
}
|
|
5578
|
+
for (const lsdBrowserContext of this.#lsdBrowserContexts) {
|
|
5579
|
+
await lsdBrowserContext.close();
|
|
5580
|
+
}
|
|
5581
|
+
await this.#browser.close();
|
|
5582
|
+
return true;
|
|
5583
|
+
}
|
|
5584
|
+
browserContexts() {
|
|
5585
|
+
return this.#lsdBrowserContexts;
|
|
5586
|
+
}
|
|
5587
|
+
browserControllerType() {
|
|
5588
|
+
return this.#browserControllerType;
|
|
5589
|
+
}
|
|
5590
|
+
browserCreationMethod() {
|
|
5591
|
+
return this.#browserCreationMethod;
|
|
5592
|
+
}
|
|
5593
|
+
browserType() {
|
|
5594
|
+
return this.#browserType;
|
|
5595
|
+
}
|
|
5596
|
+
createTime() {
|
|
5597
|
+
return this.#createTime;
|
|
5598
|
+
}
|
|
5599
|
+
doesMeetBrowserContextRequirements(browserContextRequirements) {
|
|
5600
|
+
const { browserControllerTypes, browserTypes, browserHeadlesses } = browserContextRequirements;
|
|
5601
|
+
return (browserControllerTypes.length === 0 || browserControllerTypes.includes(this.#browserControllerType)) && (browserTypes.length === 0 || browserTypes.includes(this.#browserType)) && (browserHeadlesses.length === 0 || browserHeadlesses.includes(this.#headless));
|
|
5602
|
+
}
|
|
5603
|
+
executablePath() {
|
|
5604
|
+
return this.#executablePath;
|
|
5605
|
+
}
|
|
5606
|
+
id() {
|
|
5607
|
+
return `browser-${this.#browserType}-${this.#browserIdx}`;
|
|
5608
|
+
}
|
|
5609
|
+
isConnected() {
|
|
5610
|
+
return this.#browser.isConnected();
|
|
5611
|
+
}
|
|
5612
|
+
isHeadless() {
|
|
5613
|
+
return this.#headless;
|
|
5614
|
+
}
|
|
5615
|
+
options() {
|
|
5616
|
+
return this.#options;
|
|
5617
|
+
}
|
|
5618
|
+
pid() {
|
|
5619
|
+
return this.#pid;
|
|
5620
|
+
}
|
|
5621
|
+
async pidUsage() {
|
|
5622
|
+
if (this.#pid > 0) {
|
|
5623
|
+
const usage = await (0, import_utils13.getPerformanceOfPidTree)(this.#pid, "MB");
|
|
5624
|
+
return usage;
|
|
5625
|
+
} else {
|
|
5626
|
+
return { cpu: 0, memory: 0 };
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
proxy() {
|
|
5630
|
+
return this.#proxy;
|
|
5631
|
+
}
|
|
5632
|
+
async version() {
|
|
5633
|
+
const version = await this.#browser.version();
|
|
5634
|
+
return version;
|
|
5635
|
+
}
|
|
5636
|
+
_origBrowser() {
|
|
5637
|
+
return this.#browser;
|
|
5638
|
+
}
|
|
5639
|
+
};
|
|
5640
|
+
|
|
5641
|
+
// src/controller/controller.ts
|
|
5642
|
+
var import_utils14 = require("@letsscrapedata/utils");
|
|
5643
|
+
var LsdBrowserController = class _LsdBrowserController {
|
|
5644
|
+
static #forbidConstructor = false;
|
|
5645
|
+
#puppeteer;
|
|
5646
|
+
#playwrightBrowserTypes;
|
|
5647
|
+
#patchrightBrowserTypes;
|
|
5648
|
+
#nextBrowserIdx;
|
|
5649
|
+
/**
|
|
5650
|
+
* Possible values are 'aix', 'darwin', 'freebsd','linux', 'openbsd', 'sunos', and 'win32'.
|
|
5651
|
+
*/
|
|
5652
|
+
#osPlatform;
|
|
5653
|
+
constructor() {
|
|
5654
|
+
if (_LsdBrowserController.#forbidConstructor) {
|
|
5655
|
+
throw new Error("Only one LsdBrowserController instance can be created!");
|
|
5656
|
+
}
|
|
5657
|
+
this.#puppeteer = import_puppeteer.default;
|
|
5658
|
+
this.#playwrightBrowserTypes = {
|
|
5659
|
+
chromium: import_playwright.default.chromium,
|
|
5660
|
+
firefox: import_playwright.default.firefox,
|
|
5661
|
+
webkit: import_playwright.default.webkit
|
|
5662
|
+
};
|
|
5663
|
+
this.#patchrightBrowserTypes = {
|
|
5664
|
+
chromium: import_patchright.default.chromium,
|
|
5665
|
+
firefox: import_patchright.default.firefox,
|
|
5666
|
+
webkit: import_patchright.default.webkit
|
|
5667
|
+
};
|
|
5668
|
+
this.#osPlatform = import_os.default.platform();
|
|
5669
|
+
this.#nextBrowserIdx = 1;
|
|
5670
|
+
_LsdBrowserController.#forbidConstructor = true;
|
|
5671
|
+
}
|
|
5672
|
+
#playwrightBrowserType(browserType, connectFlag = false) {
|
|
5673
|
+
if (browserType === "chromium") {
|
|
5674
|
+
return this.#playwrightBrowserTypes.chromium;
|
|
5675
|
+
} else if (connectFlag) {
|
|
5676
|
+
throw new Error(`playwright only can connect to chromium browser, not support ${browserType} browser`);
|
|
5677
|
+
} else if (browserType === "firefox") {
|
|
5678
|
+
return this.#playwrightBrowserTypes.firefox;
|
|
5679
|
+
} else if (browserType === "webkit") {
|
|
5680
|
+
return this.#playwrightBrowserTypes.webkit;
|
|
5681
|
+
} else {
|
|
5682
|
+
throw new Error(`Invalid playwright browserType ${browserType}`);
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
#patchrightBrowserType(browserType, connectFlag = false) {
|
|
5686
|
+
if (browserType === "chromium") {
|
|
5687
|
+
return this.#patchrightBrowserTypes.chromium;
|
|
5688
|
+
} else if (connectFlag) {
|
|
5689
|
+
throw new Error(`patchright only can connect to chromium browser, not support ${browserType} browser`);
|
|
5690
|
+
} else if (browserType === "firefox") {
|
|
5691
|
+
return this.#patchrightBrowserTypes.firefox;
|
|
5692
|
+
} else if (browserType === "webkit") {
|
|
5693
|
+
return this.#patchrightBrowserTypes.webkit;
|
|
5694
|
+
} else {
|
|
5695
|
+
throw new Error(`Invalid patchright browserType ${browserType}`);
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
#puppeteerProduct(browserType) {
|
|
5699
|
+
if (browserType === "chromium") {
|
|
5700
|
+
return "chrome";
|
|
5701
|
+
} else {
|
|
5702
|
+
throw new Error(`Invalid puppeteer product ${browserType}`);
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
setBrowserPlugin(browserControllerType, browserType, plugin) {
|
|
5706
|
+
if (browserControllerType === "puppeteer") {
|
|
5707
|
+
if (!PuppeteerBrowser.doesSupport(browserType)) {
|
|
5708
|
+
throw new Error(`BrowserControllerType ${browserControllerType} doesnot support browserType ${browserType}`);
|
|
5709
|
+
}
|
|
5710
|
+
this.#puppeteer = plugin;
|
|
5711
|
+
} else if (browserControllerType === "playwright") {
|
|
5712
|
+
if (!PlaywrightBrowser.doesSupport(browserType)) {
|
|
5713
|
+
throw new Error(`BrowserControllerType ${browserControllerType} doesnot support browserType ${browserType}`);
|
|
5714
|
+
}
|
|
5715
|
+
switch (browserType) {
|
|
5716
|
+
case "chromium":
|
|
5717
|
+
case "firefox":
|
|
5718
|
+
case "webkit":
|
|
5719
|
+
this.#playwrightBrowserTypes[browserType] = plugin;
|
|
5720
|
+
break;
|
|
5721
|
+
default:
|
|
5722
|
+
(0, import_utils14.unreachable)(browserType);
|
|
5723
|
+
}
|
|
5724
|
+
} else if (browserControllerType === "patchright") {
|
|
5725
|
+
if (!PatchrightBrowser.doesSupport(browserType)) {
|
|
5726
|
+
throw new Error(`BrowserControllerType ${browserControllerType} doesnot support browserType ${browserType}`);
|
|
5727
|
+
}
|
|
5728
|
+
switch (browserType) {
|
|
5729
|
+
case "chromium":
|
|
5730
|
+
case "firefox":
|
|
5731
|
+
case "webkit":
|
|
5732
|
+
this.#patchrightBrowserTypes[browserType] = plugin;
|
|
5733
|
+
break;
|
|
5734
|
+
default:
|
|
5735
|
+
(0, import_utils14.unreachable)(browserType);
|
|
5736
|
+
}
|
|
5737
|
+
} else {
|
|
5738
|
+
(0, import_utils14.unreachable)(browserControllerType);
|
|
5739
|
+
}
|
|
5740
|
+
return true;
|
|
5741
|
+
}
|
|
5742
|
+
async launch(browserControllerType, browserType, options) {
|
|
5743
|
+
let {
|
|
3733
5744
|
closeFreePagesIntervalSeconds = 300,
|
|
3734
5745
|
maxBrowserContextsPerBrowser = 10,
|
|
3735
5746
|
maxPagesPerBrowserContext = 20,
|
|
@@ -3812,6 +5823,47 @@ var LsdBrowserController = class _LsdBrowserController {
|
|
|
3812
5823
|
const browser = await playwrightBrowserType.launch(launchOptions);
|
|
3813
5824
|
lsdBrowser = new PlaywrightBrowser(browser, browserType, "launch", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
3814
5825
|
}
|
|
5826
|
+
} else if (browserControllerType === "patchright") {
|
|
5827
|
+
const launchOptions = { headless, timeout };
|
|
5828
|
+
if (executablePath) {
|
|
5829
|
+
launchOptions.executablePath = executablePath;
|
|
5830
|
+
}
|
|
5831
|
+
if (maxWindowSize) {
|
|
5832
|
+
args.push("--start-maximized");
|
|
5833
|
+
}
|
|
5834
|
+
if (proxy?.proxyUrl && proxy.proxyUrl !== "local") {
|
|
5835
|
+
const { proxyUrl: server, username, password } = proxy;
|
|
5836
|
+
launchOptions.proxy = { server, username, password };
|
|
5837
|
+
} else if (proxyPerBrowserContext && browserType === "chromium" && this.#osPlatform.startsWith("win")) {
|
|
5838
|
+
launchOptions.proxy = { server: "proxyPerBrowserContext" };
|
|
5839
|
+
}
|
|
5840
|
+
if (browserType === "chromium") {
|
|
5841
|
+
if (incognito) {
|
|
5842
|
+
args.push("--incognito");
|
|
5843
|
+
} else if (userDataDir) {
|
|
5844
|
+
args.push(`--user-data-dir=${userDataDir}`);
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
if (args.length > 0) {
|
|
5848
|
+
launchOptions.args = args;
|
|
5849
|
+
}
|
|
5850
|
+
const patchrightBrowserType = this.#patchrightBrowserType(browserType);
|
|
5851
|
+
if (!actOptions.executablePath) {
|
|
5852
|
+
actOptions.executablePath = patchrightBrowserType.executablePath();
|
|
5853
|
+
}
|
|
5854
|
+
if (options.launchMethod === "launchServer") {
|
|
5855
|
+
const browserServer = await patchrightBrowserType.launchServer(launchOptions);
|
|
5856
|
+
const process = browserServer.process();
|
|
5857
|
+
if (process?.pid) {
|
|
5858
|
+
browserPid = process.pid;
|
|
5859
|
+
}
|
|
5860
|
+
const wsEndpoint = browserServer.wsEndpoint();
|
|
5861
|
+
const browser = await patchrightBrowserType.connect(wsEndpoint);
|
|
5862
|
+
lsdBrowser = new PatchrightBrowser(browser, browserType, "launch", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
5863
|
+
} else {
|
|
5864
|
+
const browser = await patchrightBrowserType.launch(launchOptions);
|
|
5865
|
+
lsdBrowser = new PatchrightBrowser(browser, browserType, "launch", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
5866
|
+
}
|
|
3815
5867
|
} else if (browserControllerType === "puppeteer") {
|
|
3816
5868
|
const product = this.#puppeteerProduct(browserType);
|
|
3817
5869
|
const launchOptions = { headless, timeout, product };
|
|
@@ -3877,7 +5929,7 @@ var LsdBrowserController = class _LsdBrowserController {
|
|
|
3877
5929
|
}
|
|
3878
5930
|
const u = new URL(browserUrl);
|
|
3879
5931
|
const port = u.port ? parseInt(u.port) : 80;
|
|
3880
|
-
const pids = await (0,
|
|
5932
|
+
const pids = await (0, import_utils14.getPidsListeningOnPort)(port);
|
|
3881
5933
|
let browserPid = 0;
|
|
3882
5934
|
if (pids.length !== 1) {
|
|
3883
5935
|
logerr(`##browser pids.length ${pids.length} is not 1 when trying to connect to browserUrl ${browserUrl}`);
|
|
@@ -3890,6 +5942,11 @@ var LsdBrowserController = class _LsdBrowserController {
|
|
|
3890
5942
|
const browser = await playwrightBrowserType.connectOverCDP(browserUrl);
|
|
3891
5943
|
const lsdBrowser = new PlaywrightBrowser(browser, browserType, "connect", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
3892
5944
|
return lsdBrowser;
|
|
5945
|
+
} else if (browserControllerType === "patchright") {
|
|
5946
|
+
const patchrightBrowserType = this.#patchrightBrowserType(browserType, true);
|
|
5947
|
+
const browser = await patchrightBrowserType.connectOverCDP(browserUrl);
|
|
5948
|
+
const lsdBrowser = new PatchrightBrowser(browser, browserType, "connect", actOptions, this.#nextBrowserIdx++, browserPid);
|
|
5949
|
+
return lsdBrowser;
|
|
3893
5950
|
} else if (browserControllerType === "puppeteer") {
|
|
3894
5951
|
this.#puppeteerProduct(browserType);
|
|
3895
5952
|
const browser = await this.#puppeteer.connect({ browserURL: browserUrl });
|