@crawlee/puppeteer 4.0.0-beta.13 → 4.0.0-beta.131

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.
@@ -3,6 +3,7 @@ import { BrowserLauncher, Configuration } from '@crawlee/browser';
3
3
  import { PuppeteerPlugin } from '@crawlee/browser-pool';
4
4
  // @ts-ignore optional peer dependency or compatibility with es2022
5
5
  import type { Browser } from 'puppeteer';
6
+ import { z } from 'zod';
6
7
  /**
7
8
  * Apify extends the launch options of Puppeteer.
8
9
  * You can use any of the Puppeteer compatible
@@ -66,30 +67,38 @@ export interface PuppeteerLaunchContext extends BrowserLaunchContext<PuppeteerPl
66
67
  * @ignore
67
68
  */
68
69
  export declare class PuppeteerLauncher extends BrowserLauncher<PuppeteerPlugin, unknown> {
69
- readonly config: Configuration;
70
+ readonly configuration: Configuration;
70
71
  protected static optionsShape: {
71
72
  // @ts-ignore optional peer dependency or compatibility with es2022
72
- launcher: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
73
+ launcher: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
74
+ proxyUrl: z.ZodOptional<z.ZodURL>;
75
+ useChrome: z.ZodOptional<z.ZodBoolean>;
76
+ useIncognitoPages: z.ZodOptional<z.ZodBoolean>;
77
+ browserPerProxy: z.ZodOptional<z.ZodBoolean>;
78
+ ignoreProxyCertificate: z.ZodOptional<z.ZodBoolean>;
79
+ userDataDir: z.ZodOptional<z.ZodString>;
73
80
  // @ts-ignore optional peer dependency or compatibility with es2022
74
- proxyUrl: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
75
- // @ts-ignore optional peer dependency or compatibility with es2022
76
- useChrome: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
77
- // @ts-ignore optional peer dependency or compatibility with es2022
78
- useIncognitoPages: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
79
- // @ts-ignore optional peer dependency or compatibility with es2022
80
- browserPerProxy: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
81
- // @ts-ignore optional peer dependency or compatibility with es2022
82
- userDataDir: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
81
+ launchOptions: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
82
+ userAgent: z.ZodOptional<z.ZodString>;
83
+ };
84
+ protected static optionsSchema: z.ZodObject<{
83
85
  // @ts-ignore optional peer dependency or compatibility with es2022
84
- launchOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
86
+ launcher: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
87
+ proxyUrl: z.ZodOptional<z.ZodURL>;
88
+ useChrome: z.ZodOptional<z.ZodBoolean>;
89
+ useIncognitoPages: z.ZodOptional<z.ZodBoolean>;
90
+ browserPerProxy: z.ZodOptional<z.ZodBoolean>;
91
+ ignoreProxyCertificate: z.ZodOptional<z.ZodBoolean>;
92
+ userDataDir: z.ZodOptional<z.ZodString>;
85
93
  // @ts-ignore optional peer dependency or compatibility with es2022
86
- userAgent: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
87
- };
94
+ launchOptions: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
95
+ userAgent: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strict>;
88
97
  /**
89
98
  * All `PuppeteerLauncher` parameters are passed via an launchContext object.
90
99
  */
91
- constructor(launchContext?: PuppeteerLaunchContext, config?: Configuration);
92
- protected _getDefaultHeadlessOption(): boolean;
100
+ constructor(launchContext?: PuppeteerLaunchContext, configuration?: Configuration);
101
+ protected getDefaultHeadlessOption(): boolean;
93
102
  }
94
103
  /**
95
104
  * Launches headless Chrome using Puppeteer pre-configured to work within the Apify platform.
@@ -119,9 +128,8 @@ export declare class PuppeteerLauncher extends BrowserLauncher<PuppeteerPlugin,
119
128
  * @param [launchContext]
120
129
  * All `PuppeteerLauncher` parameters are passed via an launchContext object.
121
130
  * If you want to pass custom `puppeteer.launch(options)` options you can use the `PuppeteerLaunchContext.launchOptions` property.
122
- * @param [config]
131
+ * @param [configuration]
123
132
  * @returns
124
133
  * Promise that resolves to Puppeteer's `Browser` instance.
125
134
  */
