@crawlee/impit-client 4.0.0-beta.18 → 4.0.0-beta.180

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.**
@@ -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,41 +1,27 @@
1
- import type { BaseHttpClient, SendRequestOptions, StreamOptions } from '@crawlee/core';
2
- import type { ImpitOptions } from 'impit';
1
+ import type { CustomFetchOptions } from '@crawlee/http-client';
2
+ import { BaseHttpClient } from '@crawlee/http-client';
3
+ import type { CrawleeLogger } from '@crawlee/types';
4
+ import { type ImpitOptions } from 'impit';
3
5
  export declare const Browser: {
4
- readonly Chrome: "chrome";
5
- readonly Firefox: "firefox";
6
+ readonly Chrome: 'chrome';
7
+ readonly Firefox: 'firefox';
6
8
  };
7
9
  /**
8
- * A HTTP client implementation based on the `impit library.
10
+ * A HTTP client implementation based on the `impit` library.
9
11
  */
10
- export declare class ImpitHttpClient implements BaseHttpClient {
11
- private impitOptions;
12
- private maxRedirects;
13
- private followRedirects;
14
- /**
15
- * Enables reuse of `impit` clients for the same set of options.
16
- * This is useful for performance reasons, as creating
17
- * a new client for each request breaks TCP connection
18
- * (and other resources) reuse.
19
- */
20
- private clientCache;
12
+ export declare class ImpitHttpClient extends BaseHttpClient {
13
+ #private;
21
14
  private getClient;
22
- constructor(options?: Omit<ImpitOptions, 'proxyUrl'> & {
23
- maxRedirects?: number;
24
- });
25
15
  /**
26
- * Common implementation for `sendRequest` and `stream` methods.
27
- * @param request `HttpRequest` object
28
- * @returns `HttpResponse` object
16
+ * @param options.cacheClients Whether to cache `impit` clients between requests. Defaults to `true`.
29
17
  */
30
- private getResponse;
31
- /**
32
- * @inheritDoc
33
- */
34
- sendRequest(request: Request, options?: SendRequestOptions): Promise<Response>;
35
- private getStreamWithProgress;
18
+ constructor(options?: Omit<ImpitOptions, 'proxyUrl' | 'timeout'> & {
19
+ cacheClients?: boolean;
20
+ logger?: CrawleeLogger;
21
+ });
36
22
  /**
37
23
  * @inheritDoc
38
24
  */
39
- stream(request: Request, options?: StreamOptions): Promise<Response>;
25
+ fetch(request: Request, options?: RequestInit & CustomFetchOptions): Promise<Response>;
26
+ private resolveImpitBrowser;
40
27
  }
41
- //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,108 +1,103 @@
1
- import { Readable } from 'node:stream';
2
- import { ResponseWithUrl } from '@crawlee/core';
1
+ import { BaseHttpClient, ResponseWithUrl } from '@crawlee/http-client';
3
2
  import { Impit } from 'impit';
4
3
  import { LruCache } from '@apify/datastructures';
4
+ // Concrete impit impersonation profiles per browser family. The plain `chrome` /
5
+ // `firefox` aliases fall back to the oldest available version, which is a
6
+ // fingerprint giveaway — we pick one of these explicitly instead. Keep in sync
7
+ // with impit's `Browser` type when bumping the dependency.
8
+ const IMPIT_VERSIONS_BY_BROWSER = {
9
+ chrome: [
10
+ 'chrome100',
11
+ 'chrome101',
12
+ 'chrome104',
13
+ 'chrome107',
14
+ 'chrome110',
15
+ 'chrome116',
16
+ 'chrome124',
17
+ 'chrome125',
18
+ 'chrome131',
19
+ 'chrome136',
20
+ 'chrome142',
21
+ ],
22
+ firefox: ['firefox128', 'firefox133', 'firefox135', 'firefox144'],
23
+ };
5
24
  export const Browser = {
6
25
  'Chrome': 'chrome',
7
26
  'Firefox': 'firefox',
8
27
  };
9
28
  /**
10
- * A HTTP client implementation based on the `impit library.
29
+ * A HTTP client implementation based on the `impit` library.
11
30
  */
12
- export class ImpitHttpClient {
13
- impitOptions;
14
- maxRedirects;
15
- followRedirects;
31
+ export class ImpitHttpClient extends BaseHttpClient {
32
+ #impitOptions;
33
+ #cacheClients;
16
34
  /**
17
35
  * Enables reuse of `impit` clients for the same set of options.
18
36
  * This is useful for performance reasons, as creating
19
37
  * a new client for each request breaks TCP connection
20
38
  * (and other resources) reuse.
21
39
  */
22
- clientCache = new LruCache({ maxLength: 10 });
40
+ #clientCache = new LruCache({ maxLength: 10 });
41
+ /**
42
+ * Stable impit impersonation version per fingerprint object, so the same
43
+ * session keeps impersonating the same browser version across requests
44
+ * instead of rerolling on every call.
45
+ */
46
+ #impitBrowserByFingerprint = new WeakMap();
23
47
  getClient(options) {
48
+ if (!this.#cacheClients) {
49
+ return new Impit(options);
50
+ }
24
51
  const { cookieJar, ...rest } = options;
25
52
  const cacheKey = JSON.stringify(rest);
26
- const existingClient = this.clientCache.get(cacheKey);
53
+ const existingClient = this.#clientCache.get(cacheKey);
27
54
  if (existingClient && (!cookieJar || existingClient.cookieJar === cookieJar)) {
28
55
  return existingClient.client;
29
56
  }
30
57
  const client = new Impit(options);
31
- this.clientCache.add(cacheKey, { client, cookieJar: cookieJar });
58
+ this.#clientCache.add(cacheKey, { client, cookieJar: cookieJar });
32
59
  return client;
33
60
  }
34
- constructor(options) {
35
- this.impitOptions = options ?? {};
36
- this.maxRedirects = options?.maxRedirects ?? 10;
37
- this.followRedirects = options?.followRedirects ?? true;
38
- }
39
61
  /**
40
- * Common implementation for `sendRequest` and `stream` methods.
41
- * @param request `HttpRequest` object
42
- * @returns `HttpResponse` object
62
+ * @param options.cacheClients Whether to cache `impit` clients between requests. Defaults to `true`.
43
63
  */
44
- async getResponse(request, redirects, options) {
45
- if ((redirects?.redirectCount ?? 0) > this.maxRedirects) {
46
- throw new Error(`Too many redirects, maximum is ${this.maxRedirects}.`);
47
- }
48
- const impit = this.getClient({
49
- ...this.impitOptions,
50
- ...(options?.cookieJar ? { cookieJar: options?.cookieJar } : {}),
51
- proxyUrl: options?.session?.proxyInfo?.url,
52
- followRedirects: false,
53
- });
54
- const response = await impit.fetch(request, { timeout: options?.timeout });
55
- if (this.followRedirects && response.status >= 300 && response.status < 400) {
56
- const location = response.headers.get('location');
57
- const redirectUrl = new URL(location ?? '', request.url);
58
- if (!location) {
59
- throw new Error('Redirect response missing location header.');
60
- }
61
- return this.getResponse({
62
- ...request,
63
- url: redirectUrl.href,
64
- }, {
65
- redirectCount: (redirects?.redirectCount ?? 0) + 1,
66
- redirectUrls: [...(redirects?.redirectUrls ?? []), redirectUrl],
67
- });
68
- }
69
- return {
70
- response,
71
- redirectUrls: redirects?.redirectUrls ?? [],
72
- };
64
+ constructor(options) {
65
+ super({ logger: options?.logger });
66
+ const { cacheClients = true, logger: _logger, ...impitOptions } = options ?? {};
67
+ this.#impitOptions = impitOptions;
68
+ this.#cacheClients = cacheClients;
73
69
  }
74
70
  /**
75
71
  * @inheritDoc
76
72
  */
77
- async sendRequest(request, options) {
78
- const { response } = await this.getResponse(request, {}, options);
79
- // todo - cast shouldn't be needed here, impit returns `Uint8Array`
80
- return new ResponseWithUrl((await response.bytes()), response);
81
- }
82
- getStreamWithProgress(response) {
83
- const responseStream = Readable.fromWeb(response.body);
84
- let transferred = 0;
85
- const total = Number(response.headers.get('content-length') ?? 0);
86
- responseStream.on('data', (chunk) => {
87
- transferred += chunk.length;
73
+ async fetch(request, options) {
74
+ const { proxyUrl, redirect, signal, fingerprint, ignoreTlsErrors } = options ?? {};
75
+ const impitBrowser = this.resolveImpitBrowser(fingerprint);
76
+ const impit = this.getClient({
77
+ ...this.#impitOptions,
78
+ ...(impitBrowser ? { browser: impitBrowser } : {}),
79
+ // The per-request flag (from the crawler option or a MITM proxy session)
80
+ // can only enable ignoring, never override a constructor-level `true`.
81
+ ...(ignoreTlsErrors ? { ignoreTlsErrors: true } : {}),
82
+ proxyUrl,
83
+ followRedirects: redirect === 'follow',
88
84
  });
89
- const getDownloadProgress = () => {
90
- return {
91
- percent: Math.round((transferred / total) * 100),
92
- transferred,
93
- total,
94
- };
95
- };
96
- return [responseStream, getDownloadProgress];
85
+ const response = await impit.fetch(request, { signal: signal ?? undefined });
86
+ // todo - cast shouldn't be needed here, impit returns `Uint8Array`
87
+ return new ResponseWithUrl(response.body, response);
97
88
  }
98
- /**
99
- * @inheritDoc
100
- */
101
- async stream(request, options) {
102
- const { response } = await this.getResponse(request, {}, options);
103
- const [stream] = this.getStreamWithProgress(response);
104
- // Cast shouldn't be needed here, undici might have a slightly different `ReadableStream` type
105
- return new ResponseWithUrl(Readable.toWeb(stream), response);
89
+ resolveImpitBrowser(fingerprint) {
90
+ if (!fingerprint?.browser)
91
+ return undefined;
92
+ const cached = this.#impitBrowserByFingerprint.get(fingerprint);
93
+ if (cached)
94
+ return cached;
95
+ // impit can only impersonate Chrome and Firefox. Map other (Chromium-based or
96
+ // unsupported) families like `edge`/`safari` onto Chrome so the request still
97
+ // carries realistic browser headers instead of impit's bare `*/*` defaults.
98
+ const versions = IMPIT_VERSIONS_BY_BROWSER[fingerprint.browser] ?? IMPIT_VERSIONS_BY_BROWSER.chrome;
99
+ const picked = versions[Math.floor(Math.random() * versions.length)];
100
+ this.#impitBrowserByFingerprint.set(fingerprint, picked);
101
+ return picked;
106
102
  }
107
103
  }
108
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/impit-client",
3
- "version": "4.0.0-beta.18",
3
+ "version": "4.0.0-beta.180",
4
4
  "description": "impit-based HTTP client implementation for Crawlee. Impersonates browser requests to avoid bot detection.",
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"
@@ -46,15 +46,11 @@
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
- "peerDependencies": {
50
- "@crawlee/core": "4.0.0"
51
- },
52
- "devDependencies": {
53
- "@crawlee/core": "4.0.0"
54
- },
55
49
  "dependencies": {
56
- "@apify/datastructures": "^2.0.3",
57
- "impit": "^0.7.0",
50
+ "@apify/datastructures": "^3.0.1",
51
+ "@crawlee/http-client": "4.0.0-beta.180",
52
+ "@crawlee/types": "4.0.0-beta.180",
53
+ "impit": "^0.14.2",
58
54
  "tough-cookie": "^6.0.0"
59
55
  },
60
56
  "lerna": {
@@ -64,5 +60,5 @@
64
60
  }
65
61
  }
66
62
  },
67
- "gitHead": "cfaba5f4e082914d98d96d6539ab7b324d1c14c7"
63
+ "gitHead": "4adb7b0a96ee16824ee53d18f437961d9287f977"
68
64
  }
package/index.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEvF,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,OAAO,CAAC;AAMzD,eAAO,MAAM,OAAO;;;CAGV,CAAC;AAOX;;GAEG;AACH,qBAAa,eAAgB,YAAW,cAAc;IAClD,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAAU;IAEjC;;;;;OAKG;IACH,OAAO,CAAC,WAAW,CAA2F;IAE9G,OAAO,CAAC,SAAS;gBAgBL,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;IAOhF;;;;OAIG;YACW,WAAW;IA+CzB;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAOpF,OAAO,CAAC,qBAAqB;IAqB7B;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CAO7E"}
package/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}