@crawlee/impit-client 4.0.0-beta.25 → 4.0.0-beta.251

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/README.md CHANGED
@@ -1,23 +1,23 @@
1
1
  <h1 align="center">
2
2
  <a href="https://crawlee.dev">
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-dark.svg?sanitize=true">
5
- <img alt="Crawlee" src="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-light.svg?sanitize=true" width="500">
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-dark.svg?sanitize=true" />
5
+ <img alt="Crawlee" src="https://raw.githubusercontent.com/apify/crawlee/master/website/static/img/crawlee-light.svg?sanitize=true" width="500" />
6
6
  </picture>
7
7
  </a>
8
- <br>
8
+ <br />
9
9
  <small>A web scraping and browser automation library</small>
10
10
  </h1>
11
11
 
12
- <p align=center>
13
- <a href="https://trendshift.io/repositories/5179" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5179" alt="apify%2Fcrawlee | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
12
+ <p align="center">
13
+ <a href="https://trendshift.io/repositories/5179" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5179" alt="apify%2Fcrawlee | Trendshift" width="250" height="55"/></a>
14
14
  </p>
15
15
 
16
- <p align=center>
17
- <a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/v/@crawlee/core.svg" alt="NPM latest version" data-canonical-src="https://img.shields.io/npm/v/@crawlee/core/next.svg" style="max-width: 100%;"></a>
18
- <a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/dm/@crawlee/core.svg" alt="Downloads" data-canonical-src="https://img.shields.io/npm/dm/@crawlee/core.svg" style="max-width: 100%;"></a>
19
- <a href="https://discord.gg/jyEM2PRvMU" rel="nofollow"><img src="https://img.shields.io/discord/801163717915574323?label=discord" alt="Chat on discord" data-canonical-src="https://img.shields.io/discord/801163717915574323?label=discord" style="max-width: 100%;"></a>
20
- <a href="https://github.com/apify/crawlee/actions/workflows/test-ci.yml"><img src="https://github.com/apify/crawlee/actions/workflows/test-ci.yml/badge.svg?branch=master" alt="Build Status" style="max-width: 100%;"></a>
16
+ <p align="center">
17
+ <a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/v/@crawlee/core.svg" alt="NPM latest version" data-canonical-src="https://img.shields.io/npm/v/@crawlee/core/next.svg" /></a>
18
+ <a href="https://www.npmjs.com/package/@crawlee/core" rel="nofollow"><img src="https://img.shields.io/npm/dm/@crawlee/core.svg" alt="Downloads" data-canonical-src="https://img.shields.io/npm/dm/@crawlee/core.svg" /></a>
19
+ <a href="https://discord.gg/jyEM2PRvMU" rel="nofollow"><img src="https://img.shields.io/discord/801163717915574323?label=discord" alt="Chat on discord" data-canonical-src="https://img.shields.io/discord/801163717915574323?label=discord" /></a>
20
+ <a href="https://github.com/apify/crawlee/actions/workflows/test-ci.yml"><img src="https://github.com/apify/crawlee/actions/workflows/test-ci.yml/badge.svg?branch=master" alt="Build Status" /></a>
21
21
  </p>
22
22
 
23
23
  Crawlee covers your crawling and scraping end-to-end and **helps you build reliable scrapers. Fast.**
