@crawlee/puppeteer 3.0.0-alpha.2 → 3.0.0-alpha.3
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/LICENSE.md +201 -0
- package/{README.md → dist/README.md} +0 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +76 -0
- package/dist/internals/enqueue-links/click-elements.d.ts +188 -0
- package/dist/internals/enqueue-links/click-elements.d.ts.map +1 -0
- package/dist/internals/enqueue-links/click-elements.js +336 -0
- package/dist/internals/enqueue-links/click-elements.js.map +1 -0
- package/dist/internals/puppeteer-crawler.d.ts +149 -0
- package/dist/internals/puppeteer-crawler.d.ts.map +1 -0
- package/dist/internals/puppeteer-crawler.js +117 -0
- package/dist/internals/puppeteer-crawler.js.map +1 -0
- package/dist/internals/puppeteer-launcher.d.ts +134 -0
- package/dist/internals/puppeteer-launcher.d.ts.map +1 -0
- package/dist/internals/puppeteer-launcher.js +146 -0
- package/dist/internals/puppeteer-launcher.js.map +1 -0
- package/dist/internals/stealth/hiding_tricks.d.ts +10 -0
- package/dist/internals/stealth/hiding_tricks.d.ts.map +1 -0
- package/dist/internals/stealth/hiding_tricks.js +334 -0
- package/dist/internals/stealth/hiding_tricks.js.map +1 -0
- package/dist/internals/stealth/index.d.ts +40 -0
- package/dist/internals/stealth/index.d.ts.map +1 -0
- package/dist/internals/stealth/index.js +108 -0
- package/dist/internals/stealth/index.js.map +1 -0
- package/dist/internals/utils/puppeteer_request_interception.d.ts +60 -0
- package/dist/internals/utils/puppeteer_request_interception.d.ts.map +1 -0
- package/dist/internals/utils/puppeteer_request_interception.js +225 -0
- package/dist/internals/utils/puppeteer_request_interception.js.map +1 -0
- package/dist/internals/utils/puppeteer_utils.d.ts +298 -0
- package/dist/internals/utils/puppeteer_utils.d.ts.map +1 -0
- package/dist/internals/utils/puppeteer_utils.js +468 -0
- package/dist/internals/utils/puppeteer_utils.js.map +1 -0
- package/dist/package.json +66 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +8 -7
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clickElements = exports.isTargetRelevant = exports.clickElementsAndInterceptNavigationRequests = exports.enqueueLinksByClickingElements = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const browser_1 = require("@crawlee/browser");
|
|
6
|
+
const log_1 = tslib_1.__importDefault(require("@apify/log"));
|
|
7
|
+
const ow_1 = tslib_1.__importDefault(require("ow"));
|
|
8
|
+
const url_1 = require("url");
|
|
9
|
+
const puppeteer_request_interception_1 = require("../utils/puppeteer_request_interception");
|
|
10
|
+
const STARTING_Z_INDEX = 2147400000;
|
|
11
|
+
const log = log_1.default.child({ prefix: 'Puppeteer Click Elements' });
|
|
12
|
+
/**
|
|
13
|
+
* The function finds elements matching a specific CSS selector in a Puppeteer page,
|
|
14
|
+
* clicks all those elements using a mouse move and a left mouse button click and intercepts
|
|
15
|
+
* all the navigation requests that are subsequently produced by the page. The intercepted
|
|
16
|
+
* requests, including their methods, headers and payloads are then enqueued to a provided
|
|
17
|
+
* {@link RequestQueue}. This is useful to crawl JavaScript heavy pages where links are not available
|
|
18
|
+
* in `href` elements, but rather navigations are triggered in click handlers.
|
|
19
|
+
* If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
|
|
20
|
+
*
|
|
21
|
+
* Optionally, the function allows you to filter the target links' URLs using an array of {@link PseudoUrl} objects
|
|
22
|
+
* and override settings of the enqueued {@link Request} objects.
|
|
23
|
+
*
|
|
24
|
+
* **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
|
|
25
|
+
* such as changing the Z-index of elements being clicked and their visibility. Therefore,
|
|
26
|
+
* it is recommended to only use this function as the last operation in the page.
|
|
27
|
+
*
|
|
28
|
+
* **USING HEADFUL BROWSER**: When using a headful browser, this function will only be able to click elements
|
|
29
|
+
* in the focused tab, effectively limiting concurrency to 1. In headless mode, full concurrency can be achieved.
|
|
30
|
+
*
|
|
31
|
+
* **PERFORMANCE**: Clicking elements with a mouse and intercepting requests is not a low level operation
|
|
32
|
+
* that takes nanoseconds. It's not very CPU intensive, but it takes time. We strongly recommend limiting
|
|
33
|
+
* the scope of the clicking as much as possible by using a specific selector that targets only the elements
|
|
34
|
+
* that you assume or know will produce a navigation. You can certainly click everything by using
|
|
35
|
+
* the `*` selector, but be prepared to wait minutes to get results on a large and complex page.
|
|
36
|
+
*
|
|
37
|
+
* **Example usage**
|
|
38
|
+
*
|
|
39
|
+
* ```javascript
|
|
40
|
+
* await Actor.utils.puppeteer.enqueueLinksByClickingElements({
|
|
41
|
+
* page,
|
|
42
|
+
* requestQueue,
|
|
43
|
+
* selector: 'a.product-detail',
|
|
44
|
+
* pseudoUrls: [
|
|
45
|
+
* 'https://www.example.com/handbags/[.*]'
|
|
46
|
+
* 'https://www.example.com/purses/[.*]'
|
|
47
|
+
* ],
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @returns Promise that resolves to {@link BatchAddRequestsResult} object.
|
|
52
|
+
*/
|
|
53
|
+
async function enqueueLinksByClickingElements(options) {
|
|
54
|
+
(0, ow_1.default)(options, ow_1.default.object.exactShape({
|
|
55
|
+
page: ow_1.default.object.hasKeys('goto', 'evaluate'),
|
|
56
|
+
requestQueue: ow_1.default.object.hasKeys('fetchNextRequest', 'addRequest'),
|
|
57
|
+
selector: ow_1.default.string,
|
|
58
|
+
clickOptions: ow_1.default.optional.object.hasKeys('clickCount', 'delay'),
|
|
59
|
+
pseudoUrls: ow_1.default.optional.array.ofType(ow_1.default.any(ow_1.default.string, ow_1.default.object.hasKeys('purl'))),
|
|
60
|
+
globs: ow_1.default.optional.array.ofType(ow_1.default.any(ow_1.default.string, ow_1.default.object.hasKeys('glob'))),
|
|
61
|
+
regexps: ow_1.default.optional.array.ofType(ow_1.default.any(ow_1.default.regExp, ow_1.default.object.hasKeys('regexp'))),
|
|
62
|
+
transformRequestFunction: ow_1.default.optional.function,
|
|
63
|
+
waitForPageIdleSecs: ow_1.default.optional.number,
|
|
64
|
+
maxWaitForPageIdleSecs: ow_1.default.optional.number,
|
|
65
|
+
}));
|
|
66
|
+
const { page, requestQueue, selector, clickOptions, pseudoUrls, globs, regexps, transformRequestFunction, waitForPageIdleSecs = 1, maxWaitForPageIdleSecs = 5, } = options;
|
|
67
|
+
const waitForPageIdleMillis = waitForPageIdleSecs * 1000;
|
|
68
|
+
const maxWaitForPageIdleMillis = maxWaitForPageIdleSecs * 1000;
|
|
69
|
+
const urlPatternObjects = [];
|
|
70
|
+
if (pseudoUrls?.length) {
|
|
71
|
+
log.deprecated('`pseudoUrls` option is deprecated, use `globs` or `regexps` instead');
|
|
72
|
+
urlPatternObjects.push(...(0, browser_1.constructRegExpObjectsFromPseudoUrls)(pseudoUrls));
|
|
73
|
+
}
|
|
74
|
+
if (globs?.length) {
|
|
75
|
+
urlPatternObjects.push(...(0, browser_1.constructGlobObjectsFromGlobs)(globs));
|
|
76
|
+
}
|
|
77
|
+
if (regexps?.length) {
|
|
78
|
+
urlPatternObjects.push(...(0, browser_1.constructRegExpObjectsFromRegExps)(regexps));
|
|
79
|
+
}
|
|
80
|
+
const interceptedRequests = await clickElementsAndInterceptNavigationRequests({
|
|
81
|
+
page,
|
|
82
|
+
selector,
|
|
83
|
+
waitForPageIdleMillis,
|
|
84
|
+
maxWaitForPageIdleMillis,
|
|
85
|
+
clickOptions,
|
|
86
|
+
});
|
|
87
|
+
let requestOptions = (0, browser_1.createRequestOptions)(interceptedRequests);
|
|
88
|
+
if (transformRequestFunction) {
|
|
89
|
+
requestOptions = requestOptions.map(transformRequestFunction).filter((r) => !!r);
|
|
90
|
+
}
|
|
91
|
+
const requests = (0, browser_1.createRequests)(requestOptions, urlPatternObjects);
|
|
92
|
+
return requestQueue.addRequests(requests);
|
|
93
|
+
}
|
|
94
|
+
exports.enqueueLinksByClickingElements = enqueueLinksByClickingElements;
|
|
95
|
+
/**
|
|
96
|
+
* Clicks all elements of given page matching given selector.
|
|
97
|
+
* Catches and intercepts all initiated navigation requests and opened pages.
|
|
98
|
+
* Returns a list of all target URLs.
|
|
99
|
+
* @ignore
|
|
100
|
+
*/
|
|
101
|
+
async function clickElementsAndInterceptNavigationRequests(options) {
|
|
102
|
+
const { page, selector, waitForPageIdleMillis, maxWaitForPageIdleMillis, clickOptions, } = options;
|
|
103
|
+
const uniqueRequests = new Set();
|
|
104
|
+
const browser = page.browser();
|
|
105
|
+
const onInterceptedRequest = createInterceptRequestHandler(page, uniqueRequests);
|
|
106
|
+
const onTargetCreated = createTargetCreatedHandler(page, uniqueRequests);
|
|
107
|
+
const onFrameNavigated = createFrameNavigatedHandler(page, uniqueRequests);
|
|
108
|
+
await (0, puppeteer_request_interception_1.addInterceptRequestHandler)(page, onInterceptedRequest);
|
|
109
|
+
browser.on("targetcreated" /* TargetCreated */, onTargetCreated);
|
|
110
|
+
page.on("framenavigated" /* FrameNavigated */, onFrameNavigated);
|
|
111
|
+
await preventHistoryNavigation(page);
|
|
112
|
+
await clickElements(page, selector, clickOptions);
|
|
113
|
+
await waitForPageIdle({ page, waitForPageIdleMillis, maxWaitForPageIdleMillis });
|
|
114
|
+
await restoreHistoryNavigationAndSaveCapturedUrls(page, uniqueRequests);
|
|
115
|
+
browser.off("targetcreated" /* TargetCreated */, onTargetCreated);
|
|
116
|
+
page.off("framenavigated" /* FrameNavigated */, onFrameNavigated);
|
|
117
|
+
await (0, puppeteer_request_interception_1.removeInterceptRequestHandler)(page, onInterceptedRequest);
|
|
118
|
+
const serializedRequests = Array.from(uniqueRequests);
|
|
119
|
+
return serializedRequests.map((r) => JSON.parse(r));
|
|
120
|
+
}
|
|
121
|
+
exports.clickElementsAndInterceptNavigationRequests = clickElementsAndInterceptNavigationRequests;
|
|
122
|
+
/**
|
|
123
|
+
* @ignore
|
|
124
|
+
*/
|
|
125
|
+
function createInterceptRequestHandler(page, requests) {
|
|
126
|
+
return async function onInterceptedRequest(req) {
|
|
127
|
+
if (!isTopFrameNavigationRequest(page, req))
|
|
128
|
+
return req.continue();
|
|
129
|
+
const url = req.url();
|
|
130
|
+
requests.add(JSON.stringify({
|
|
131
|
+
url,
|
|
132
|
+
headers: req.headers(),
|
|
133
|
+
method: req.method(),
|
|
134
|
+
payload: req.postData(),
|
|
135
|
+
}));
|
|
136
|
+
if (req.redirectChain().length) {
|
|
137
|
+
await req.respond({ body: '' }); // Prevents 301/302 redirect
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
await req.abort('aborted'); // Prevents navigation by js
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* @ignore
|
|
146
|
+
*/
|
|
147
|
+
function isTopFrameNavigationRequest(page, req) {
|
|
148
|
+
return req.isNavigationRequest()
|
|
149
|
+
&& req.frame() === page.mainFrame();
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* @ignore
|
|
153
|
+
*/
|
|
154
|
+
function createTargetCreatedHandler(page, requests) {
|
|
155
|
+
return async function onTargetCreated(target) {
|
|
156
|
+
if (!isTargetRelevant(page, target))
|
|
157
|
+
return;
|
|
158
|
+
const url = target.url();
|
|
159
|
+
requests.add(JSON.stringify({ url }));
|
|
160
|
+
// We want to close the page but don't care about
|
|
161
|
+
// possible errors like target closed.
|
|
162
|
+
try {
|
|
163
|
+
const createdPage = await target.page();
|
|
164
|
+
await createdPage.close();
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
log.debug('enqueueLinksByClickingElements: Could not close spawned page.', { error: err.stack });
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* We're only interested in pages created by the page we're currently clicking in.
|
|
173
|
+
* There will generally be a lot of other targets being created in the browser.
|
|
174
|
+
*/
|
|
175
|
+
function isTargetRelevant(page, target) {
|
|
176
|
+
return target.type() === 'page'
|
|
177
|
+
&& page.target() === target.opener();
|
|
178
|
+
}
|
|
179
|
+
exports.isTargetRelevant = isTargetRelevant;
|
|
180
|
+
/**
|
|
181
|
+
* @ignore
|
|
182
|
+
*/
|
|
183
|
+
function createFrameNavigatedHandler(page, requests) {
|
|
184
|
+
return function onFrameNavigated(frame) {
|
|
185
|
+
if (frame !== page.mainFrame())
|
|
186
|
+
return;
|
|
187
|
+
const url = frame.url();
|
|
188
|
+
requests.add(JSON.stringify({ url }));
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* @ignore
|
|
193
|
+
*/
|
|
194
|
+
async function preventHistoryNavigation(page) {
|
|
195
|
+
/* istanbul ignore next */
|
|
196
|
+
return page.evaluate(() => {
|
|
197
|
+
window.__originalHistory__ = window.history; // eslint-disable-line no-underscore-dangle
|
|
198
|
+
delete window.history; // Simple override does not work.
|
|
199
|
+
window.history = {
|
|
200
|
+
stateHistory: [],
|
|
201
|
+
length: 0,
|
|
202
|
+
state: {},
|
|
203
|
+
go() { },
|
|
204
|
+
back() { },
|
|
205
|
+
forward() { },
|
|
206
|
+
pushState(...args) {
|
|
207
|
+
this.stateHistory.push(args);
|
|
208
|
+
},
|
|
209
|
+
replaceState(...args) {
|
|
210
|
+
this.stateHistory.push(args);
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Click all elements matching the given selector. To be able to do this using
|
|
217
|
+
* Puppeteer's `.click()` we need to make sure the elements are reachable by mouse,
|
|
218
|
+
* so we first move them to the top of the page's stacking context and then click.
|
|
219
|
+
* We do all in series to prevent elements from hiding one another. Therefore,
|
|
220
|
+
* for large element sets, this will take considerable amount of time.
|
|
221
|
+
* @ignore
|
|
222
|
+
*/
|
|
223
|
+
async function clickElements(page, selector, clickOptions) {
|
|
224
|
+
const elementHandles = await page.$$(selector);
|
|
225
|
+
log.debug(`enqueueLinksByClickingElements: There are ${elementHandles.length} elements to click.`);
|
|
226
|
+
let clickedElementsCount = 0;
|
|
227
|
+
let zIndex = STARTING_Z_INDEX;
|
|
228
|
+
let shouldLogWarning = true;
|
|
229
|
+
for (const handle of elementHandles) {
|
|
230
|
+
try {
|
|
231
|
+
await page.evaluate(updateElementCssToEnableMouseClick, handle, zIndex++);
|
|
232
|
+
await handle.click(clickOptions);
|
|
233
|
+
clickedElementsCount++;
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
const e = err;
|
|
237
|
+
if (shouldLogWarning && e.stack.includes('is detached from document')) {
|
|
238
|
+
log.warning(`An element with selector ${selector} that you're trying to click has been removed from the page. `
|
|
239
|
+
+ 'This was probably caused by an earlier click which triggered some JavaScript on the page that caused it to change. '
|
|
240
|
+
+ 'If you\'re trying to enqueue pagination links, we suggest using the "next" button, if available and going one by one.');
|
|
241
|
+
shouldLogWarning = false;
|
|
242
|
+
}
|
|
243
|
+
log.debug('enqueueLinksByClickingElements: Click failed.', { stack: e.stack });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
log.debug(`enqueueLinksByClickingElements: Successfully clicked ${clickedElementsCount} elements out of ${elementHandles.length}`);
|
|
247
|
+
}
|
|
248
|
+
exports.clickElements = clickElements;
|
|
249
|
+
/* istanbul ignore next */
|
|
250
|
+
/**
|
|
251
|
+
* This is an in browser function!
|
|
252
|
+
*/
|
|
253
|
+
function updateElementCssToEnableMouseClick(el, zIndex) {
|
|
254
|
+
el.style.visibility = 'visible';
|
|
255
|
+
el.style.display = 'block';
|
|
256
|
+
el.style.position = 'fixed';
|
|
257
|
+
el.style.zIndex = String(zIndex);
|
|
258
|
+
el.style.left = '0';
|
|
259
|
+
el.style.top = '0';
|
|
260
|
+
const boundingRect = el.getBoundingClientRect();
|
|
261
|
+
if (!boundingRect.height)
|
|
262
|
+
el.style.height = '10px';
|
|
263
|
+
if (!boundingRect.width)
|
|
264
|
+
el.style.width = '10px';
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* This function tracks whether any requests, frame navigations or targets were emitted
|
|
268
|
+
* in the past idleIntervalMillis and whenever the interval registers no activity,
|
|
269
|
+
* the function returns.
|
|
270
|
+
*
|
|
271
|
+
* It will also return when a final timeout, represented by the timeoutMillis parameter
|
|
272
|
+
* is reached, to prevent blocking on pages with constant network activity.
|
|
273
|
+
*
|
|
274
|
+
* We need this to make sure we don't finish too soon when intercepting requests triggered
|
|
275
|
+
* by clicking in the page. They often get registered by the Node.js process only some
|
|
276
|
+
* milliseconds after clicking and we would lose those requests. This is especially prevalent
|
|
277
|
+
* when there's only a single element to click.
|
|
278
|
+
* @ignore
|
|
279
|
+
*/
|
|
280
|
+
async function waitForPageIdle({ page, waitForPageIdleMillis, maxWaitForPageIdleMillis }) {
|
|
281
|
+
return new Promise((resolve) => {
|
|
282
|
+
let timeout;
|
|
283
|
+
let maxTimeout;
|
|
284
|
+
const context = page.browserContext();
|
|
285
|
+
function newTabTracker(target) {
|
|
286
|
+
if (isTargetRelevant(page, target))
|
|
287
|
+
activityHandler();
|
|
288
|
+
}
|
|
289
|
+
function activityHandler() {
|
|
290
|
+
clearTimeout(timeout);
|
|
291
|
+
timeout = setTimeout(() => {
|
|
292
|
+
clearTimeout(maxTimeout);
|
|
293
|
+
finish();
|
|
294
|
+
}, waitForPageIdleMillis);
|
|
295
|
+
}
|
|
296
|
+
function maxTimeoutHandler() {
|
|
297
|
+
log.debug(`enqueueLinksByClickingElements: Page still showed activity after ${maxWaitForPageIdleMillis}ms. `
|
|
298
|
+
+ 'This is probably due to the website itself dispatching requests, but some links may also have been missed.');
|
|
299
|
+
finish();
|
|
300
|
+
}
|
|
301
|
+
function finish() {
|
|
302
|
+
page.off("request" /* Request */, activityHandler);
|
|
303
|
+
page.off("framenavigated" /* FrameNavigated */, activityHandler);
|
|
304
|
+
context.off("targetcreated" /* TargetCreated */, newTabTracker);
|
|
305
|
+
resolve();
|
|
306
|
+
}
|
|
307
|
+
maxTimeout = setTimeout(maxTimeoutHandler, maxWaitForPageIdleMillis);
|
|
308
|
+
activityHandler(); // We call this once manually in case there would be no requests at all.
|
|
309
|
+
page.on("request" /* Request */, activityHandler);
|
|
310
|
+
page.on("framenavigated" /* FrameNavigated */, activityHandler);
|
|
311
|
+
context.on("targetcreated" /* TargetCreated */, newTabTracker);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* @ignore
|
|
316
|
+
*/
|
|
317
|
+
async function restoreHistoryNavigationAndSaveCapturedUrls(page, requests) {
|
|
318
|
+
/* eslint-disable no-shadow */
|
|
319
|
+
/* istanbul ignore next */
|
|
320
|
+
const state = await page.evaluate(() => {
|
|
321
|
+
const { stateHistory } = window.history;
|
|
322
|
+
window.history = window.__originalHistory__; // eslint-disable-line no-underscore-dangle
|
|
323
|
+
return stateHistory;
|
|
324
|
+
});
|
|
325
|
+
state.forEach((args) => {
|
|
326
|
+
try {
|
|
327
|
+
const stateUrl = args[args.length - 1];
|
|
328
|
+
const url = new url_1.URL(stateUrl, page.url()).href;
|
|
329
|
+
requests.add(JSON.stringify({ url }));
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
log.debug('enqueueLinksByClickingElements: Failed to ', { error: err.stack });
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=click-elements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"click-elements.js","sourceRoot":"","sources":["../../../src/internals/enqueue-links/click-elements.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { BrowserCrawler, BrowserCrawlerHandleRequest, BrowserCrawlerOptions, BrowserCrawlingContext, BrowserHook } from '@crawlee/browser';
|
|
2
|
+
import { PuppeteerPlugin } from '@crawlee/browser-pool';
|
|
3
|
+
import { HTTPResponse, LaunchOptions, Page } from 'puppeteer';
|
|
4
|
+
import { PuppeteerLaunchContext } from './puppeteer-launcher';
|
|
5
|
+
import { DirectNavigationOptions } from './utils/puppeteer_utils';
|
|
6
|
+
export declare type PuppeteerController = ReturnType<PuppeteerPlugin['_createController']>;
|
|
7
|
+
export declare type PuppeteerCrawlContext = BrowserCrawlingContext<Page, HTTPResponse, PuppeteerController>;
|
|
8
|
+
export declare type PuppeteerHook = BrowserHook<PuppeteerCrawlContext, PuppeteerGoToOptions>;
|
|
9
|
+
export declare type PuppeteerRequestHandlerParam = BrowserCrawlingContext<Page, HTTPResponse, PuppeteerController>;
|
|
10
|
+
export declare type PuppeteerRequestHandler = BrowserCrawlerHandleRequest<PuppeteerRequestHandlerParam>;
|
|
11
|
+
export declare type PuppeteerGoToOptions = Parameters<Page['goto']>[1];
|
|
12
|
+
export interface PuppeteerCrawlerOptions extends BrowserCrawlerOptions<PuppeteerCrawlContext, PuppeteerGoToOptions, {
|
|
13
|
+
browserPlugins: [PuppeteerPlugin];
|
|
14
|
+
}> {
|
|
15
|
+
/**
|
|
16
|
+
* Options used by {@link launchPuppeteer} to start new Puppeteer instances.
|
|
17
|
+
*/
|
|
18
|
+
launchContext?: PuppeteerLaunchContext;
|
|
19
|
+
/**
|
|
20
|
+
* Async functions that are sequentially evaluated before the navigation. Good for setting additional cookies
|
|
21
|
+
* or browser properties before navigation. The function accepts two parameters, `crawlingContext` and `gotoOptions`,
|
|
22
|
+
* which are passed to the `page.goto()` function the crawler calls to navigate.
|
|
23
|
+
* Example:
|
|
24
|
+
* ```
|
|
25
|
+
* preNavigationHooks: [
|
|
26
|
+
* async (crawlingContext, gotoOptions) => {
|
|
27
|
+
* const { page } = crawlingContext;
|
|
28
|
+
* await page.evaluate((attr) => { window.foo = attr; }, 'bar');
|
|
29
|
+
* },
|
|
30
|
+
* ]
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
preNavigationHooks?: PuppeteerHook[];
|
|
34
|
+
/**
|
|
35
|
+
* Async functions that are sequentially evaluated after the navigation. Good for checking if the navigation was successful.
|
|
36
|
+
* The function accepts `crawlingContext` as the only parameter.
|
|
37
|
+
* Example:
|
|
38
|
+
* ```
|
|
39
|
+
* postNavigationHooks: [
|
|
40
|
+
* async (crawlingContext) => {
|
|
41
|
+
* const { page } = crawlingContext;
|
|
42
|
+
* if (hasCaptcha(page)) {
|
|
43
|
+
* await solveCaptcha (page);
|
|
44
|
+
* }
|
|
45
|
+
* },
|
|
46
|
+
* ]
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
postNavigationHooks?: PuppeteerHook[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Provides a simple framework for parallel crawling of web pages
|
|
53
|
+
* using headless Chrome with [Puppeteer](https://github.com/puppeteer/puppeteer).
|
|
54
|
+
* The URLs to crawl are fed either from a static list of URLs
|
|
55
|
+
* or from a dynamic queue of URLs enabling recursive crawling of websites.
|
|
56
|
+
*
|
|
57
|
+
* Since `PuppeteerCrawler` uses headless Chrome to download web pages and extract data,
|
|
58
|
+
* it is useful for crawling of websites that require to execute JavaScript.
|
|
59
|
+
* If the target website doesn't need JavaScript, consider using {@link CheerioCrawler},
|
|
60
|
+
* which downloads the pages using raw HTTP requests and is about 10x faster.
|
|
61
|
+
*
|
|
62
|
+
* The source URLs are represented using {@link Request} objects that are fed from
|
|
63
|
+
* {@link RequestList} or {@link RequestQueue} instances provided by the {@link PuppeteerCrawlerOptions.requestList}
|
|
64
|
+
* or {@link PuppeteerCrawlerOptions.requestQueue} constructor options, respectively.
|
|
65
|
+
*
|
|
66
|
+
* If both {@link PuppeteerCrawlerOptions.requestList} and {@link PuppeteerCrawlerOptions.requestQueue} are used,
|
|
67
|
+
* the instance first processes URLs from the {@link RequestList} and automatically enqueues all of them
|
|
68
|
+
* to {@link RequestQueue} before it starts their processing. This ensures that a single URL is not crawled multiple times.
|
|
69
|
+
*
|
|
70
|
+
* The crawler finishes when there are no more {@link Request} objects to crawl.
|
|
71
|
+
*
|
|
72
|
+
* `PuppeteerCrawler` opens a new Chrome page (i.e. tab) for each {@link Request} object to crawl
|
|
73
|
+
* and then calls the function provided by user as the {@link PuppeteerCrawlerOptions.handlePageFunction} option.
|
|
74
|
+
*
|
|
75
|
+
* New pages are only opened when there is enough free CPU and memory available,
|
|
76
|
+
* using the functionality provided by the {@link AutoscaledPool} class.
|
|
77
|
+
* All {@link AutoscaledPool} configuration options can be passed to the {@link PuppeteerCrawlerOptions.autoscaledPoolOptions}
|
|
78
|
+
* parameter of the `PuppeteerCrawler` constructor. For user convenience, the `minConcurrency` and `maxConcurrency`
|
|
79
|
+
* {@link AutoscaledPoolOptions} are available directly in the `PuppeteerCrawler` constructor.
|
|
80
|
+
*
|
|
81
|
+
* Note that the pool of Puppeteer instances is internally managed by the [BrowserPool](https://github.com/apify/browser-pool) class.
|
|
82
|
+
*
|
|
83
|
+
* **Example usage:**
|
|
84
|
+
*
|
|
85
|
+
* ```javascript
|
|
86
|
+
* const crawler = new PuppeteerCrawler({
|
|
87
|
+
* requestList,
|
|
88
|
+
* handlePageFunction: async ({ page, request }) => {
|
|
89
|
+
* // This function is called to extract data from a single web page
|
|
90
|
+
* // 'page' is an instance of Puppeteer.Page with page.goto(request.url) already called
|
|
91
|
+
* // 'request' is an instance of Request class with information about the page to load
|
|
92
|
+
* await Actor.pushData({
|
|
93
|
+
* title: await page.title(),
|
|
94
|
+
* url: request.url,
|
|
95
|
+
* succeeded: true,
|
|
96
|
+
* })
|
|
97
|
+
* },
|
|
98
|
+
* handleFailedRequestFunction: async ({ request }) => {
|
|
99
|
+
* // This function is called when the crawling of a request failed too many times
|
|
100
|
+
* await Actor.pushData({
|
|
101
|
+
* url: request.url,
|
|
102
|
+
* succeeded: false,
|
|
103
|
+
* errors: request.errorMessages,
|
|
104
|
+
* })
|
|
105
|
+
* },
|
|
106
|
+
* });
|
|
107
|
+
*
|
|
108
|
+
* await crawler.run();
|
|
109
|
+
* ```
|
|
110
|
+
* @category Crawlers
|
|
111
|
+
*/
|
|
112
|
+
export declare class PuppeteerCrawler extends BrowserCrawler<{
|
|
113
|
+
browserPlugins: [PuppeteerPlugin];
|
|
114
|
+
}, LaunchOptions, PuppeteerCrawlContext> {
|
|
115
|
+
protected static optionsShape: {
|
|
116
|
+
browserPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
117
|
+
launchContext: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
118
|
+
handlePageFunction: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
|
|
119
|
+
gotoFunction: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
|
|
120
|
+
gotoTimeoutSecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
121
|
+
navigationTimeoutSecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
122
|
+
preNavigationHooks: import("ow").ArrayPredicate<unknown> & import("ow").BasePredicate<unknown[] | undefined>;
|
|
123
|
+
postNavigationHooks: import("ow").ArrayPredicate<unknown> & import("ow").BasePredicate<unknown[] | undefined>;
|
|
124
|
+
sessionPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
125
|
+
persistCookiesPerSession: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
|
|
126
|
+
useSessionPool: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
|
|
127
|
+
proxyConfiguration: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
128
|
+
requestList: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
129
|
+
requestQueue: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
130
|
+
requestHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
|
|
131
|
+
handleRequestFunction: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
|
|
132
|
+
requestHandlerTimeoutSecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
133
|
+
handleRequestTimeoutSecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
134
|
+
failedRequestHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
|
|
135
|
+
handleFailedRequestFunction: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
|
|
136
|
+
maxRequestRetries: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
137
|
+
maxRequestsPerCrawl: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
138
|
+
autoscaledPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
139
|
+
minConcurrency: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
140
|
+
maxConcurrency: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
|
|
141
|
+
log: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* All `PuppeteerCrawler` parameters are passed via an options object.
|
|
145
|
+
*/
|
|
146
|
+
constructor(options: PuppeteerCrawlerOptions);
|
|
147
|
+
protected _navigationHandler(crawlingContext: PuppeteerCrawlContext, gotoOptions: DirectNavigationOptions): Promise<any>;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=puppeteer-crawler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"puppeteer-crawler.d.ts","sourceRoot":"","sources":["../../src/internals/puppeteer-crawler.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,cAAc,EACd,2BAA2B,EAC3B,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACd,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAAsB,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE5E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAqB,MAAM,sBAAsB,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAgB,MAAM,yBAAyB,CAAC;AAEhF,oBAAY,mBAAmB,GAAG,UAAU,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC,CAAC;AAEnF,oBAAY,qBAAqB,GAAG,sBAAsB,CAAC,IAAI,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAA;AAEnG,oBAAY,aAAa,GAAG,WAAW,CAAC,qBAAqB,EAAE,oBAAoB,CAAC,CAAC;AAErF,oBAAY,4BAA4B,GAAG,sBAAsB,CAAC,IAAI,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAA;AAE1G,oBAAY,uBAAuB,GAAG,2BAA2B,CAAC,4BAA4B,CAAC,CAAC;AAEhG,oBAAY,oBAAoB,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB,CAClE,qBAAqB,EACrB,oBAAoB,EACpB;IAAE,cAAc,EAAE,CAAC,eAAe,CAAC,CAAA;CAAE,CACxC;IACG;;OAEG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC;IAEvC;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CAAC,EAAE,aAAa,EAAE,CAAC;IAErC;;;;;;;;;;;;;;OAcG;IACH,mBAAmB,CAAC,EAAE,aAAa,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,qBAAa,gBAAiB,SAAQ,cAAc,CAAC;IAAE,cAAc,EAAE,CAAC,eAAe,CAAC,CAAA;CAAE,EAAE,aAAa,EAAE,qBAAqB,CAAC;IAC7H,iBAA0B,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;MAIpC;IAEF;;OAEG;gBACS,OAAO,EAAE,uBAAuB;cAqCnB,kBAAkB,CAAC,eAAe,EAAE,qBAAqB,EAAE,WAAW,EAAE,uBAAuB;CAQ3H"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PuppeteerCrawler = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const browser_1 = require("@crawlee/browser");
|
|
6
|
+
const ow_1 = tslib_1.__importDefault(require("ow"));
|
|
7
|
+
const puppeteer_launcher_1 = require("./puppeteer-launcher");
|
|
8
|
+
const stealth_1 = require("./stealth");
|
|
9
|
+
const puppeteer_utils_1 = require("./utils/puppeteer_utils");
|
|
10
|
+
/**
|
|
11
|
+
* Provides a simple framework for parallel crawling of web pages
|
|
12
|
+
* using headless Chrome with [Puppeteer](https://github.com/puppeteer/puppeteer).
|
|
13
|
+
* The URLs to crawl are fed either from a static list of URLs
|
|
14
|
+
* or from a dynamic queue of URLs enabling recursive crawling of websites.
|
|
15
|
+
*
|
|
16
|
+
* Since `PuppeteerCrawler` uses headless Chrome to download web pages and extract data,
|
|
17
|
+
* it is useful for crawling of websites that require to execute JavaScript.
|
|
18
|
+
* If the target website doesn't need JavaScript, consider using {@link CheerioCrawler},
|
|
19
|
+
* which downloads the pages using raw HTTP requests and is about 10x faster.
|
|
20
|
+
*
|
|
21
|
+
* The source URLs are represented using {@link Request} objects that are fed from
|
|
22
|
+
* {@link RequestList} or {@link RequestQueue} instances provided by the {@link PuppeteerCrawlerOptions.requestList}
|
|
23
|
+
* or {@link PuppeteerCrawlerOptions.requestQueue} constructor options, respectively.
|
|
24
|
+
*
|
|
25
|
+
* If both {@link PuppeteerCrawlerOptions.requestList} and {@link PuppeteerCrawlerOptions.requestQueue} are used,
|
|
26
|
+
* the instance first processes URLs from the {@link RequestList} and automatically enqueues all of them
|
|
27
|
+
* to {@link RequestQueue} before it starts their processing. This ensures that a single URL is not crawled multiple times.
|
|
28
|
+
*
|
|
29
|
+
* The crawler finishes when there are no more {@link Request} objects to crawl.
|
|
30
|
+
*
|
|
31
|
+
* `PuppeteerCrawler` opens a new Chrome page (i.e. tab) for each {@link Request} object to crawl
|
|
32
|
+
* and then calls the function provided by user as the {@link PuppeteerCrawlerOptions.handlePageFunction} option.
|
|
33
|
+
*
|
|
34
|
+
* New pages are only opened when there is enough free CPU and memory available,
|
|
35
|
+
* using the functionality provided by the {@link AutoscaledPool} class.
|
|
36
|
+
* All {@link AutoscaledPool} configuration options can be passed to the {@link PuppeteerCrawlerOptions.autoscaledPoolOptions}
|
|
37
|
+
* parameter of the `PuppeteerCrawler` constructor. For user convenience, the `minConcurrency` and `maxConcurrency`
|
|
38
|
+
* {@link AutoscaledPoolOptions} are available directly in the `PuppeteerCrawler` constructor.
|
|
39
|
+
*
|
|
40
|
+
* Note that the pool of Puppeteer instances is internally managed by the [BrowserPool](https://github.com/apify/browser-pool) class.
|
|
41
|
+
*
|
|
42
|
+
* **Example usage:**
|
|
43
|
+
*
|
|
44
|
+
* ```javascript
|
|
45
|
+
* const crawler = new PuppeteerCrawler({
|
|
46
|
+
* requestList,
|
|
47
|
+
* handlePageFunction: async ({ page, request }) => {
|
|
48
|
+
* // This function is called to extract data from a single web page
|
|
49
|
+
* // 'page' is an instance of Puppeteer.Page with page.goto(request.url) already called
|
|
50
|
+
* // 'request' is an instance of Request class with information about the page to load
|
|
51
|
+
* await Actor.pushData({
|
|
52
|
+
* title: await page.title(),
|
|
53
|
+
* url: request.url,
|
|
54
|
+
* succeeded: true,
|
|
55
|
+
* })
|
|
56
|
+
* },
|
|
57
|
+
* handleFailedRequestFunction: async ({ request }) => {
|
|
58
|
+
* // This function is called when the crawling of a request failed too many times
|
|
59
|
+
* await Actor.pushData({
|
|
60
|
+
* url: request.url,
|
|
61
|
+
* succeeded: false,
|
|
62
|
+
* errors: request.errorMessages,
|
|
63
|
+
* })
|
|
64
|
+
* },
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* await crawler.run();
|
|
68
|
+
* ```
|
|
69
|
+
* @category Crawlers
|
|
70
|
+
*/
|
|
71
|
+
class PuppeteerCrawler extends browser_1.BrowserCrawler {
|
|
72
|
+
/**
|
|
73
|
+
* All `PuppeteerCrawler` parameters are passed via an options object.
|
|
74
|
+
*/
|
|
75
|
+
constructor(options) {
|
|
76
|
+
(0, ow_1.default)(options, 'PuppeteerCrawlerOptions', ow_1.default.object.exactShape(PuppeteerCrawler.optionsShape));
|
|
77
|
+
const { launchContext = {}, browserPoolOptions = {}, proxyConfiguration, ...browserCrawlerOptions } = options;
|
|
78
|
+
const { stealth = false } = launchContext;
|
|
79
|
+
if (launchContext.proxyUrl) {
|
|
80
|
+
throw new Error('PuppeteerCrawlerOptions.launchContext.proxyUrl is not allowed in PuppeteerCrawler.'
|
|
81
|
+
+ 'Use PuppeteerCrawlerOptions.proxyConfiguration');
|
|
82
|
+
}
|
|
83
|
+
const puppeteerLauncher = new puppeteer_launcher_1.PuppeteerLauncher(launchContext);
|
|
84
|
+
browserPoolOptions.browserPlugins = [
|
|
85
|
+
puppeteerLauncher.createBrowserPlugin(),
|
|
86
|
+
];
|
|
87
|
+
super({ ...browserCrawlerOptions, proxyConfiguration, browserPoolOptions });
|
|
88
|
+
if (stealth) {
|
|
89
|
+
this.browserPool.postLaunchHooks.push(async (_pageId, browserController) => {
|
|
90
|
+
// TODO: We can do this better now. It is not necessary to override the page.
|
|
91
|
+
// we can modify the page in the postPageCreateHook
|
|
92
|
+
const { hideWebDriver, ...newStealthOptions } = puppeteerLauncher.stealthOptions;
|
|
93
|
+
(0, stealth_1.applyStealthToBrowser)(browserController.browser, newStealthOptions);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
this.launchContext = launchContext;
|
|
97
|
+
}
|
|
98
|
+
async _navigationHandler(crawlingContext, gotoOptions) {
|
|
99
|
+
if (this.gotoFunction) {
|
|
100
|
+
this.log.deprecated('PuppeteerCrawlerOptions.gotoFunction is deprecated. Use "preNavigationHooks" and "postNavigationHooks" instead.');
|
|
101
|
+
return this.gotoFunction(crawlingContext, gotoOptions);
|
|
102
|
+
}
|
|
103
|
+
return (0, puppeteer_utils_1.gotoExtended)(crawlingContext.page, crawlingContext.request, gotoOptions);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.PuppeteerCrawler = PuppeteerCrawler;
|
|
107
|
+
Object.defineProperty(PuppeteerCrawler, "optionsShape", {
|
|
108
|
+
enumerable: true,
|
|
109
|
+
configurable: true,
|
|
110
|
+
writable: true,
|
|
111
|
+
value: {
|
|
112
|
+
...browser_1.BrowserCrawler.optionsShape,
|
|
113
|
+
browserPoolOptions: ow_1.default.optional.object,
|
|
114
|
+
launchContext: ow_1.default.optional.object,
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=puppeteer-crawler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"puppeteer-crawler.js","sourceRoot":"","sources":["../../src/internals/puppeteer-crawler.ts"],"names":[],"mappings":";;;;AAAA,8CAM0B;AAK1B,oDAAoB;AAEpB,6DAAiF;AACjF,uCAAkD;AAClD,6DAAgF;AA0DhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,MAAa,gBAAiB,SAAQ,wBAA2F;IAO7H;;OAEG;IACH,YAAY,OAAgC;QACxC,IAAA,YAAE,EAAC,OAAO,EAAE,yBAAyB,EAAE,YAAE,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC;QAE5F,MAAM,EACF,aAAa,GAAG,EAAE,EAClB,kBAAkB,GAAG,EAAwB,EAC7C,kBAAkB,EAClB,GAAG,qBAAqB,EAC3B,GAAG,OAAO,CAAC;QAEZ,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,aAAa,CAAC;QAE1C,IAAI,aAAa,CAAC,QAAQ,EAAE;YACxB,MAAM,IAAI,KAAK,CAAC,oFAAoF;kBAC9F,gDAAgD,CAAC,CAAC;SAC3D;QAED,MAAM,iBAAiB,GAAG,IAAI,sCAAiB,CAAC,aAAa,CAAC,CAAC;QAE/D,kBAAkB,CAAC,cAAc,GAAG;YAChC,iBAAiB,CAAC,mBAAmB,EAAE;SAC1C,CAAC;QAEF,KAAK,CAAC,EAAE,GAAG,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAE5E,IAAI,OAAO,EAAE;YACT,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE;gBACvE,6EAA6E;gBAC7E,oDAAoD;gBACpD,MAAM,EAAE,aAAa,EAAE,GAAG,iBAAiB,EAAE,GAAG,iBAAiB,CAAC,cAAe,CAAC;gBAClF,IAAA,+BAAqB,EAAC,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;YACxE,CAAC,CAAC,CAAC;SACN;QAED,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACvC,CAAC;IAEkB,KAAK,CAAC,kBAAkB,CAAC,eAAsC,EAAE,WAAoC;QACpH,IAAI,IAAI,CAAC,YAAY,EAAE;YACnB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,iHAAiH,CAAC,CAAC;YAEvI,OAAO,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,WAAyB,CAAC,CAAC;SACxE;QACD,OAAO,IAAA,8BAAY,EAAC,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACpF,CAAC;;AAtDL,4CAuDC;AAtDG;;;;WAAyC;QACrC,GAAG,wBAAc,CAAC,YAAY;QAC9B,kBAAkB,EAAE,YAAE,CAAC,QAAQ,CAAC,MAAM;QACtC,aAAa,EAAE,YAAE,CAAC,QAAQ,CAAC,MAAM;KACpC;GAAC"}
|