@crawlee/puppeteer 4.0.0-beta.99 → 4.0.0-rc.0

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from '@crawlee/browser';
2
+ export * from './internals/puppeteer-browser-pool.js';
2
3
  export * from './internals/puppeteer-crawler.js';
3
4
  export * from './internals/puppeteer-launcher.js';
4
5
  export * as puppeteerRequestInterception from './internals/utils/puppeteer_request_interception.js';
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from '@crawlee/browser';
2
+ export * from './internals/puppeteer-browser-pool.js';
2
3
  export * from './internals/puppeteer-crawler.js';
3
4
  export * from './internals/puppeteer-launcher.js';
4
5
  export * as puppeteerRequestInterception from './internals/utils/puppeteer_request_interception.js';
@@ -1,4 +1,4 @@
1
- import type { GlobInput, IRequestManager, PseudoUrlInput, RegExpInput, RequestTransform, SkippedRequestCallback } from '@crawlee/browser';
1
+ import type { IRequestManager, RequestTransform, SkippedRequestCallback, UrlPatternInput } from '@crawlee/browser';
2
2
  import type { BatchAddRequestsResult, Dictionary } from '@crawlee/types';
3
3
  // @ts-ignore optional peer dependency or compatibility with es2022
4
4
  import type { ClickOptions, Page, Target } from 'puppeteer';