@@ -34,7 +34,7 @@ Crawlee is available as the [`crawlee`](https://www.npmjs.com/package/crawlee) N
34
34
 
35
35
  We recommend visiting the [Introduction tutorial](https://crawlee.dev/js/docs/introduction) in Crawlee documentation for more information.
36
36
 
37
- > Crawlee requires **Node.js 16 or higher**.
37
+ > Crawlee requires **Node.js 22.13 or higher**.
38
38
 
39
39
  ### With Crawlee CLI
40
40
 
@@ -89,7 +89,7 @@ By default, Crawlee stores data to `./storage` in the current working directory.
89
89
  We provide automated beta builds for every merged code change in Crawlee. You can find them in the npm [list of releases](https://www.npmjs.com/package/crawlee?activeTab=versions). If you want to test new features or bug fixes before we release them, feel free to install a beta build like this:
90
90
 
91
91
  ```bash
92
- npm install crawlee@3.12.3-beta.13
92
+ npm install crawlee@next
93
93
  ```
94
94
 
95
95
  If you also use the [Apify SDK](https://github.com/apify/apify-sdk-js), you need to specify dependency overrides in your `package.json` file so that you don't end up with multiple versions of Crawlee installed:
@@ -98,9 +98,9 @@ If you also use the [Apify SDK](https://github.com/apify/apify-sdk-js), you need
98
98
  {
99
99
  "overrides": {
100
100
  "apify": {
101
- "@crawlee/core": "3.12.3-beta.13",
102
- "@crawlee/types": "3.12.3-beta.13",
103
- "@crawlee/utils": "3.12.3-beta.13"
101
+ "@crawlee/core": "$crawlee",
102
+ "@crawlee/types": "$crawlee",
103
+ "@crawlee/utils": "$crawlee"
104
104
  }
105
105
  }
106
106
  }
package/index.d.ts CHANGED
@@ -1,27 +1,25 @@
1
1
  import type { CustomFetchOptions } from '@crawlee/http-client';
2
2
  import { BaseHttpClient } from '@crawlee/http-client';
3
+ import type { CrawleeLogger } from '@crawlee/types';
3
4
  import { type ImpitOptions } from 'impit';
4
5
  export declare const Browser: {
5
- readonly Chrome: "chrome";
6
- readonly Firefox: "firefox";
6
+ readonly Chrome: 'chrome';
7
+ readonly Firefox: 'firefox';
7
8
  };
8
9
  /**
9
10
  * A HTTP client implementation based on the `impit` library.
10
11
  */
11
12
  export declare class ImpitHttpClient extends BaseHttpClient {
12
- private impitOptions;
13
+ #private;
13
14
  /**
14
- * Enables reuse of `impit` clients for the same set of options.
15
- * This is useful for performance reasons, as creating
16
- * a new client for each request breaks TCP connection
17
- * (and other resources) reuse.
15
+ * @param options.cacheClients Whether to cache `impit` clients between requests. Defaults to `true`.
18
16
  */
19
- private clientCache;
20
- private getClient;
21
- constructor(options?: Omit<ImpitOptions, 'proxyUrl' | 'timeout'>);
17
+ constructor(options?: Omit<ImpitOptions, 'proxyUrl' | 'timeout'> & {
18
+ cacheClients?: boolean;
19
+ logger?: CrawleeLogger;
20
+ });
22
21
  /**
23
22
  * @inheritDoc
24
23
  */
25
- fetch(request: Request, options?: RequestInit & CustomFetchOptions): Promise<Response>;
24
+ protected fetch(request: Request, options?: RequestInit & CustomFetchOptions): Promise<Response>;
26
25
  }
27
- //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,6 +1,27 @@
1
+ import { serviceLocator } from '@crawlee/core';
1
2
  import { BaseHttpClient, ResponseWithUrl } from '@crawlee/http-client';
2
3
  import { Impit } from 'impit';
3
4
  import { LruCache } from '@apify/datastructures';
5
+ // Concrete impit impersonation profiles per browser family. The plain `chrome` /
6
+ // `firefox` aliases fall back to the oldest available version, which is a
7
+ // fingerprint giveaway — we pick one of these explicitly instead. Keep in sync
8
+ // with impit's `Browser` type when bumping the dependency.
9
+ const IMPIT_VERSIONS_BY_BROWSER = {
10
+ chrome: [
11
+ 'chrome100',
12
+ 'chrome101',
13
+ 'chrome104',
14
+ 'chrome107',
15
+ 'chrome110',
16
+ 'chrome116',
17
+ 'chrome124',
18
+ 'chrome125',
19
+ 'chrome131',
20
+ 'chrome136',
21
+ 'chrome142',
22
+ ],
23
+ firefox: ['firefox128', 'firefox133', 'firefox135', 'firefox144'],
24
+ };
4
25
  export const Browser = {
5
26
  'Chrome': 'chrome',
6
27
  'Firefox': 'firefox',
@@ -9,42 +30,87 @@ export const Browser = {
9
30
  * A HTTP client implementation based on the `impit` library.
10
31
  */
11
32
  export class ImpitHttpClient extends BaseHttpClient {
12
- impitOptions;
33
+ #impitOptions;
34
+ #cacheClients;
13
35
  /**
14
36
  * Enables reuse of `impit` clients for the same set of options.
15
37
  * This is useful for performance reasons, as creating
16
38
  * a new client for each request breaks TCP connection
17
39
  * (and other resources) reuse.
18
40
  */
19
- clientCache = new LruCache({ maxLength: 10 });
20
- getClient(options) {
41
+ #clientCache = new LruCache({ maxLength: 10 });
42
+ /**
43
+ * Stable impit impersonation version per fingerprint object, so the same
44
+ * session keeps impersonating the same browser version across requests
45
+ * instead of rerolling on every call.
46
+ */
47
+ #impitBrowserByFingerprint = new WeakMap();
48
+ #getClient(options) {
49
+ if (!this.#cacheClients) {
50
+ return new Impit(options);
51
+ }
21
52
  const { cookieJar, ...rest } = options;
22
53
  const cacheKey = JSON.stringify(rest);
23
- const existingClient = this.clientCache.get(cacheKey);
54
+ const existingClient = this.#clientCache.get(cacheKey);
24
55
  if (existingClient && (!cookieJar || existingClient.cookieJar === cookieJar)) {
25
56
  return existingClient.client;
26
57
  }
27
58
  const client = new Impit(options);
28
- this.clientCache.add(cacheKey, { client, cookieJar: cookieJar });
59
+ this.#clientCache.add(cacheKey, { client, cookieJar: cookieJar });
29
60
  return client;
30
61
  }
62
+ /**
63
+ * @param options.cacheClients Whether to cache `impit` clients between requests. Defaults to `true`.
64
+ */
31
65
  constructor(options) {
32
- super();
33
- this.impitOptions = options ?? {};
66
+ super({ logger: options?.logger });
67
+ const { cacheClients = true, logger, ...impitOptions } = options ?? {};
68
+ if (impitOptions.browser) {
69
+ (logger ?? serviceLocator.getLogger()).warningOnce('The `browser` option of ImpitHttpClient overrides the browser fingerprint of the session. ' +
70
+ 'Using it with Crawlee crawlers can lead to incompatible Session setups.');
71
+ }
72
+ this.#impitOptions = impitOptions;
73
+ this.#cacheClients = cacheClients;
34
74
  }
35
75
  /**
36
76
  * @inheritDoc
37
77
  */
38
78
  async fetch(request, options) {
39
- const { proxyUrl, redirect, signal } = options ?? {};
40
- const impit = this.getClient({
41
- ...this.impitOptions,
79
+ const { proxyUrl, redirect, signal, fingerprint, ignoreTlsErrors } = options ?? {};
80
+ const impitBrowser = this.#impitOptions.browser ? undefined : this.#resolveImpitBrowser(fingerprint);
81
+ const impit = this.#getClient({
82
+ ...this.#impitOptions,
83
+ ...(impitBrowser ? { browser: impitBrowser } : {}),
84
+ // The per-request flag (from the crawler option or a MITM proxy session)
85
+ // can only enable ignoring, never override a constructor-level `true`.
86
+ ...(ignoreTlsErrors ? { ignoreTlsErrors: true } : {}),
42
87
  proxyUrl,
43
88
  followRedirects: redirect === 'follow',
44
89
  });
45
- const response = await impit.fetch(request, { signal: signal ?? undefined });
90
+ // The caller's signal carries the deadline (for crawlers, the navigation window that `extendTimeout()` can
91
+ // push out), so impit's 30 s default must not cut the request short. Without a signal, the default stays.
92
+ const response = await impit.fetch(request, {
93
+ signal: signal ?? undefined,
94
+ ...(signal ? { timeout: null } : {}),
95
+ });
96
+ // Once the headers are in, an aborted signal only stops impit's body reads, while the download itself keeps
97
+ // going. Piping the body through the signal cancels the download too.
98
+ const body = signal && response.body ? response.body.pipeThrough(new TransformStream(), { signal }) : response.body;
46
99
  // todo - cast shouldn't be needed here, impit returns `Uint8Array`
47
- return new ResponseWithUrl(response.body, response);
100
+ return new ResponseWithUrl(body, response);
101
+ }
102
+ #resolveImpitBrowser(fingerprint) {
103
+ if (!fingerprint?.browser)
104
+ return undefined;
105
+ const cached = this.#impitBrowserByFingerprint.get(fingerprint);
106
+ if (cached)
107
+ return cached;
108
+ // impit can only impersonate Chrome and Firefox. Map other (Chromium-based or
109
+ // unsupported) families like `edge`/`safari` onto Chrome so the request still
110
+ // carries realistic browser headers instead of impit's bare `*/*` defaults.
111
+ const versions = IMPIT_VERSIONS_BY_BROWSER[fingerprint.browser] ?? IMPIT_VERSIONS_BY_BROWSER.chrome;
112
+ const picked = versions[Math.floor(Math.random() * versions.length)];
113
+ this.#impitBrowserByFingerprint.set(fingerprint, picked);
114
+ return picked;
48
115
  }
49
116
  }
50
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@crawlee/impit-client",
3
- "version": "4.0.0-beta.25",
3
+ "version": "4.0.0-beta.251",
4
4
  "description": "impit-based HTTP client implementation for Crawlee. Impersonates browser requests to avoid bot detection.",
5
5
  "engines": {
6
- "node": ">=22.0.0"
6
+ "node": ">=22.13.0"
7
7
  },
8
8
  "type": "module",
9
9
  "exports": {
@@ -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"
@@ -47,10 +47,12 @@
47
47
  "access": "public"
48
48
  },
49
49
  "dependencies": {
50
- "@apify/datastructures": "^2.0.3",
51
- "@crawlee/http-client": "4.0.0-beta.25",
52
- "impit": "^0.8.2",
53
- "tough-cookie": "^6.0.0"
50
+ "@apify/datastructures": "^3.0.1",
51
+ "@crawlee/core": "4.0.0-beta.251",
52
+ "@crawlee/http-client": "4.0.0-beta.251",
53
+ "@crawlee/types": "4.0.0-beta.251",
54
+ "impit": "^0.14.8",
55
+ "tough-cookie": "^6.0.2"
54
56
  },
55
57
  "lerna": {
56
58
  "command": {
@@ -59,5 +61,5 @@
59
61
  }
60
62
  }
61
63
  },
62
- "gitHead": "17411c1cb92d9829cf4179cc9c0a7d46a17942a4"
64
+ "gitHead": "e8a0d8a538fabf624c73b6354dbf7194882300a5"
63
65
  }
package/index.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAmB,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AAKjD,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAEX;;GAEG;AACH,qBAAa,eAAgB,SAAQ,cAAc;IAC/C,OAAO,CAAC,YAAY,CAAe;IAEnC;;;;;OAKG;IACH,OAAO,CAAC,WAAW,CAA2F;IAE9G,OAAO,CAAC,SAAS;gBAgBL,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,GAAG,SAAS,CAAC;IAKhE;;OAEG;IACG,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC;CAc/F"}
package/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,KAAK,EAAqB,MAAM,OAAO,CAAC;AAGjD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,CAAC,MAAM,OAAO,GAAG;IACnB,QAAQ,EAAE,QAAQ;IAClB,SAAS,EAAE,SAAS;CACd,CAAC;AAEX;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,cAAc;IACvC,YAAY,CAAe;IAEnC;;;;;OAKG;IACK,WAAW,GAA2D,IAAI,QAAQ,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;IAEtG,SAAS,CAAC,OAAqB;QACnC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;QAEvC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEtD,IAAI,cAAc,IAAI,CAAC,CAAC,SAAS,IAAI,cAAc,CAAC,SAAS,KAAK,SAAS,CAAC,EAAE,CAAC;YAC3E,OAAO,cAAc,CAAC,MAAM,CAAC;QACjC,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAA2B,EAAE,CAAC,CAAC;QAEnF,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,YAAY,OAAoD;QAC5D,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,YAAY,GAAG,OAAO,IAAI,EAAE,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,OAAgB,EAAE,OAA0C;QACpE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;QAErD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;YACzB,GAAG,IAAI,CAAC,YAAY;YACpB,QAAQ;YACR,eAAe,EAAE,QAAQ,KAAK,QAAQ;SACzC,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;QAE7E,mEAAmE;QACnE,OAAO,IAAI,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACxD,CAAC;CACJ"}