126
- export declare function launchPuppeteer(launchContext?: PuppeteerLaunchContext, config?: Configuration): Promise<Browser>;
127
- //# sourceMappingURL=puppeteer-launcher.d.ts.map
135
+ export declare function launchPuppeteer(launchContext?: PuppeteerLaunchContext, configuration?: Configuration): Promise<Browser>;
@@ -1,31 +1,31 @@
1
- import { BrowserLauncher, Configuration } from '@crawlee/browser';
1
+ import { BrowserLauncher, Configuration, parseArgument, schemas } from '@crawlee/browser';
2
2
  import { PuppeteerPlugin } from '@crawlee/browser-pool';
3
- import ow from 'ow';
3
+ import { z } from 'zod';
4
4
  /**
5
5
  * `PuppeteerLauncher` is based on the `BrowserLauncher`. It launches `puppeteer` browser instance.
6
6
  * @ignore
7
7
  */
8
8
  export class PuppeteerLauncher extends BrowserLauncher {
9
- config;
9
+ configuration;
10
10
  static optionsShape = {
11
11
  ...BrowserLauncher.optionsShape,
12
- launcher: ow.optional.object,
12
+ launcher: schemas.anyObject.optional(),
13
13
  };
14
+ static optionsSchema = z.strictObject(PuppeteerLauncher.optionsShape);
14
15
  /**
15
16
  * All `PuppeteerLauncher` parameters are passed via an launchContext object.
16
17
  */
17
- constructor(launchContext = {}, config = Configuration.getGlobalConfig()) {
18
- ow(launchContext, 'PuppeteerLauncher', ow.object.exactShape(PuppeteerLauncher.optionsShape));
19
- const { launcher = BrowserLauncher.requireLauncherOrThrow('puppeteer', 'apify/actor-node-puppeteer-chrome'), ...browserLauncherOptions } = launchContext;
18
+ constructor(launchContext = {}, configuration = Configuration.getGlobalConfiguration()) {
19
+ const { launcher = BrowserLauncher.requireLauncherOrThrow('puppeteer', 'apify/actor-node-puppeteer-chrome'), ...browserLauncherOptions } = parseArgument(launchContext, PuppeteerLauncher.optionsSchema, 'PuppeteerLaunchContext');
20
20
  super({
21
21
  ...browserLauncherOptions,
22
22
  launcher,
23
- }, config);
24
- this.config = config;
23
+ }, configuration);
24
+ this.configuration = configuration;
25
25
  this.Plugin = PuppeteerPlugin;
26
26
  }
27
- _getDefaultHeadlessOption() {
28
- const headless = super._getDefaultHeadlessOption();
27
+ getDefaultHeadlessOption() {
28
+ const headless = super.getDefaultHeadlessOption();
29
29
  return headless ? 'new' : headless;
30
30
  }
31
31
  }