@@ -25,64 +25,29 @@ export interface EnqueueLinksByClickingElementsOptions {
25
25
  */
26
26
  clickOptions?: ClickOptions;
27
27
  /**
28
- * An array of glob pattern strings or plain objects
29
- * containing glob pattern strings matching the URLs to be enqueued.
28
+ * An array of URL patterns that URLs must match to be enqueued.
30
29
  *
31
- * The plain objects must include at least the `glob` property, which holds the glob pattern string.
32
- * All remaining keys will be used as request options for the corresponding enqueued {@link Request} objects.
33
- *
34
- * The matching is always case-insensitive.
35
- * If you need case-sensitive matching, use `regexps` property directly.
36
- *
37
- * If `globs` is an empty array or `undefined`, then the function
38
- * enqueues all the intercepted navigation requests produced by the page
39
- * after clicking on elements matching the provided CSS selector.
40
- */
41
- globs?: GlobInput[];
42
- /**
43
- * An array of glob pattern strings, regexp patterns or plain objects
44
- * containing patterns matching URLs that will **never** be enqueued.
45
- *
46
- * The plain objects must include either the `glob` property or the `regexp` property.
30
+ * Accepts glob pattern strings, `{ glob: string }` objects, `RegExp` instances, or `{ regexp: RegExp }` objects.
47
31
  *
48
32
  * Glob matching is always case-insensitive.
49
- * If you need case-sensitive matching, provide a regexp.
50
- */
51
- exclude?: readonly (GlobInput | RegExpInput)[];
52
- /**
53
- * An array of regular expressions or plain objects
54
- * containing regular expressions matching the URLs to be enqueued.
55
- *
56
- * The plain objects must include at least the `regexp` property, which holds the regular expression.
57
- * All remaining keys will be used as request options for the corresponding enqueued {@link Request} objects.
33
+ * If you need case-sensitive matching, use a `RegExp`.
58
34
  *
59
- * If `regexps` is an empty array or `undefined`, then the function
35
+ * If `include` is an empty array or `undefined`, then the function
60
36
  * enqueues all the intercepted navigation requests produced by the page
61
37
  * after clicking on elements matching the provided CSS selector.
62
38
  */
63
- regexps?: RegExpInput[];
39
+ include?: UrlPatternInput[];
64
40
  /**
65
- * *NOTE:* In future versions of SDK the options will be removed.
66
- * Please use `globs` or `regexps` instead.
67
- *
68
- * An array of {@link PseudoUrl} strings or plain objects
69
- * containing {@link PseudoUrl} strings matching the URLs to be enqueued.
70
- *
71
- * The plain objects must include at least the `purl` property, which holds the pseudo-URL pattern string.
72
- * All remaining keys will be used as request options for the corresponding enqueued {@link Request} objects.
41
+ * An array of URL patterns. Matching URLs will **not** be enqueued.
73
42
  *
74
- * With a pseudo-URL string, the matching is always case-insensitive.
75
- * If you need case-sensitive matching, use `regexps` property directly.
43
+ * Accepts glob pattern strings, `{ glob: string }` objects, `RegExp` instances, or `{ regexp: RegExp }` objects.
76
44
  *
77
- * If `pseudoUrls` is an empty array or `undefined`, then the function
78
- * enqueues all the intercepted navigation requests produced by the page
79
- * after clicking on elements matching the provided CSS selector.
80
- *
81
- * @deprecated prefer using `globs` or `regexps` instead
45
+ * Glob matching is always case-insensitive.
46
+ * If you need case-sensitive matching, use a `RegExp`.
82
47
  */
83
- pseudoUrls?: PseudoUrlInput[];
48
+ exclude?: readonly UrlPatternInput[];
84
49
  /**
85
- * After {@link Request} objects are constructed and filtered by URL patterns (`globs`, `regexps`, `pseudoUrls`),
50
+ * After request options are filtered by `include`/`exclude` patterns,
86
51
  * this function can be used to remove them or modify their contents such as `userData`, `payload` or, most importantly
87
52
  * `uniqueKey`. This is useful when you need to enqueue multiple `Requests` to the queue that share the same URL,
88
53
  * but differ in methods or payloads, or to dynamically update or create `userData`.
@@ -97,8 +62,8 @@ export interface EnqueueLinksByClickingElementsOptions {
97
62
  * }
98
63
  * ```
99
64
  *
100
- * Note that `transformRequestFunction` has the highest priority and can overwrite request options
101
- * specified in `globs`, `regexps`, or `pseudoUrls` objects, as well as the global `label` option.
65
+ * Note that `transformRequestFunction` has the highest priority and can overwrite
66
+ * the global `label` option.
102
67
  *
103
68
  * The function receives a {@link RequestOptions} object and can return either:
104
69
  * - The modified {@link RequestOptions} object
@@ -159,8 +124,7 @@ export interface EnqueueLinksByClickingElementsOptions {
159
124
  * in `href` elements, but rather navigations are triggered in click handlers.
160
125
  * If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
161
126
  *
162
- * Optionally, the function allows you to filter the target links' URLs using an array of {@link PseudoUrl} objects
163
- * and override settings of the enqueued {@link Request} objects.
127
+ * Optionally, the function allows you to filter the target links' URLs using an array of glob or regexp patterns.
164
128
  *
165
129
  * **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
166
130
  * such as changing the Z-index of elements being clicked and their visibility. Therefore,
@@ -182,9 +146,9 @@ export interface EnqueueLinksByClickingElementsOptions {
182
146
  * page,
183
147
  * requestManager,
184
148
  * selector: 'a.product-detail',
185
- * pseudoUrls: [
186
- * 'https://www.example.com/handbags/[.*]'
187
- * 'https://www.example.com/purses/[.*]'
149
+ * include: [
150
+ * 'https://www.example.com/handbags/*',
151
+ * 'https://www.example.com/purses/*',
188
152
  * ],
189
153
  * });
190
154
  * ```
@@ -1,9 +1,25 @@
1
1
  import { URL } from 'node:url';
2
- import { applyRequestTransform, constructGlobObjectsFromGlobs, constructRegExpObjectsFromPseudoUrls, constructRegExpObjectsFromRegExps, createRequestOptions, filterRequestOptionsByPatterns, Request, serviceLocator, } from '@crawlee/browser';
3
- import ow from 'ow';
2
+ import { applyRequestTransform, constructUrlPatternObjects, createRequestOptions, filterRequestOptionsByPatterns, parseArgument, urlPatternSchema, Request, schemas, serviceLocator, } from '@crawlee/browser';
3
+ import { z } from 'zod';
4
4
  import { addInterceptRequestHandler, removeInterceptRequestHandler } from '../utils/puppeteer_request_interception.js';
5
5
  const STARTING_Z_INDEX = 2147400000;
6
6
  const getLog = () => serviceLocator.getChildLog('Puppeteer Click Elements');
7
+ const enqueueLinksByClickingElementsOptionsSchema = z.strictObject({
8
+ page: schemas.objectWithKeys(['goto', 'evaluate']),
9
+ requestManager: schemas.objectWithKeys(['fetchNextRequest', 'addRequestsBatched']),
10
+ selector: z.string(),
11
+ userData: schemas.anyObject.optional(),
12
+ clickOptions: schemas.anyObject.optional(),
13
+ include: schemas.arrayOf(urlPatternSchema, 'URL patterns').optional(),
14
+ exclude: schemas.arrayOf(urlPatternSchema, 'URL patterns').optional(),
15
+ transformRequestFunction: schemas.anyFunction.optional(),
16
+ waitForPageIdleSecs: schemas.anyNumber.default(1),
17
+ maxWaitForPageIdleSecs: schemas.anyNumber.default(5),
18
+ label: z.string().optional(),
19
+ forefront: z.boolean().optional(),
20
+ skipNavigation: z.boolean().optional(),
21
+ onSkippedRequest: schemas.anyFunction.optional(),
22
+ });
7
23
  /**
8
24
  * The function finds elements matching a specific CSS selector in a Puppeteer page,
9
25
  * clicks all those elements using a mouse move and a left mouse button click and intercepts
@@ -13,8 +29,7 @@ const getLog = () => serviceLocator.getChildLog('Puppeteer Click Elements');
13
29
  * in `href` elements, but rather navigations are triggered in click handlers.
14
30
  * If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
15
31
  *
16
- * Optionally, the function allows you to filter the target links' URLs using an array of {@link PseudoUrl} objects
17
- * and override settings of the enqueued {@link Request} objects.
32
+ * Optionally, the function allows you to filter the target links' URLs using an array of glob or regexp patterns.
18
33
  *
19
34
  * **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
20
35
  * such as changing the Z-index of elements being clicked and their visibility. Therefore,
@@ -36,9 +51,9 @@ const getLog = () => serviceLocator.getChildLog('Puppeteer Click Elements');
36
51
  * page,
37
52
  * requestManager,
38
53
  * selector: 'a.product-detail',
39
- * pseudoUrls: [
40
- * 'https://www.example.com/handbags/[.*]'
41
- * 'https://www.example.com/purses/[.*]'
54
+ * include: [
55
+ * 'https://www.example.com/handbags/*',
56
+ * 'https://www.example.com/purses/*',
42
57
  * ],
43
58
  * });
44
59
  * ```
@@ -46,51 +61,12 @@ const getLog = () => serviceLocator.getChildLog('Puppeteer Click Elements');
46
61
  * @returns Promise that resolves to {@link BatchAddRequestsResult} object.
47
62
  */
48
63
  export async function enqueueLinksByClickingElements(options) {
49
- ow(options, ow.object.exactShape({
50
- page: ow.object.hasKeys('goto', 'evaluate'),
51
- requestManager: ow.object.hasKeys('fetchNextRequest', 'addRequestsBatched'),
52
- selector: ow.string,
53
- userData: ow.optional.object,
54
- clickOptions: ow.optional.object,
55
- pseudoUrls: ow.optional.array.ofType(ow.any(ow.string, ow.object.hasKeys('purl'))),
56
- globs: ow.optional.array.ofType(ow.any(ow.string, ow.object.hasKeys('glob'))),
57
- regexps: ow.optional.array.ofType(ow.any(ow.regExp, ow.object.hasKeys('regexp'))),
58
- exclude: ow.optional.array.ofType(ow.any(ow.string, ow.regExp, ow.object.hasKeys('glob'), ow.object.hasKeys('regexp'))),
59
- transformRequestFunction: ow.optional.function,
60
- waitForPageIdleSecs: ow.optional.number,
61
- maxWaitForPageIdleSecs: ow.optional.number,
62
- label: ow.optional.string,
63
- forefront: ow.optional.boolean,
64
- skipNavigation: ow.optional.boolean,
65
- onSkippedRequest: ow.optional.function,
66
- }));
67
- const { page, requestManager, selector, clickOptions,
68
- // oxlint-disable-next-line typescript/no-deprecated -- still accepted for backwards compat
69
- pseudoUrls, globs, regexps, transformRequestFunction, waitForPageIdleSecs = 1, maxWaitForPageIdleSecs = 5, forefront, exclude, onSkippedRequest, } = options;
64
+ const parsedOptions = parseArgument(options, enqueueLinksByClickingElementsOptionsSchema, 'EnqueueLinksByClickingElementsOptions');
65
+ const { page, requestManager, selector, clickOptions, include, exclude, transformRequestFunction, waitForPageIdleSecs, maxWaitForPageIdleSecs, forefront, onSkippedRequest, } = parsedOptions;
70
66
  const waitForPageIdleMillis = waitForPageIdleSecs * 1000;
71
67
  const maxWaitForPageIdleMillis = maxWaitForPageIdleSecs * 1000;
72
- const urlExcludePatternObjects = [];
73
- const urlPatternObjects = [];
74
- if (exclude?.length) {
75
- for (const excl of exclude) {
76
- if (typeof excl === 'string' || 'glob' in excl) {
77
- urlExcludePatternObjects.push(...constructGlobObjectsFromGlobs([excl]));
78
- }
79
- else if (excl instanceof RegExp || 'regexp' in excl) {
80
- urlExcludePatternObjects.push(...constructRegExpObjectsFromRegExps([excl]));
81
- }
82
- }
83
- }
84
- if (pseudoUrls?.length) {
85
- getLog().deprecated('`pseudoUrls` option is deprecated, use `globs` or `regexps` instead');
86
- urlPatternObjects.push(...constructRegExpObjectsFromPseudoUrls(pseudoUrls));
87
- }
88
- if (globs?.length) {
89
- urlPatternObjects.push(...constructGlobObjectsFromGlobs(globs));
90
- }
91
- if (regexps?.length) {
92
- urlPatternObjects.push(...constructRegExpObjectsFromRegExps(regexps));
93
- }
68
+ const urlExcludePatternObjects = exclude?.length ? constructUrlPatternObjects(exclude) : [];
69
+ const urlPatternObjects = include?.length ? constructUrlPatternObjects(include) : [];
94
70
  const interceptedRequests = await clickElementsAndInterceptNavigationRequests({
95
71
  page,
96
72
  selector,
@@ -98,7 +74,7 @@ export async function enqueueLinksByClickingElements(options) {
98
74
  maxWaitForPageIdleMillis,
99
75
  clickOptions,
100
76
  });
101
- const requestOptions = createRequestOptions(interceptedRequests, options);
77
+ const requestOptions = createRequestOptions(interceptedRequests, parsedOptions);
102
78
  const skippedByFilters = [];
103
79
  let filteredOptions = filterRequestOptionsByPatterns(requestOptions, urlPatternObjects.length > 0 ? urlPatternObjects : undefined, urlExcludePatternObjects, undefined, (url) => skippedByFilters.push(url));
104
80
  if (onSkippedRequest && skippedByFilters.length > 0) {
@@ -0,0 +1,55 @@
1
+ import type { Configuration } from '@crawlee/browser';
2
+ import type { BrowserPool, BrowserPoolHooks, BrowserPoolOptions, PuppeteerPlugin, RemoteBrowserPool, RemoteBrowserPoolOptions } from '@crawlee/browser-pool';
3
+ // @ts-ignore optional peer dependency or compatibility with es2022
4
+ import type { Page } from 'puppeteer';
5
+ import type { PuppeteerLaunchContext } from './puppeteer-launcher.js';
6
+ /** A {@link BrowserPool} of Puppeteer browsers, as built by {@link puppeteerBrowserPool}. */
7
+ export type PuppeteerBrowserPool = BrowserPool<{
8
+ browserPlugins: [PuppeteerPlugin];
9
+ }, [PuppeteerPlugin]>;
10
+ export interface PuppeteerBrowserPoolOptions extends Omit<BrowserPoolOptions, 'browserPlugins'>, BrowserPoolHooks<ReturnType<PuppeteerPlugin['createController']>, ReturnType<PuppeteerPlugin['createLaunchContext']>, Page> {
11
+ /** How to launch the browser: proxy, user data dir, whether to use full Chrome, ... */
12
+ launchContext?: PuppeteerLaunchContext;
13
+ /**
14
+ * Whether to run the browser in headless mode. Shorthand for `launchContext.launchOptions.headless`.
15
+ * Defaults to `true`, and can also be set via {@link Configuration}.
16
+ */
17
+ headless?: boolean | 'new' | 'old';
18
+ /** Configuration to read the browser defaults from. Defaults to the global configuration. */
19
+ configuration?: Configuration;
20
+ }
21
+ export interface RemotePuppeteerBrowserPoolOptions extends Pick<PuppeteerBrowserPoolOptions, 'launchContext' | 'headless' | 'configuration'>, Omit<RemoteBrowserPoolOptions, 'browserPlugins'> {
22
+ }
23
+ /**
24
+ * Builds a {@link BrowserPool} of Puppeteer browsers to pass to a {@link PuppeteerCrawler} as its
25
+ * {@link BrowserCrawlerOptions.browserPool|`browserPool`}.
26
+ *
27
+ * It accepts every {@link BrowserPoolOptions|`BrowserPool` option} plus the crawler's own `launchContext` and
28
+ * `headless`, and derives the browser plugin from them - so a pool built here always matches the crawler it is
29
+ * given to, and configuring one never means assembling a {@link PuppeteerPlugin} by hand.
30
+ *
31
+ * **Example usage:**
32
+ *
33
+ * ```javascript
34
+ * const crawler = new PuppeteerCrawler({
35
+ * browserPool: puppeteerBrowserPool({ maxOpenPagesPerBrowser: 1 }),
36
+ * requestHandler: async ({ page }) => { ... },
37
+ * });
38
+ * ```
39
+ *
40
+ * The returned pool is *not* torn down by the crawler, which is what makes it shareable between crawlers.
41
+ *
42
+ * @category Browser management
43
+ */
44
+ export declare function puppeteerBrowserPool(options?: PuppeteerBrowserPoolOptions): PuppeteerBrowserPool;
45
+ /**
46
+ * The {@link RemoteBrowserPool} counterpart of {@link puppeteerBrowserPool}: connects to a remote browser
47
+ * service (Browserbase, Browserless, Steel, ...) with a Puppeteer plugin derived from `launchContext`.
48
+ *
49
+ * A {@link PuppeteerCrawler} accepts the same connection details directly via
50
+ * {@link BrowserCrawlerOptions.remoteBrowser|`remoteBrowser`}; reach for this factory when you also need to
51
+ * tune the wrapping pool, or to share one remote pool between crawlers.
52
+ *
53
+ * @category Browser management
54
+ */
55
+ export declare function remotePuppeteerBrowserPool(options: RemotePuppeteerBrowserPoolOptions): RemoteBrowserPool<Page>;
@@ -0,0 +1,48 @@
1
+ import { PuppeteerLauncher } from './puppeteer-launcher.js';
2
+ /**
3
+ * Builds a {@link BrowserPool} of Puppeteer browsers to pass to a {@link PuppeteerCrawler} as its
4
+ * {@link BrowserCrawlerOptions.browserPool|`browserPool`}.
5
+ *
6
+ * It accepts every {@link BrowserPoolOptions|`BrowserPool` option} plus the crawler's own `launchContext` and
7
+ * `headless`, and derives the browser plugin from them - so a pool built here always matches the crawler it is
8
+ * given to, and configuring one never means assembling a {@link PuppeteerPlugin} by hand.
9
+ *
10
+ * **Example usage:**
11
+ *
12
+ * ```javascript
13
+ * const crawler = new PuppeteerCrawler({
14
+ * browserPool: puppeteerBrowserPool({ maxOpenPagesPerBrowser: 1 }),
15
+ * requestHandler: async ({ page }) => { ... },
16
+ * });
17
+ * ```
18
+ *
19
+ * The returned pool is *not* torn down by the crawler, which is what makes it shareable between crawlers.
20
+ *
21
+ * @category Browser management
22
+ */
23
+ export function puppeteerBrowserPool(options = {}) {
24
+ const { launchContext, headless, configuration, ...poolOptions } = options;
25
+ return puppeteerLauncher(launchContext, headless, configuration).createBrowserPool(poolOptions);
26
+ }
27
+ /**
28
+ * The {@link RemoteBrowserPool} counterpart of {@link puppeteerBrowserPool}: connects to a remote browser
29
+ * service (Browserbase, Browserless, Steel, ...) with a Puppeteer plugin derived from `launchContext`.
30
+ *
31
+ * A {@link PuppeteerCrawler} accepts the same connection details directly via
32
+ * {@link BrowserCrawlerOptions.remoteBrowser|`remoteBrowser`}; reach for this factory when you also need to
33
+ * tune the wrapping pool, or to share one remote pool between crawlers.
34
+ *
35
+ * @category Browser management
36
+ */
37
+ export function remotePuppeteerBrowserPool(options) {
38
+ const { launchContext, headless, configuration, ...remoteOptions } = options;
39
+ return puppeteerLauncher(launchContext, headless, configuration).createRemoteBrowserPool(remoteOptions);
40
+ }
41
+ function puppeteerLauncher(launchContext = {}, headless, configuration) {
42
+ return new PuppeteerLauncher(headless == null
43
+ ? launchContext
44
+ : {
45
+ ...launchContext,
46
+ launchOptions: { ...launchContext.launchOptions, headless: headless },
47
+ }, configuration);
48
+ }
@@ -1,26 +1,27 @@
1
1
  import type { BrowserCrawlerOptions, BrowserCrawlingContext, BrowserHook, GetUserDataFromRequest, RouterHandler, RouterRoutes, RouteSchemas, RoutesFromSchemas } from '@crawlee/browser';
2
2
  import { BrowserCrawler } from '@crawlee/browser';
3
- import type { PuppeteerPlugin } from '@crawlee/browser-pool';
4
3
  import type { Dictionary } from '@crawlee/types';
5
4
  // @ts-ignore optional peer dependency or compatibility with es2022
6
5
  import type { HTTPResponse, LaunchOptions, Page } from 'puppeteer';
6
+ import { z } from 'zod';
7
7
  import type { EnqueueLinksByClickingElementsOptions } from './enqueue-links/click-elements.js';
8
8
  import type { PuppeteerLaunchContext } from './puppeteer-launcher.js';
9
9
  import type { InterceptHandler } from './utils/puppeteer_request_interception.js';
10
10
  import type { BlockRequestsOptions, DirectNavigationOptions, InfiniteScrollOptions, InjectFileOptions, PuppeteerContextUtils, SaveSnapshotOptions } from './utils/puppeteer_utils.js';
11
11
  export type PuppeteerGoToOptions = NonNullable<Parameters<Page['goto']>[1]>;
12
- export interface PuppeteerCrawlingContext<UserData extends Dictionary = Dictionary> extends BrowserCrawlingContext<Page, HTTPResponse, UserData, PuppeteerGoToOptions>, PuppeteerContextUtils {
12
+ export interface PuppeteerCrawlingContext<UserData extends Dictionary = any> extends BrowserCrawlingContext<Page, HTTPResponse, UserData, PuppeteerGoToOptions>, PuppeteerContextUtils {
13
13
  }
14
- // @ts-ignore optional peer dependency or compatibility with es2022
15
- export interface PuppeteerHook extends BrowserHook<PuppeteerCrawlingContext> {
16
- }
17
- export interface PuppeteerCrawlerOptions<ContextExtension = Dictionary<never>, ExtendedContext extends PuppeteerCrawlingContext = PuppeteerCrawlingContext & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<PuppeteerCrawlingContext['request']>>> extends BrowserCrawlerOptions<Page, HTTPResponse, PuppeteerCrawlingContext, ContextExtension, ExtendedContext, {
18
- browserPlugins: [PuppeteerPlugin];
19
- }, Routes> {
14
+ export type PuppeteerHook<UserData extends Dictionary = any> = BrowserHook<PuppeteerCrawlingContext<UserData>>;
15
+ export interface PuppeteerCrawlerOptions<ContextExtension = Dictionary<never>, ExtendedContext extends PuppeteerCrawlingContext = PuppeteerCrawlingContext & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<PuppeteerCrawlingContext['request']>>, StatisticStateExtension extends object = {}> extends BrowserCrawlerOptions<Page, HTTPResponse, PuppeteerCrawlingContext, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> {
20
16
  /**
21
17
  * Options used by {@link launchPuppeteer} to start new Puppeteer instances.
22
18
  */
23
19
  launchContext?: PuppeteerLaunchContext;
20
+ /**
21
+ * Whether to run browser in headless mode. Defaults to `true`.
22
+ * Can be also set via {@link Configuration}.
23
+ */
24
+ headless?: boolean | 'new' | 'old';
24
25
  /**
25
26
  * Async functions that are sequentially evaluated before the navigation. Good for setting additional cookies
26
27
  * or browser properties before navigation. The function receives the `crawlingContext`; the options object
@@ -37,7 +38,7 @@ export interface PuppeteerCrawlerOptions<ContextExtension = Dictionary<never>, E
37
38
  * ]
38
39
  * ```
39
40
  */
40
- preNavigationHooks?: BrowserHook<PuppeteerCrawlingContext, ContextExtension>[];
41
+ preNavigationHooks?: BrowserHook<PuppeteerCrawlingContext<GetUserDataFromRequest<ExtendedContext['request']>>, ContextExtension>[];
41
42
  /**
42
43
  * Async functions that are sequentially evaluated after the navigation. Good for checking if the navigation was successful.
43
44
  * The function accepts `crawlingContext` as the only parameter. A hook may optionally return a partial object
@@ -54,7 +55,7 @@ export interface PuppeteerCrawlerOptions<ContextExtension = Dictionary<never>, E
54
55
  * ]
55
56
  * ```
56
57
  */
57
- postNavigationHooks?: BrowserHook<PuppeteerCrawlingContext, ContextExtension>[];
58
+ postNavigationHooks?: BrowserHook<PuppeteerCrawlingContext<GetUserDataFromRequest<ExtendedContext['request']>>, ContextExtension>[];
58
59
  }
59
60
  /**
60
61
  * Provides a simple framework for parallel crawling of web pages
@@ -121,103 +122,127 @@ export interface PuppeteerCrawlerOptions<ContextExtension = Dictionary<never>, E
121
122
  * ```
122
123
  * @category Crawlers
123
124
  */
124
- export declare class PuppeteerCrawler<ContextExtension = Dictionary<never>, ExtendedContext extends PuppeteerCrawlingContext = PuppeteerCrawlingContext & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<PuppeteerCrawlingContext['request']>>> extends BrowserCrawler<Page, HTTPResponse, {
125
- browserPlugins: [PuppeteerPlugin];
126
- }, LaunchOptions, PuppeteerCrawlingContext, ContextExtension, ExtendedContext, Routes> {
125
+ export declare class PuppeteerCrawler<ContextExtension = Dictionary<never>, ExtendedContext extends PuppeteerCrawlingContext = PuppeteerCrawlingContext & ContextExtension, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<PuppeteerCrawlingContext['request']>>, StatisticStateExtension extends object = {}> extends BrowserCrawler<Page, HTTPResponse, LaunchOptions, PuppeteerCrawlingContext, ContextExtension, ExtendedContext, Routes, StatisticStateExtension> {
127
126
  protected static optionsShape: {
128
- // @ts-ignore optional peer dependency or compatibility with es2022
129
- browserPoolOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
130
- // @ts-ignore optional peer dependency or compatibility with es2022
131
- navigationTimeoutSecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
132
- // @ts-ignore optional peer dependency or compatibility with es2022
133
- preNavigationHooks: import("ow").ArrayPredicate<unknown> & import("ow").BasePredicate<unknown[] | undefined>;
134
- // @ts-ignore optional peer dependency or compatibility with es2022
135
- postNavigationHooks: import("ow").ArrayPredicate<unknown> & import("ow").BasePredicate<unknown[] | undefined>;
136
- // @ts-ignore optional peer dependency or compatibility with es2022
137
- launchContext: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
138
- // @ts-ignore optional peer dependency or compatibility with es2022
139
- headless: import("ow").AnyPredicate<string | boolean>;
140
- // @ts-ignore optional peer dependency or compatibility with es2022
141
- browserPool: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
142
- // @ts-ignore optional peer dependency or compatibility with es2022
143
- remoteBrowser: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
144
- // @ts-ignore optional peer dependency or compatibility with es2022
145
- saveResponseCookies: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
146
- // @ts-ignore optional peer dependency or compatibility with es2022
147
- proxyConfiguration: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
148
- // @ts-ignore optional peer dependency or compatibility with es2022
149
- contextPipelineBuilder: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
150
- // @ts-ignore optional peer dependency or compatibility with es2022
151
- extendContext: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
152
- // @ts-ignore optional peer dependency or compatibility with es2022
153
- requestList: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
154
- // @ts-ignore optional peer dependency or compatibility with es2022
155
- requestQueue: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
156
- // @ts-ignore optional peer dependency or compatibility with es2022
157
- requestHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
158
- // @ts-ignore optional peer dependency or compatibility with es2022
159
- requestHandlerTimeoutSecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
160
- // @ts-ignore optional peer dependency or compatibility with es2022
161
- errorHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
162
- // @ts-ignore optional peer dependency or compatibility with es2022
163
- failedRequestHandler: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
164
- // @ts-ignore optional peer dependency or compatibility with es2022
165
- maxRequestRetries: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
166
- // @ts-ignore optional peer dependency or compatibility with es2022
167
- sameDomainDelaySecs: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
168
- // @ts-ignore optional peer dependency or compatibility with es2022
169
- maxRequestsPerCrawl: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
170
- // @ts-ignore optional peer dependency or compatibility with es2022
171
- maxCrawlDepth: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
172
- // @ts-ignore optional peer dependency or compatibility with es2022
173
- taskLoopOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
174
- // @ts-ignore optional peer dependency or compatibility with es2022
175
- concurrencySystem: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
176
- // @ts-ignore optional peer dependency or compatibility with es2022
177
- sessionPool: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
178
- // @ts-ignore optional peer dependency or compatibility with es2022
179
- statusMessageLoggingInterval: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
180
- // @ts-ignore optional peer dependency or compatibility with es2022
181
- statusMessageCallback: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
182
- // @ts-ignore optional peer dependency or compatibility with es2022
183
- additionalHttpErrorStatusCodes: import("ow").ArrayPredicate<number>;
184
- // @ts-ignore optional peer dependency or compatibility with es2022
185
- ignoreHttpErrorStatusCodes: import("ow").ArrayPredicate<number>;
186
- // @ts-ignore optional peer dependency or compatibility with es2022
187
- blockedStatusCodes: import("ow").ArrayPredicate<number>;
188
- // @ts-ignore optional peer dependency or compatibility with es2022
189
- retryOnBlocked: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
190
- // @ts-ignore optional peer dependency or compatibility with es2022
191
- respectRobotsTxtFile: import("ow").AnyPredicate<boolean | object>;
192
- // @ts-ignore optional peer dependency or compatibility with es2022
193
- onSkippedRequest: import("ow").Predicate<Function> & import("ow").BasePredicate<Function | undefined>;
194
- // @ts-ignore optional peer dependency or compatibility with es2022
195
- httpClient: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
196
- // @ts-ignore optional peer dependency or compatibility with es2022
197
- configuration: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
198
- // @ts-ignore optional peer dependency or compatibility with es2022
199
- storageBackend: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
200
- // @ts-ignore optional peer dependency or compatibility with es2022
201
- eventManager: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
202
- // @ts-ignore optional peer dependency or compatibility with es2022
203
- logger: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
204
- // @ts-ignore optional peer dependency or compatibility with es2022
205
- minConcurrency: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
206
- // @ts-ignore optional peer dependency or compatibility with es2022
207
- maxConcurrency: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
208
- // @ts-ignore optional peer dependency or compatibility with es2022
209
- maxRequestsPerMinute: import("ow").NumberPredicate & import("ow").BasePredicate<number | undefined>;
210
- // @ts-ignore optional peer dependency or compatibility with es2022
211
- keepAlive: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
212
- // @ts-ignore optional peer dependency or compatibility with es2022
213
- statisticsOptions: import("ow").ObjectPredicate<object> & import("ow").BasePredicate<object | undefined>;
214
- // @ts-ignore optional peer dependency or compatibility with es2022
215
- id: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
127
+ headless: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
128
+ navigationTimeoutSecs: z.ZodDefault<z.ZodCustom<number, number>>;
129
+ preNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
130
+ postNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
131
+ launchContext: z.ZodDefault<z.ZodCustom<Dictionary, Dictionary>>;
132
+ browserPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
133
+ browserPoolBuilder: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
134
+ remoteBrowser: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
135
+ saveResponseCookies: z.ZodDefault<z.ZodBoolean>;
136
+ proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
137
+ ignoreIframes: z.ZodDefault<z.ZodBoolean>;
138
+ ignoreShadowRoots: z.ZodDefault<z.ZodBoolean>;
139
+ contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
140
+ extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
141
+ requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
142
+ requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
143
+ requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
144
+ requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
145
+ requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
146
+ errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
147
+ failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
148
+ maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
149
+ sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
150
+ maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
151
+ maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
152
+ taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
153
+ concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
154
+ sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
155
+ statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
156
+ statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
157
+ additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
158
+ ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
159
+ blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
160
+ retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
161
+ respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
162
+ transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
163
+ requestQueue: z.ZodOptional<z.ZodEnum<{
164
+ deferred: "deferred";
165
+ writeThrough: "writeThrough";
166
+ }>>;
167
+ }, z.core.$strict>]>>;
168
+ onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
169
+ // @ts-ignore optional peer dependency or compatibility with es2022
170
+ httpClient: z.ZodOptional<z.ZodCustom<import("@crawlee/http-client").BaseHttpClient, import("@crawlee/http-client").BaseHttpClient>>;
171
+ // @ts-ignore optional peer dependency or compatibility with es2022
172
+ configuration: z.ZodOptional<z.ZodCustom<import("@crawlee/browser").Configuration, import("@crawlee/browser").Configuration>>;
173
+ storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
174
+ // @ts-ignore optional peer dependency or compatibility with es2022
175
+ eventManager: z.ZodOptional<z.ZodCustom<import("@crawlee/browser").EventManager, import("@crawlee/browser").EventManager>>;
176
+ logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
177
+ minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
178
+ maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
179
+ maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
180
+ keepAlive: z.ZodOptional<z.ZodBoolean>;
181
+ statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
182
+ id: z.ZodOptional<z.ZodString>;
216
183
  };
184
+ protected static optionsSchema: z.ZodObject<{
185
+ headless: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
186
+ navigationTimeoutSecs: z.ZodDefault<z.ZodCustom<number, number>>;
187
+ preNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
188
+ postNavigationHooks: z.ZodDefault<z.ZodCustom<unknown[], unknown[]>>;
189
+ launchContext: z.ZodDefault<z.ZodCustom<Dictionary, Dictionary>>;
190
+ browserPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
191
+ browserPoolBuilder: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
192
+ remoteBrowser: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
193
+ saveResponseCookies: z.ZodDefault<z.ZodBoolean>;
194
+ proxyConfiguration: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
195
+ ignoreIframes: z.ZodDefault<z.ZodBoolean>;
196
+ ignoreShadowRoots: z.ZodDefault<z.ZodBoolean>;
197
+ contextPipelineBuilder: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
198
+ extendContext: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
199
+ requestList: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
200
+ requestQueue: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
201
+ requestManager: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
202
+ requestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
203
+ requestHandlerTimeoutSecs: z.ZodOptional<z.ZodCustom<number, number>>;
204
+ errorHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
205
+ failedRequestHandler: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
206
+ maxRequestRetries: z.ZodDefault<z.ZodCustom<number, number>>;
207
+ sameDomainDelaySecs: z.ZodDefault<z.ZodCustom<number, number>>;
208
+ maxRequestsPerCrawl: z.ZodOptional<z.ZodCustom<number, number>>;
209
+ maxCrawlDepth: z.ZodOptional<z.ZodCustom<number, number>>;
210
+ taskLoopOptions: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
211
+ concurrencySystem: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
212
+ sessionPool: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
213
+ statusMessageLoggingInterval: z.ZodDefault<z.ZodCustom<number, number>>;
214
+ statusMessageCallback: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
215
+ additionalHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
216
+ ignoreHttpErrorStatusCodes: z.ZodDefault<z.ZodArray<z.ZodCustom<number, number>>>;
217
+ blockedStatusCodes: z.ZodOptional<z.ZodArray<z.ZodCustom<number, number>>>;
218
+ retryOnBlocked: z.ZodDefault<z.ZodBoolean>;
219
+ respectRobotsTxtFile: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCustom<Dictionary, Dictionary>]>>;
220
+ transactionalStorage: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
221
+ requestQueue: z.ZodOptional<z.ZodEnum<{
222
+ deferred: "deferred";
223
+ writeThrough: "writeThrough";
224
+ }>>;
225
+ }, z.core.$strict>]>>;
226
+ onSkippedRequest: z.ZodOptional<z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>>;
227
+ // @ts-ignore optional peer dependency or compatibility with es2022
228
+ httpClient: z.ZodOptional<z.ZodCustom<import("@crawlee/http-client").BaseHttpClient, import("@crawlee/http-client").BaseHttpClient>>;
229
+ // @ts-ignore optional peer dependency or compatibility with es2022
230
+ configuration: z.ZodOptional<z.ZodCustom<import("@crawlee/browser").Configuration, import("@crawlee/browser").Configuration>>;
231
+ storageBackend: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
232
+ // @ts-ignore optional peer dependency or compatibility with es2022
233
+ eventManager: z.ZodOptional<z.ZodCustom<import("@crawlee/browser").EventManager, import("@crawlee/browser").EventManager>>;
234
+ logger: z.ZodOptional<z.ZodType<Dictionary<any>, unknown, z.core.$ZodTypeInternals<Dictionary<any>, unknown>>>;
235
+ minConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
236
+ maxConcurrency: z.ZodOptional<z.ZodCustom<number, number>>;
237
+ maxRequestsPerMinute: z.ZodOptional<z.ZodCustom<number, number>>;
238
+ keepAlive: z.ZodOptional<z.ZodBoolean>;
239
+ statistics: z.ZodOptional<z.ZodCustom<Dictionary, Dictionary>>;
240
+ id: z.ZodOptional<z.ZodString>;
241
+ }, z.core.$strict>;
217
242
  /**
218
243
  * All `PuppeteerCrawler` parameters are passed via an options object.
219
244
  */
220
- constructor(options?: PuppeteerCrawlerOptions<ContextExtension, ExtendedContext, Routes>);
245
+ constructor(options?: PuppeteerCrawlerOptions<ContextExtension, ExtendedContext, Routes, StatisticStateExtension>);
221
246
  // @ts-ignore optional peer dependency or compatibility with es2022
222
247
  protected buildContextPipeline(): import("@crawlee/browser").ContextPipeline<import("@crawlee/browser").CrawlingContext<Dictionary>, BrowserCrawlingContext<Page, HTTPResponse, Dictionary, Dictionary> & {
223
248
  injectFile: (filePath: string, options?: InjectFileOptions) => Promise<unknown>;
@@ -237,7 +262,7 @@ export declare class PuppeteerCrawler<ContextExtension = Dictionary<never>, Exte
237
262
  closeCookieModals: () => Promise<void>;
238
263
  }>;
239
264
  private enhanceContext;
240
- protected _navigationHandler(crawlingContext: PuppeteerCrawlingContext, gotoOptions: DirectNavigationOptions): Promise<HTTPResponse | null>;
265
+ protected navigationHandler(crawlingContext: PuppeteerCrawlingContext, gotoOptions: DirectNavigationOptions): Promise<HTTPResponse | null>;
241
266
  }
242
267
  /**
243
268
  * Creates new {@link Router} instance that works based on request labels.
@@ -1,7 +1,7 @@
1
- import { BrowserCrawler, RequestState, Router } from '@crawlee/browser';
2
- import { serviceLocator } from '@crawlee/core';
3
- import ow from 'ow';
4
- import { PuppeteerLauncher } from './puppeteer-launcher.js';
1
+ import { assertBrowserPoolNotConfigured, BrowserCrawler, RequestState, Router } from '@crawlee/browser';
2
+ import { parseArgument, serviceLocator } from '@crawlee/core';
3
+ import { z } from 'zod';
4
+ import { puppeteerBrowserPool, remotePuppeteerBrowserPool } from './puppeteer-browser-pool.js';
5
5
  import { gotoExtended, puppeteerUtils } from './utils/puppeteer_utils.js';
6
6
  /**
7
7
  * Provides a simple framework for parallel crawling of web pages
@@ -71,37 +71,36 @@ import { gotoExtended, puppeteerUtils } from './utils/puppeteer_utils.js';
71
71
  export class PuppeteerCrawler extends BrowserCrawler {
72
72
  static optionsShape = {
73
73
  ...BrowserCrawler.optionsShape,
74
- browserPoolOptions: ow.optional.object,
74
+ // Deliberately looser than the declared type: Puppeteer's own accepted string values have moved over
75
+ // time (`'new'`/`'old'`, now `'shell'`), and the value is forwarded to it verbatim.
76
+ headless: z.union([z.boolean(), z.string()]).optional(),
75
77
  };
78
+ static optionsSchema = z.strictObject(PuppeteerCrawler.optionsShape);
76
79
  /**
77
80
  * All `PuppeteerCrawler` parameters are passed via an options object.
78
81
  */
79
82
  constructor(options = {}) {
80
- ow(options, 'PuppeteerCrawlerOptions', ow.object.exactShape(PuppeteerCrawler.optionsShape));
81
- const { launchContext = {}, headless, proxyConfiguration, contextPipelineBuilder, ...browserCrawlerOptions } = options;
82
- const browserPoolOptions = {
83
- ...options.browserPoolOptions,
84
- };
83
+ const parsedOptions = parseArgument(options, PuppeteerCrawler.optionsSchema, 'PuppeteerCrawlerOptions');
84
+ const { launchContext, headless, configuration, proxyConfiguration, contextPipelineBuilder, ...browserCrawlerOptions } = parsedOptions;
85
85
  if (launchContext.proxyUrl) {
86
86
  throw new Error('PuppeteerCrawlerOptions.launchContext.proxyUrl is not allowed in PuppeteerCrawler.' +
87
87
  'Use PuppeteerCrawlerOptions.proxyConfiguration');
88
88
  }
89
- // `browserPlugins` is working when it's not overridden by `launchContext`,
90
- // which for crawlers it is always overridden. Hence the error to use the other option.
91
- if (browserPoolOptions.browserPlugins) {
92
- throw new Error('browserPoolOptions.browserPlugins is disallowed. Use launchContext.launcher instead.');
93
- }
94
- if (headless != null) {
95
- launchContext.launchOptions ??= {};
96
- launchContext.launchOptions.headless = headless;
89
+ if (options.browserPool) {
90
+ // The raw options, not the parsed ones: `launchContext` has a default, so by now it is always set.
91
+ assertBrowserPoolNotConfigured(new.target.name, {
92
+ launchContext: options.launchContext,
93
+ headless: options.headless,
94
+ });
97
95
  }
98
- const puppeteerLauncher = new PuppeteerLauncher(launchContext, options.configuration);
99
- browserPoolOptions.browserPlugins = [puppeteerLauncher.createBrowserPlugin()];
100
96
  super({
101
97
  ...browserCrawlerOptions,
102
98
  launchContext,
99
+ configuration,
103
100
  proxyConfiguration,
104
- browserPoolOptions,
101
+ browserPoolBuilder: (remoteBrowser) => remoteBrowser
102
+ ? remotePuppeteerBrowserPool({ ...remoteBrowser, launchContext, headless, configuration })
103
+ : puppeteerBrowserPool({ launchContext, headless, configuration }),
105
104
  contextPipelineBuilder: contextPipelineBuilder ?? (() => this.buildContextPipeline()),
106
105
  });
107
106
  }
@@ -146,7 +145,7 @@ export class PuppeteerCrawler extends BrowserCrawler {
146
145
  closeCookieModals: async () => puppeteerUtils.closeCookieModals(context.page),
147
146
  };
148
147
  }
149
- async _navigationHandler(crawlingContext, gotoOptions) {
148
+ async navigationHandler(crawlingContext, gotoOptions) {
150
149
  return gotoExtended(crawlingContext.page, crawlingContext.request, gotoOptions);
151
150
  }
152
151
  }
@@ -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
@@ -69,24 +70,30 @@ export declare class PuppeteerLauncher extends BrowserLauncher<PuppeteerPlugin,
69
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
- ignoreProxyCertificate: import("ow").BooleanPredicate & import("ow").BasePredicate<boolean | undefined>;
83
- // @ts-ignore optional peer dependency or compatibility with es2022
84
- 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<{
85
85
  // @ts-ignore optional peer dependency or compatibility with es2022
86
- 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>;
87
93
  // @ts-ignore optional peer dependency or compatibility with es2022
88
- userAgent: import("ow").StringPredicate & import("ow").BasePredicate<string | undefined>;
89
- };
94
+ launchOptions: z.ZodOptional<z.ZodCustom<import("@crawlee/types").Dictionary, import("@crawlee/types").Dictionary>>;
95
+ userAgent: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strict>;
90
97
  /**
91
98
  * All `PuppeteerLauncher` parameters are passed via an launchContext object.
92
99
  */
@@ -1,6 +1,6 @@
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
@@ -9,14 +9,14 @@ export class PuppeteerLauncher extends BrowserLauncher {
9
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
18
  constructor(launchContext = {}, configuration = Configuration.getGlobalConfiguration()) {
18
- ow(launchContext, 'PuppeteerLauncher', ow.object.exactShape(PuppeteerLauncher.optionsShape));
19
- const { launcher = BrowserLauncher.requireLauncherOrThrow('puppeteer', 'apify/actor-node-puppeteer-chrome'), ...browserLauncherOptions } = launchContext;
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,
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import { serviceLocator } from '@crawlee/browser';
3
- import ow from 'ow';
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) {
@@ -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';
@@ -387,8 +387,7 @@ export interface PuppeteerContextUtils {
387
387
  * in `href` elements, but rather navigations are triggered in click handlers.
388
388
  * If you're looking to find URLs in `href` attributes of the page, see {@link enqueueLinks}.
389
389
  *
390
- * Optionally, the function allows you to filter the target links' URLs using an array of {@link PseudoUrl} objects
391
- * 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.
392
391
  *
393
392
  * **IMPORTANT**: To be able to do this, this function uses various mutations on the page,
394
393
  * such as changing the Z-index of elements being clicked and their visibility. Therefore,
@@ -409,9 +408,9 @@ export interface PuppeteerContextUtils {
409
408
  * async requestHandler({ enqueueLinksByClickingElements }) {
410
409
  * await enqueueLinksByClickingElements({
411
410
  * selector: 'a.product-detail',
412
- * globs: [
413
- * 'https://www.example.com/handbags/**'
414
- * 'https://www.example.com/purses/**'
411
+ * include: [
412
+ * 'https://www.example.com/handbags/**',
413
+ * 'https://www.example.com/purses/**',
415
414
  * ],
416
415
  * });
417
416
  * });
@@ -20,10 +20,9 @@
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, serviceLocator, 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 ow from 'ow';
25
+ import { z } from 'zod';
27
26
  import { LruCache } from '@apify/datastructures';
28
27
  import { enqueueLinksByClickingElements } from '../enqueue-links/click-elements.js';
29
28
  import { addInterceptRequestHandler, removeInterceptRequestHandler } from './puppeteer_request_interception.js';
@@ -31,6 +30,37 @@ const require = createRequire(import.meta.url);
31
30
  const jqueryPath = require.resolve('jquery');
32
31
  const MAX_INJECT_FILE_CACHE_SIZE = 10;
33
32
  const DEFAULT_BLOCK_REQUEST_URL_PATTERNS = ['.css', '.jpg', '.jpeg', '.png', '.svg', '.gif', '.woff', '.pdf', '.zip'];
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
+ });
34
64
  const getLog = () => serviceLocator.getChildLog('Puppeteer Utils');
35
65
  /**
36
66
  * Cache contents of previously injected files to limit file system access.
@@ -48,18 +78,16 @@ const injectedFilesCache = new LruCache({ maxLength: MAX_INJECT_FILE_CACHE_SIZE
48
78
  * @param [options]
49
79
  */
50
80
  export async function injectFile(page, filePath, options = {}) {
51
- ow(page, ow.object.validate(validators.browserPage));
52
- ow(filePath, ow.string);
53
- ow(options, ow.object.exactShape({
54
- surviveNavigations: ow.optional.boolean,
55
- }));
81
+ parseArgument(page, validators.browserPage);
82
+ parseArgument(filePath, filePathSchema);
83
+ const { surviveNavigations } = parseArgument(options, injectFileOptionsSchema);
56
84
  let contents = injectedFilesCache.get(filePath);
57
85
  if (!contents) {
58
86
  contents = await readFile(filePath, 'utf8');
59
87
  injectedFilesCache.add(filePath, contents);
60
88
  }
61
89
  const evalP = page.evaluate(contents);
62
- if (options.surviveNavigations) {
90
+ if (surviveNavigations) {
63
91
  page.on('framenavigated', async () => page
64
92
  .evaluate(contents)
65
93
  .catch((error) => getLog().warning('An error occurred during the script injection!', { error })));
@@ -93,7 +121,7 @@ export async function injectFile(page, filePath, options = {}) {
93
121
  * @param [options.surviveNavigations] Opt-out option to disable the JQuery reinjection after navigation.
94
122
  */
95
123
  export async function injectJQuery(page, options) {
96
- ow(page, ow.object.validate(validators.browserPage));
124
+ parseArgument(page, validators.browserPage);
97
125
  return injectFile(page, jqueryPath, { surviveNavigations: options?.surviveNavigations ?? true });
98
126
  }
99
127
  /**
@@ -109,7 +137,7 @@ export async function injectJQuery(page, options) {
109
137
  * @param ignoreShadowRoots
110
138
  */
111
139
  export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIframes = false) {
112
- ow(page, ow.object.validate(validators.browserPage));
140
+ parseArgument(page, validators.browserPage);
113
141
  if (page.frames().length > 1 && !ignoreIframes) {
114
142
  const frames = await page.$$('iframe');
115
143
  await Promise.all(frames.map(async (frame) => {
@@ -142,7 +170,8 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
142
170
  ? null
143
171
  : (await page.evaluate(`(${expandShadowRoots.toString()})(document)`));
144
172
  const pageContent = html || (await page.content());
145
- return cheerio.load(pageContent);
173
+ const { load } = await import('cheerio');
174
+ return load(pageContent);
146
175
  }
147
176
  /**
148
177
  * Forces the Puppeteer browser tab to block loading URLs that match a provided pattern.
@@ -187,12 +216,8 @@ export async function parseWithCheerio(page, ignoreShadowRoots = false, ignoreIf
187
216
  * @param [options]
188
217
  */
189
218
  export async function blockRequests(page, options = {}) {
190
- ow(page, ow.object.validate(validators.browserPage));
191
- ow(options, ow.object.exactShape({
192
- urlPatterns: ow.optional.array.ofType(ow.string),
193
- extraUrlPatterns: ow.optional.array.ofType(ow.string),
194
- }));
195
- const { urlPatterns = DEFAULT_BLOCK_REQUEST_URL_PATTERNS, extraUrlPatterns = [] } = options;
219
+ parseArgument(page, validators.browserPage);
220
+ const { urlPatterns, extraUrlPatterns } = parseArgument(options, blockRequestsOptionsSchema);
196
221
  const patternsToBlock = [...urlPatterns, ...extraUrlPatterns];
197
222
  // We use CDP commands instead of request interception as the latter disables caching, which is not ideal
198
223
  await sendCDPCommand(page, 'Network.setBlockedURLs', { urls: patternsToBlock });
@@ -249,9 +274,9 @@ export const blockResources = async (page, resourceTypes = ['stylesheet', 'font'
249
274
  * @deprecated
250
275
  */
251
276
  export async function cacheResponses(page, cache, responseUrlRules) {
252
- ow(page, ow.object.validate(validators.browserPage));
253
- ow(cache, ow.object);
254
- ow(responseUrlRules, ow.array.ofType(ow.any(ow.string, ow.regExp)));
277
+ parseArgument(page, validators.browserPage);
278
+ parseArgument(cache, schemas.anyObject);
279
+ parseArgument(responseUrlRules, responseUrlRulesSchema);
255
280
  serviceLocator
256
281
  .getLogger()
257
282
  .deprecated('utils.puppeteer.cacheResponses() has a high impact on performance ' +
@@ -344,14 +369,9 @@ export function compileScript(scriptString, context = Object.create(null)) {
344
369
  * @param [gotoOptions] Custom options for `page.goto()`.
345
370
  */
346
371
  export async function gotoExtended(page, request, gotoOptions = {}) {
347
- ow(page, ow.object.validate(validators.browserPage));
348
- ow(request, ow.object.partialShape({
349
- url: ow.string.url,
350
- method: ow.optional.string,
351
- headers: ow.optional.object,
352
- payload: ow.optional.any(ow.string, ow.uint8Array),
353
- }));
354
- ow(gotoOptions, ow.object);
372
+ parseArgument(page, validators.browserPage);
373
+ parseArgument(request, gotoExtendedRequestSchema);
374
+ parseArgument(gotoOptions, schemas.anyObject);
355
375
  gotoOptions = { ...gotoOptions };
356
376
  if (gotoOptions.waitUntil === 'networkidle') {
357
377
  gotoOptions.waitUntil = 'networkidle0';
@@ -407,16 +427,8 @@ export async function gotoExtended(page, request, gotoOptions = {}) {
407
427
  * @param [options]
408
428
  */
409
429
  export async function infiniteScroll(page, options = {}) {
410
- ow(page, ow.object.validate(validators.browserPage));
411
- ow(options, ow.object.exactShape({
412
- timeoutSecs: ow.optional.number,
413
- maxScrollHeight: ow.optional.number,
414
- waitForSecs: ow.optional.number,
415
- scrollDownAndUp: ow.optional.boolean,
416
- buttonSelector: ow.optional.string,
417
- stopScrollCallback: ow.optional.function,
418
- }));
419
- 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);
420
432
  let finished;
421
433
  const startTime = Date.now();
422
434
  const CHECK_INTERVAL_MILLIS = 1000;
@@ -508,16 +520,8 @@ export async function infiniteScroll(page, options = {}) {
508
520
  * @param [options]
509
521
  */
510
522
  export async function saveSnapshot(page, options = {}) {
511
- ow(page, ow.object.validate(validators.browserPage));
512
- ow(options, ow.object.exactShape({
513
- key: ow.optional.string.nonEmpty,
514
- screenshotQuality: ow.optional.number,
515
- saveScreenshot: ow.optional.boolean,
516
- saveHtml: ow.optional.boolean,
517
- keyValueStoreName: ow.optional.string,
518
- configuration: ow.optional.object,
519
- }));
520
- const { key = 'SNAPSHOT', screenshotQuality = 50, saveScreenshot = true, saveHtml = true, keyValueStoreName, configuration, } = options;
523
+ parseArgument(page, validators.browserPage);
524
+ const { key, screenshotQuality, saveScreenshot, saveHtml, keyValueStoreName, configuration } = parseArgument(options, saveSnapshotOptionsSchema);
521
525
  try {
522
526
  const store = await KeyValueStore.open(keyValueStoreName ? { name: keyValueStoreName } : null, {
523
527
  configuration: configuration ?? Configuration.getGlobalConfiguration(),
@@ -564,7 +568,7 @@ ${error.message}
564
568
  return idcacPlaywright;
565
569
  }
566
570
  export async function closeCookieModals(page) {
567
- ow(page, ow.object.validate(validators.browserPage));
571
+ parseArgument(page, validators.browserPage);
568
572
  const idcac = await getIdcacPlaywright();
569
573
  if (idcac?.getInjectableScript()) {
570
574
  await page.evaluate(idcac.getInjectableScript());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/puppeteer",
3
- "version": "4.0.0-beta.99",
3
+ "version": "4.0.0-rc.0",
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"
@@ -48,17 +48,16 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@apify/datastructures": "^2.0.3",
51
- "@crawlee/browser": "4.0.0-beta.99",
52
- "@crawlee/browser-pool": "4.0.0-beta.99",
53
- "@crawlee/core": "4.0.0-beta.99",
54
- "@crawlee/types": "4.0.0-beta.99",
55
- "@crawlee/utils": "4.0.0-beta.99",
51
+ "@crawlee/browser": "4.0.0-rc.0",
52
+ "@crawlee/browser-pool": "4.0.0-rc.0",
53
+ "@crawlee/core": "4.0.0-rc.0",
54
+ "@crawlee/types": "4.0.0-rc.0",
55
+ "@crawlee/utils": "4.0.0-rc.0",
56
56
  "cheerio": "^1.0.0",
57
57
  "devtools-protocol": "*",
58
- "idcac-playwright": "^0.2.0",
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": {
64
63
  "idcac-playwright": "^0.2.0",
@@ -79,5 +78,5 @@
79
78
  }
80
79
  }
81
80
  },
82
- "gitHead": "ad2748380941842bb10cff100f4b4caad92049e3"
81
+ "gitHead": "79ab33dacdacb83e0197e6516d145f3aceef80c7"
83
82
  }