@@ -57,12 +57,11 @@ export class PuppeteerLauncher extends BrowserLauncher {
57
57
  * @param [launchContext]
58
58
  * All `PuppeteerLauncher` parameters are passed via an launchContext object.
59
59
  * If you want to pass custom `puppeteer.launch(options)` options you can use the `PuppeteerLaunchContext.launchOptions` property.
60
- * @param [config]
60
+ * @param [configuration]
61
61
  * @returns
62
62
  * Promise that resolves to Puppeteer's `Browser` instance.
63
63
  */
64
- export async function launchPuppeteer(launchContext, config = Configuration.getGlobalConfig()) {
65
- const puppeteerLauncher = new PuppeteerLauncher(launchContext, config);
64
+ export async function launchPuppeteer(launchContext, configuration = Configuration.getGlobalConfiguration()) {
65
+ const puppeteerLauncher = new PuppeteerLauncher(launchContext, configuration);
66
66
  return puppeteerLauncher.launch();
67
67
  }
68
- //# sourceMappingURL=puppeteer-launcher.js.map
@@ -58,4 +58,3 @@ export declare function addInterceptRequestHandler(page: Page, handler: Intercep
58
58
  * @param handler Request interception handler.
59
59
  */
60
60
  export declare function removeInterceptRequestHandler(page: Page, handler: InterceptHandler): Promise<void>;
61
- //# sourceMappingURL=puppeteer_request_interception.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import ow from 'ow';
3
- import log from '@apify/log';
2
+ import { parseArgument, schemas, serviceLocator } from '@crawlee/browser';
3
+ const pageSchema = schemas.objectWithKeys(['goto', 'evaluate']);
4
4
  // We use weak maps here so that the content gets discarded after page gets closed.
5
5
  const pageInterceptRequestHandlersMap = new WeakMap(); // Maps page to an array of request interception handlers.
6
6
  const pageInterceptRequestMasterHandlerMap = new WeakMap(); // Maps page to master request interception handler.
@@ -137,8 +137,8 @@ async function handleRequest(request, interceptRequestHandlers) {
137
137
  * @param handler Request interception handler.
138
138
  */
139
139
  export async function addInterceptRequestHandler(page, handler) {
140
- ow(page, ow.object.hasKeys('goto', 'evaluate'));
141
- ow(handler, ow.function);
140
+ parseArgument(page, pageSchema);
141
+ parseArgument(handler, schemas.anyFunction);
142
142
  if (!pageInterceptRequestHandlersMap.has(page)) {
143
143
  pageInterceptRequestHandlersMap.set(page, []);
144
144
  }
@@ -174,8 +174,8 @@ export async function addInterceptRequestHandler(page, handler) {
174
174
  * @param handler Request interception handler.
175
175
  */
176
176
  export async function removeInterceptRequestHandler(page, handler) {
177
- ow(page, ow.object.hasKeys('goto', 'evaluate'));
178
- ow(handler, ow.function);
177
+ parseArgument(page, pageSchema);
178
+ parseArgument(handler, schemas.anyFunction);
179
179
  const handlersArray = pageInterceptRequestHandlersMap.get(page).filter((item) => item !== handler);
180
180
  pageInterceptRequestHandlersMap.set(page, handlersArray);
181
181
  if (handlersArray.length === 0) {
@@ -194,7 +194,7 @@ export async function removeInterceptRequestHandler(page, handler) {
194
194
  interceptedRequestsInProgress.removeListener('delete', onDelete);
195
195
  }
196
196
  catch (error) {
197
- log.debug('Error while disabling request interception', { error });
197
+ serviceLocator.getLogger().debug('Error while disabling request interception', { error });
198
198
  }
199
199
  }
200
200
  };
@@ -207,4 +207,3 @@ async function disableRequestInterception(page) {
207
207
  const requestHandler = pageInterceptRequestMasterHandlerMap.get(page);
208
208
  page.off('request', requestHandler);
209
209
  }
210
- //# sourceMappingURL=puppeteer_request_interception.js.map
@@ -20,7 +20,7 @@
20
20
  import type { Request } from '@crawlee/browser';
21
21
  import { Configuration } from '@crawlee/browser';
22
22
  import type { BatchAddRequestsResult, Dictionary } from '@crawlee/types';
23
- import { type CheerioRoot } from '@crawlee/utils';
23
+ import { type CheerioRoot } from '@crawlee/utils/internal';
24
24
  import type { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping.js';
25
25
  // @ts-ignore optional peer dependency or compatibility with es2022
26
26
  import type { HTTPResponse, Page, ResponseForRequest } from 'puppeteer';
@@ -305,9 +305,9 @@ export interface SaveSnapshotOptions {
305
305
  keyValueStoreName?: string | null;
306
306
  /**
307
307
  * Configuration of the crawler that will be used to save the snapshot.
308
- * @default Configuration.getGlobalConfig()
308
+ * @default Configuration.getGlobalConfiguration()
309
309
  */
310
- config?: Configuration;
310
+ configuration?: Configuration;
311
311
  }
312
312
  /**
313
313
  * Saves a full screenshot and HTML of the current page into a Key-Value store.
@@ -316,7 +316,6 @@ export interface SaveSnapshotOptions {
316
316
  */
317
317
  export declare function saveSnapshot(page: Page, options?: SaveSnapshotOptions): Promise<void>;
318
318
  export declare function closeCookieModals(page: Page): Promise<void>;
319
- /** @internal */
320
319
  export interface PuppeteerContextUtils {
321
320
  /**
322
321
  * Injects a JavaScript file into current `page`.
@@ -388,8 +387,7 @@ export interface PuppeteerContextUtils {
388
387
  * in `href` elements, but rather navigations are triggered in click handlers.
389
388
  * If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
390
389
  *
391
- * Optionally, the function allows you to filter the target links' URLs using an array of {@link PseudoUrl} objects
392
- * and override settings of the enqueued {@link Request} objects.
390
+ * Optionally, the function allows you to filter the target links' URLs using an array of glob or regexp patterns.
393
391
  *
394
392
  * **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
395
393
  * such as changing the Z-index of elements being clicked and their visibility. Therefore,
@@ -410,9 +408,9 @@ export interface PuppeteerContextUtils {
410
408
  * async requestHandler({ enqueueLinksByClickingElements }) {
411
409
  * await enqueueLinksByClickingElements({
412
410
  * selector: 'a.product-detail',
413
- * globs: [
414
- * 'https://www.example.com/handbags/**'
415
- * 'https://www.example.com/purses/**'
411
+ * include: [
412
+ * 'https://www.example.com/handbags/**',
413
+ * 'https://www.example.com/purses/**',
416
414
  * ],
417
415
  * });
418
416
  * });
@@ -420,7 +418,7 @@ export interface PuppeteerContextUtils {
420
418
  *
421
419
  * @returns Promise that resolves to {@link BatchAddRequestsResult} object.
422
420
  */
423
- enqueueLinksByClickingElements(options: Omit<EnqueueLinksByClickingElementsOptions, 'page' | 'requestQueue'>): Promise<BatchAddRequestsResult>;
421
+ enqueueLinksByClickingElements(options: Omit<EnqueueLinksByClickingElementsOptions, 'page' | 'requestManager'>): Promise<BatchAddRequestsResult>;
424
422
  /**
425
423
  * Forces the Puppeteer browser tab to block loading URLs that match a provided pattern.
426
424
  * This is useful to speed up crawling of websites, since it reduces the amount
@@ -552,6 +550,15 @@ export interface PuppeteerContextUtils {
552
550
  saveSnapshot(options?: SaveSnapshotOptions): Promise<void>;
553
551
  /**
554
552
  * Tries to close cookie consent modals on the page. Based on the I Don't Care About Cookies browser extension.
553
+ *
554
+ * Note that this method requires the idcac-playwright package to be installed.
555
+ * Crawlee does not include it by default due to licensing issues.
556
+ *
557
+ * To use this method, please install the package manually by running:
558
+ *
559
+ * ```bash
560
+ * npm install idcac-playwright
561
+ * ```
555
562
  */
556
563
  closeCookieModals(): Promise<void>;
557
564
  }
@@ -571,4 +578,3 @@ export declare const puppeteerUtils: {
571
578
  parseWithCheerio: typeof parseWithCheerio;
572
579
  closeCookieModals: typeof closeCookieModals;
573
580
  };
574
- //# sourceMappingURL=puppeteer_utils.d.ts.map
@@ -20,20 +20,48 @@
20
20
  import { readFile } from 'node:fs/promises';
21
21
  import { createRequire } from 'node:module';
22
22
  import vm from 'node:vm';
23
- import { Configuration, KeyValueStore, validators } from '@crawlee/browser';
23
+ import { Configuration, KeyValueStore, parseArgument, schemas, serviceLocator, validators } from '@crawlee/browser';
24
24
  import { expandShadowRoots, sleep } from '@crawlee/utils';
25
- import * as cheerio from 'cheerio';
26
- import { getInjectableScript } from 'idcac-playwright';
27
- import ow from 'ow';
25
+ import { z } from 'zod';
28
26
  import { LruCache } from '@apify/datastructures';
29
- import log_ from '@apify/log';
30
27
  import { enqueueLinksByClickingElements } from '../enqueue-links/click-elements.js';
31
28
  import { addInterceptRequestHandler, removeInterceptRequestHandler } from './puppeteer_request_interception.js';
32
29
  const require = createRequire(import.meta.url);
33
30
  const jqueryPath = require.resolve('jquery');
34
31
  const MAX_INJECT_FILE_CACHE_SIZE = 10;
35
32
  const DEFAULT_BLOCK_REQUEST_URL_PATTERNS = ['.css', '.jpg', '.jpeg', '.png', '.svg', '.gif', '.woff', '.pdf', '.zip'];
36
- const log = log_.child({ prefix: 'Puppeteer Utils' });
33
+ const filePathSchema = z.string();
34
+ const injectFileOptionsSchema = z.strictObject({
35
+ surviveNavigations: z.boolean().optional(),
36
+ });
37
+ const responseUrlRulesSchema = schemas.arrayOf(z.union([z.string(), z.instanceof(RegExp)]), 'strings or RegExps');
38
+ const gotoExtendedRequestSchema = z.looseObject({
39
+ url: z.url(),
40
+ method: z.string().optional(),
41
+ headers: schemas.anyObject.optional(),
42
+ payload: z.union([z.string(), z.instanceof(Uint8Array)]).optional(),
43
+ });
44
+ const blockRequestsOptionsSchema = z.strictObject({
45
+ urlPatterns: schemas.arrayOf(z.string(), 'strings').default(DEFAULT_BLOCK_REQUEST_URL_PATTERNS),
46
+ extraUrlPatterns: schemas.arrayOf(z.string(), 'strings').default(() => []),
47
+ });
48
+ const infiniteScrollOptionsSchema = z.strictObject({
49
+ timeoutSecs: schemas.anyNumber.default(0),
50
+ maxScrollHeight: schemas.anyNumber.default(0),
51
+ waitForSecs: schemas.anyNumber.default(4),
52
+ scrollDownAndUp: z.boolean().default(false),
53
+ buttonSelector: z.string().optional(),
54
+ stopScrollCallback: schemas.anyFunction.optional(),
55
+ });
56
+ const saveSnapshotOptionsSchema = z.strictObject({
57
+ key: z.string().min(1).default('SNAPSHOT'),
58
+ screenshotQuality: schemas.anyNumber.default(50),
59
+ saveScreenshot: z.boolean().default(true),
60
+ saveHtml: z.boolean().default(true),
61
+ keyValueStoreName: z.string().optional(),
62
+ configuration: schemas.anyObject.optional(),
63
+ });
64
+ const getLog = () => serviceLocator.getChildLog('Puppeteer Utils');
37
65
  /**
38
66
  * Cache contents of previously injected files to limit file system access.
39
67
  */
@@ -50,21 +78,19 @@ const injectedFilesCache = new LruCache({ maxLength: MAX_INJECT_FILE_CACHE_SIZE
50
78
  * @param [options]
51
79
  */
52
80
  export async function injectFile(page, filePath, options = {}) {
53
- ow(page, ow.object.validate(validators.browserPage));
54
- ow(filePath, ow.string);
55
- ow(options, ow.object.exactShape({
56
- surviveNavigations: ow.optional.boolean,
57
- }));
81
+ parseArgument(page, validators.browserPage);
82
+ parseArgument(filePath, filePathSchema);
83
+ const { surviveNavigations } = parseArgument(options, injectFileOptionsSchema);
58
84
  let contents = injectedFilesCache.get(filePath);
59
85
  if (!contents) {
60
86
  contents = await readFile(filePath, 'utf8');
61
87
  injectedFilesCache.add(filePath, contents);
62
88
  }
63
89
  const evalP = page.evaluate(contents);
64
- if (options.surviveNavigations) {
90
+ if (surviveNavigations) {
65
91
  page.on('framenavigated', async () => page
66
92
  .evaluate(contents)
67
- .catch((error) => log.warning('An error occurred during the script injection!', { error })));
93
+ .catch((error) => getLog().warning('An error occurred during the script injection!', { error })));
68
94
  }
69
95
  return evalP;
70
96
  }
@@ -95,7 +121,7 @@ export async function injectFile(page, filePath, options = {}) {
95
121
  * @param [options.surviveNavigations] Opt-out option to disable the JQuery reinjection after navigation.
96
122
  */
97
123
  export async function injectJQuery(page, options) {
98
- ow(page, ow.object.validate(validators.browserPage));
124
+ parseArgument(page, validators.browserPage);
99
125
  return injectFile(page, jqueryPath, { surviveNavigations: options?.surviveNavigations ?? true });
100
126
  }
101
127
  /**
@@ -111,14 +137,22 @@ export async function injectJQuery(page, options) {
111
137
  * @param ignoreShadowRoots
112
138
  */
113
139
  export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIframes = false) {
114
- ow(page, ow.object.validate(validators.browserPage));
140
+ parseArgument(page, validators.browserPage);
115
141
  if (page.frames().length > 1 && !ignoreIframes) {
116
142
  const frames = await page.$$('iframe');
117
143
  await Promise.all(frames.map(async (frame) => {
118
144
  try {
119
145
  const iframe = await frame.contentFrame();
120
146
  if (iframe) {
121
- const contents = await iframe.content();
147
+ const getIframeHTML = async () => {
148
+ try {
149
+ return iframe.$eval('body', (el) => el.innerHTML);
150
+ }
151
+ catch {
152
+ return iframe.content();
153
+ }
154
+ };
155
+ const contents = await getIframeHTML();
122
156
  await frame.evaluate((f, c) => {
123
157
  const replacementNode = document.createElement('div');
124
158
  replacementNode.innerHTML = c;
@@ -128,7 +162,7 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
128
162
  }
129
163
  }
130
164
  catch (error) {
131
- log.warning(`Failed to extract iframe content: ${error}`);
165
+ getLog().warning(`Failed to extract iframe content: ${error}`);
132
166
  }
133
167
  }));
134
168
  }
@@ -136,7 +170,8 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
136
170
  ? null
137
171
  : (await page.evaluate(`(${expandShadowRoots.toString()})(document)`));
138
172
  const pageContent = html || (await page.content());
139
- return cheerio.load(pageContent);
173
+ const { load } = await import('cheerio');
174
+ return load(pageContent);
140
175
  }
141
176
  /**
142
177
  * Forces the Puppeteer browser tab to block loading URLs that match a provided pattern.
@@ -181,12 +216,8 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
181
216
  * @param [options]
182
217
  */
183
218
  export async function blockRequests(page, options = {}) {
184
- ow(page, ow.object.validate(validators.browserPage));
185
- ow(options, ow.object.exactShape({
186
- urlPatterns: ow.optional.array.ofType(ow.string),
187
- extraUrlPatterns: ow.optional.array.ofType(ow.string),
188
- }));
189
- const { urlPatterns = DEFAULT_BLOCK_REQUEST_URL_PATTERNS, extraUrlPatterns = [] } = options;
219
+ parseArgument(page, validators.browserPage);
220
+ const { urlPatterns, extraUrlPatterns } = parseArgument(options, blockRequestsOptionsSchema);
190
221
  const patternsToBlock = [...urlPatterns, ...extraUrlPatterns];
191
222
  // We use CDP commands instead of request interception as the latter disables caching, which is not ideal
192
223
  await sendCDPCommand(page, 'Network.setBlockedURLs', { urls: patternsToBlock });
@@ -214,7 +245,9 @@ export async function sendCDPCommand(page, command, ...args) {
214
245
  * @deprecated
215
246
  */
216
247
  export const blockResources = async (page, resourceTypes = ['stylesheet', 'font', 'image', 'media']) => {
217
- log.deprecated('utils.puppeteer.blockResources() has a high impact on performance in recent versions of Puppeteer. ' +
248
+ serviceLocator
249
+ .getLogger()
250
+ .deprecated('utils.puppeteer.blockResources() has a high impact on performance in recent versions of Puppeteer. ' +
218
251
  'Until this resolves, please use utils.puppeteer.blockRequests()');
219
252
  await addInterceptRequestHandler(page, async (request) => {
220
253
  const type = request.resourceType();
@@ -241,10 +274,12 @@ export const blockResources = async (page, resourceTypes = ['stylesheet', 'font'
241
274
  * @deprecated
242
275
  */
243
276
  export async function cacheResponses(page, cache, responseUrlRules) {
244
- ow(page, ow.object.validate(validators.browserPage));
245
- ow(cache, ow.object);
246
- ow(responseUrlRules, ow.array.ofType(ow.any(ow.string, ow.regExp)));
247
- log.deprecated('utils.puppeteer.cacheResponses() has a high impact on performance ' +
277
+ parseArgument(page, validators.browserPage);
278
+ parseArgument(cache, schemas.anyObject);
279
+ parseArgument(responseUrlRules, responseUrlRulesSchema);
280
+ serviceLocator
281
+ .getLogger()
282
+ .deprecated('utils.puppeteer.cacheResponses() has a high impact on performance ' +
248
283
  "in recent versions of Puppeteer so it's use is discouraged until this issue resolves.");
249
284
  await addInterceptRequestHandler(page, async (request) => {
250
285
  const url = request.url();
@@ -314,7 +349,7 @@ export function compileScript(scriptString, context = Object.create(null)) {
314
349
  func = vm.runInNewContext(funcString, context); // "Secure" the context by removing prototypes, unless custom context is provided.
315
350
  }
316
351
  catch (err) {
317
- log.exception(err, 'Cannot compile script!');
352
+ getLog().exception(err, 'Cannot compile script!');
318
353
  throw err;
319
354
  }
320
355
  if (typeof func !== 'function')
@@ -334,23 +369,20 @@ export function compileScript(scriptString, context = Object.create(null)) {
334
369
  * @param [gotoOptions] Custom options for `page.goto()`.
335
370
  */
336
371
  export async function gotoExtended(page, request, gotoOptions = {}) {
337
- ow(page, ow.object.validate(validators.browserPage));
338
- ow(request, ow.object.partialShape({
339
- url: ow.string.url,
340
- method: ow.optional.string,
341
- headers: ow.optional.object,
342
- payload: ow.optional.any(ow.string, ow.uint8Array),
343
- }));
344
- ow(gotoOptions, ow.object);
372
+ parseArgument(page, validators.browserPage);
373
+ parseArgument(request, gotoExtendedRequestSchema);
374
+ parseArgument(gotoOptions, schemas.anyObject);
345
375
  gotoOptions = { ...gotoOptions };
346
376
  if (gotoOptions.waitUntil === 'networkidle') {
347
377
  gotoOptions.waitUntil = 'networkidle0';
348
378
  }
349
379
  const { url, method, headers, payload } = request;
350
380
  const isEmpty = (o) => !o || Object.keys(o).length === 0;
351
- if (method !== 'GET' || payload || !isEmpty(headers)) {
381
+ if (method !== 'GET' || payload) {
352
382
  // This is not deprecated, we use it to log only once.
353
- log.deprecated('Using other request methods than GET, rewriting headers and adding payloads has a high impact on performance ' +
383
+ serviceLocator
384
+ .getLogger()
385
+ .deprecated('Using other request methods than GET, rewriting headers and adding payloads has a high impact on performance ' +
354
386
  'in recent versions of Puppeteer. Use only when necessary.');
355
387
  let wasCalled = false;
356
388
  const interceptRequestHandler = async (interceptedRequest) => {
@@ -373,6 +405,19 @@ export async function gotoExtended(page, request, gotoOptions = {}) {
373
405
  };
374
406
  await addInterceptRequestHandler(page, interceptRequestHandler);
375
407
  }
408
+ else if (!isEmpty(headers)) {
409
+ const extraHeaders = { ...headers };
410
+ // Chrome bundled with Puppeteer 25+ ignores a `User-Agent` passed via `setExtraHTTPHeaders()`, it has to be set explicitly.
411
+ for (const name of Object.keys(extraHeaders)) {
412
+ if (name.toLowerCase() === 'user-agent') {
413
+ await page.setUserAgent(extraHeaders[name]);
414
+ delete extraHeaders[name];
415
+ }
416
+ }
417
+ if (!isEmpty(extraHeaders)) {
418
+ await page.setExtraHTTPHeaders(extraHeaders);
419
+ }
420
+ }
376
421
  return page.goto(url, gotoOptions);
377
422
  }
378
423
  /**
@@ -382,16 +427,8 @@ export async function gotoExtended(page, request, gotoOptions = {}) {
382
427
  * @param [options]
383
428
  */
384
429
  export async function infiniteScroll(page, options = {}) {
385
- ow(page, ow.object.validate(validators.browserPage));
386
- ow(options, ow.object.exactShape({
387
- timeoutSecs: ow.optional.number,
388
- maxScrollHeight: ow.optional.number,
389
- waitForSecs: ow.optional.number,
390
- scrollDownAndUp: ow.optional.boolean,
391
- buttonSelector: ow.optional.string,
392
- stopScrollCallback: ow.optional.function,
393
- }));
394
- const { timeoutSecs = 0, maxScrollHeight = 0, waitForSecs = 4, scrollDownAndUp = false, buttonSelector, stopScrollCallback, } = options;
430
+ parseArgument(page, validators.browserPage);
431
+ const { timeoutSecs, maxScrollHeight, waitForSecs, scrollDownAndUp, buttonSelector, stopScrollCallback } = parseArgument(options, infiniteScrollOptionsSchema);
395
432
  let finished;
396
433
  const startTime = Date.now();
397
434
  const CHECK_INTERVAL_MILLIS = 1000;
@@ -483,19 +520,11 @@ export async function infiniteScroll(page, options = {}) {
483
520
  * @param [options]
484
521
  */
485
522
  export async function saveSnapshot(page, options = {}) {
486
- ow(page, ow.object.validate(validators.browserPage));
487
- ow(options, ow.object.exactShape({
488
- key: ow.optional.string.nonEmpty,
489
- screenshotQuality: ow.optional.number,
490
- saveScreenshot: ow.optional.boolean,
491
- saveHtml: ow.optional.boolean,
492
- keyValueStoreName: ow.optional.string,
493
- config: ow.optional.object,
494
- }));
495
- const { key = 'SNAPSHOT', screenshotQuality = 50, saveScreenshot = true, saveHtml = true, keyValueStoreName, config, } = options;
523
+ parseArgument(page, validators.browserPage);
524
+ const { key, screenshotQuality, saveScreenshot, saveHtml, keyValueStoreName, configuration } = parseArgument(options, saveSnapshotOptionsSchema);
496
525
  try {
497
- const store = await KeyValueStore.open(keyValueStoreName, {
498
- config: config ?? Configuration.getGlobalConfig(),
526
+ const store = await KeyValueStore.open(keyValueStoreName ? { name: keyValueStoreName } : null, {
527
+ configuration: configuration ?? Configuration.getGlobalConfiguration(),
499
528
  });
500
529
  if (saveScreenshot) {
501
530
  const screenshotName = `${key}.jpg`;
@@ -516,8 +545,34 @@ export async function saveSnapshot(page, options = {}) {
516
545
  throw new Error(`saveSnapshot with key ${key} failed.\nCause:${err.message}`);
517
546
  }
518
547
  }
548
+ let idcacPlaywright = null;
549
+ async function getIdcacPlaywright() {
550
+ if (idcacPlaywright)
551
+ return idcacPlaywright;
552
+ try {
553
+ idcacPlaywright = await import('idcac-playwright');
554
+ }
555
+ catch (error) {
556
+ getLog().warning(`Failed to import 'idcac-playwright'.
557
+
558
+ We recently made idcac-playwright an optional dependency due to licensing issues.
559
+ To use this feature, please install it manually by running
560
+
561
+ npm install idcac-playwright
562
+
563
+ Original error message follows:
564
+
565
+ ${error.message}
566
+ `);
567
+ }
568
+ return idcacPlaywright;
569
+ }
519
570
  export async function closeCookieModals(page) {
520
- await page.evaluate(getInjectableScript());
571
+ parseArgument(page, validators.browserPage);
572
+ const idcac = await getIdcacPlaywright();
573
+ if (idcac?.getInjectableScript()) {
574
+ await page.evaluate(idcac.getInjectableScript());
575
+ }
521
576
  }
522
577
  export { enqueueLinksByClickingElements, addInterceptRequestHandler, removeInterceptRequestHandler };
523
578
  /** @internal */
@@ -535,4 +590,3 @@ export const puppeteerUtils = {
535
590
  parseWithCheerio,
536
591
  closeCookieModals,
537
592
  };
538
- //# sourceMappingURL=puppeteer_utils.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/puppeteer",
3
- "version": "4.0.0-beta.13",
3
+ "version": "4.0.0-beta.131",
4
4
  "description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "homepage": "https://crawlee.dev",
40
40
  "scripts": {
41
- "build": "yarn clean && yarn compile && yarn copy",
41
+ "build": "pnpm clean && pnpm compile && pnpm copy",
42
42
  "clean": "rimraf ./dist",
43
43
  "compile": "tsc -p tsconfig.build.json",
44
44
  "copy": "tsx ../../scripts/copy.ts"
@@ -48,22 +48,25 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@apify/datastructures": "^2.0.3",
51
- "@apify/log": "^2.5.18",
52
- "@crawlee/browser": "4.0.0-beta.13",
53
- "@crawlee/browser-pool": "4.0.0-beta.13",
54
- "@crawlee/types": "4.0.0-beta.13",
55
- "@crawlee/utils": "4.0.0-beta.13",
51
+ "@crawlee/browser": "4.0.0-beta.131",
52
+ "@crawlee/browser-pool": "4.0.0-beta.131",
53
+ "@crawlee/core": "4.0.0-beta.131",
54
+ "@crawlee/types": "4.0.0-beta.131",
55
+ "@crawlee/utils": "4.0.0-beta.131",
56
56
  "cheerio": "^1.0.0",
57
57
  "devtools-protocol": "*",
58
- "idcac-playwright": "^0.1.3",
59
58
  "jquery": "^3.7.1",
60
- "ow": "^2.0.0",
61
- "tslib": "^2.8.1"
59
+ "tslib": "^2.8.1",
60
+ "zod": "^4.4.3"
62
61
  },
63
62
  "peerDependencies": {
63
+ "idcac-playwright": "^0.2.0",
64
64
  "puppeteer": "*"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
+ "idcac-playwright": {
68
+ "optional": true
69
+ },
67
70
  "puppeteer": {
68
71
  "optional": true
69
72
  }
@@ -75,5 +78,5 @@
75
78
  }
76
79
  }
77
80
  },
78
- "gitHead": "24df7c8dba6b6c9623c02ccc655d8ee748f003e3"
81
+ "gitHead": "de741c0b63d0da061e4ffcd6ce6fdc0c92401399"
79
82
  }
package/index.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAElD,OAAO,KAAK,4BAA4B,MAAM,qDAAqD,CAAC;AACpG,YAAY,EAAE,gBAAgB,EAAE,MAAM,qDAAqD,CAAC;AAE5F,OAAO,KAAK,cAAc,MAAM,sCAAsC,CAAC;AACvE,YAAY,EACR,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,IAAI,gCAAgC,EAC3D,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAC;AACtF,YAAY,EAAE,qCAAqC,EAAE,MAAM,6CAA6C,CAAC"}
package/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAElD,OAAO,KAAK,4BAA4B,MAAM,qDAAqD,CAAC;AAGpG,OAAO,KAAK,cAAc,MAAM,sCAAsC,CAAC;AAWvE,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAC"}