@crawlee/basic 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.
Files changed (84) hide show
  1. package/README.md +14 -14
  2. package/index.d.ts +10 -2
  3. package/index.js +10 -1
  4. package/internals/autoscaling/autoscaled_pool.d.ts +196 -0
  5. package/internals/autoscaling/autoscaled_pool.js +385 -0
  6. package/internals/autoscaling/concurrency_system.d.ts +267 -0
  7. package/internals/autoscaling/concurrency_system.js +364 -0
  8. package/internals/autoscaling/cpu_load_signal.d.ts +43 -0
  9. package/internals/autoscaling/cpu_load_signal.js +46 -0
  10. package/internals/autoscaling/event_loop_load_signal.d.ts +51 -0
  11. package/internals/autoscaling/event_loop_load_signal.js +60 -0
  12. package/internals/autoscaling/index.d.ts +9 -0
  13. package/internals/autoscaling/index.js +9 -0
  14. package/internals/autoscaling/load_signal.d.ts +98 -0
  15. package/internals/autoscaling/load_signal.js +103 -0
  16. package/internals/autoscaling/memory_load_signal.d.ts +47 -0
  17. package/internals/autoscaling/memory_load_signal.js +104 -0
  18. package/internals/autoscaling/snapshotter.d.ts +84 -0
  19. package/internals/autoscaling/snapshotter.js +67 -0
  20. package/internals/autoscaling/storage_backend_load_signal.d.ts +56 -0
  21. package/internals/autoscaling/storage_backend_load_signal.js +73 -0
  22. package/internals/autoscaling/system_status.d.ts +122 -0
  23. package/internals/autoscaling/system_status.js +139 -0
  24. package/internals/autoscaling/weighted_avg.d.ts +5 -0
  25. package/internals/autoscaling/weighted_avg.js +14 -0
  26. package/internals/basic-crawler.d.ts +462 -332
  27. package/internals/basic-crawler.js +1378 -815
  28. package/internals/cookie_utils.d.ts +44 -0
  29. package/internals/cookie_utils.js +122 -0
  30. package/internals/crawler-run.d.ts +76 -0
  31. package/internals/crawler-run.js +142 -0
  32. package/internals/crawlers/context_pipeline.d.ts +70 -0
  33. package/internals/crawlers/context_pipeline.js +122 -0
  34. package/internals/crawlers/crawler_commons.d.ts +193 -0
  35. package/internals/crawlers/crawler_commons.js +1 -0
  36. package/internals/crawlers/error_snapshotter.d.ts +57 -0
  37. package/internals/crawlers/error_snapshotter.js +117 -0
  38. package/internals/crawlers/error_tracker.d.ts +54 -0
  39. package/internals/crawlers/error_tracker.js +308 -0
  40. package/internals/crawlers/index.d.ts +5 -0
  41. package/internals/crawlers/index.js +4 -0
  42. package/internals/crawlers/internals/types.d.ts +7 -0
  43. package/internals/crawlers/internals/types.js +1 -0
  44. package/internals/crawlers/statistics.d.ts +318 -0
  45. package/internals/crawlers/statistics.js +525 -0
  46. package/internals/enqueue_links/enqueue_links.d.ts +152 -0
  47. package/internals/enqueue_links/enqueue_links.js +76 -0
  48. package/internals/enqueue_links/index.d.ts +2 -0
  49. package/internals/enqueue_links/index.js +2 -0
  50. package/internals/enqueue_links/shared.d.ts +98 -0
  51. package/internals/enqueue_links/shared.js +259 -0
  52. package/internals/errors.d.ts +58 -0
  53. package/internals/errors.js +72 -0
  54. package/internals/http.d.ts +9 -0
  55. package/internals/http.js +28 -0
  56. package/internals/request-timeout.d.ts +48 -0
  57. package/internals/request-timeout.js +73 -0
  58. package/internals/router.d.ts +306 -0
  59. package/internals/router.js +310 -0
  60. package/internals/send-request.d.ts +4 -4
  61. package/internals/send-request.js +22 -11
  62. package/internals/session_pool/consts.d.ts +3 -0
  63. package/internals/session_pool/consts.js +3 -0
  64. package/internals/session_pool/errors.d.ts +7 -0
  65. package/internals/session_pool/errors.js +11 -0
  66. package/internals/session_pool/fingerprint.d.ts +9 -0
  67. package/internals/session_pool/fingerprint.js +30 -0
  68. package/internals/session_pool/index.d.ts +4 -0
  69. package/internals/session_pool/index.js +4 -0
  70. package/internals/session_pool/session.d.ts +153 -0
  71. package/internals/session_pool/session.js +221 -0
  72. package/internals/session_pool/session_pool.d.ts +242 -0
  73. package/internals/session_pool/session_pool.js +429 -0
  74. package/internals/sitemap_request_loader.d.ts +196 -0
  75. package/internals/sitemap_request_loader.js +432 -0
  76. package/internals/throttling_request_manager.d.ts +216 -0
  77. package/internals/throttling_request_manager.js +826 -0
  78. package/package.json +19 -13
  79. package/index.d.ts.map +0 -1
  80. package/index.js.map +0 -1
  81. package/internals/basic-crawler.d.ts.map +0 -1
  82. package/internals/basic-crawler.js.map +0 -1
  83. package/internals/send-request.d.ts.map +0 -1
  84. package/internals/send-request.js.map +0 -1
@@ -1,4 +1,6 @@
1
- import { type BaseHttpClient, type HttpRequestOptions, type Request, type Session } from '@crawlee/core';
1
+ import type { Request as CrawleeRequest } from '@crawlee/core';
2
+ import type { BaseHttpClient } from '@crawlee/http-client';
3
+ import type { HttpRequestOptions, ISession, SendRequestOptions } from '@crawlee/types';
2
4
  /**
3
5
  * Prepares a function to be used as the `sendRequest` context helper.
4
6
  *
@@ -6,7 +8,5 @@ import { type BaseHttpClient, type HttpRequestOptions, type Request, type Sessio
6
8
  * @param httpClient The HTTP client that will perform the requests.
7
9
  * @param originRequest The crawling request being processed.
8
10
  * @param session The user session associated with the current request.
9
- * @param getProxyUrl A function that will return the proxy URL that should be used for handling the request.
10
11
  */
11
- export declare function createSendRequest(httpClient: BaseHttpClient, originRequest: Request, session: Session | undefined): (overrideOptions?: Partial<HttpRequestOptions>) => Promise<Response>;
12
- //# sourceMappingURL=send-request.d.ts.map
12
+ export declare function createSendRequest(httpClient: BaseHttpClient, originRequest: CrawleeRequest, session: ISession): (overrideRequest?: Partial<HttpRequestOptions>, overrideOptions?: SendRequestOptions) => Promise<Response>;
@@ -5,18 +5,29 @@
5
5
  * @param httpClient The HTTP client that will perform the requests.
6
6
  * @param originRequest The crawling request being processed.
7
7
  * @param session The user session associated with the current request.
8
- * @param getProxyUrl A function that will return the proxy URL that should be used for handling the request.
9
8
  */
10
9
  export function createSendRequest(httpClient, originRequest, session) {
11
- return async (overrideOptions = {}) => {
12
- const cookieJar = session
13
- ? {
14
- getCookieString: async (url) => session.getCookieString(url),
15
- setCookie: async (rawCookie, url) => session.setCookie(rawCookie, url),
16
- ...overrideOptions?.cookieJar,
17
- }
18
- : overrideOptions?.cookieJar;
19
- return httpClient.sendRequest(originRequest.intoFetchAPIRequest(), { session, cookieJar: cookieJar });
10
+ return async (overrideRequest = {}, overrideOptions = {}) => {
11
+ const baseRequest = originRequest.intoFetchAPIRequest();
12
+ const mergedUrl = overrideRequest.url ?? baseRequest.url;
13
+ const mergedMethod = overrideRequest.method ?? baseRequest.method;
14
+ const mergedHeaders = new Headers(baseRequest.headers);
15
+ if (overrideRequest.headers) {
16
+ overrideRequest.headers.forEach((value, key) => {
17
+ mergedHeaders.set(key, value);
18
+ });
19
+ }
20
+ const request = new Request(mergedUrl, {
21
+ method: mergedMethod,
22
+ headers: mergedHeaders,
23
+ body: overrideRequest.body ?? baseRequest.body,
24
+ });
25
+ return httpClient.sendRequest(request, {
26
+ session,
27
+ cookieJar: overrideOptions?.cookieJar ?? session.cookieJar,
28
+ timeoutMillis: overrideOptions.timeoutMillis,
29
+ signal: overrideOptions.signal,
30
+ ignoreTlsErrors: overrideOptions.ignoreTlsErrors,
31
+ });
20
32
  };
21
33
  }
22
- //# sourceMappingURL=send-request.js.map
@@ -0,0 +1,3 @@
1
+ export declare const BLOCKED_STATUS_CODES: number[];
2
+ export declare const PERSIST_STATE_KEY = "CRAWLEE_SESSION_POOL_STATE";
3
+ export declare const MAX_POOL_SIZE = 1000;
@@ -0,0 +1,3 @@
1
+ export const BLOCKED_STATUS_CODES = [401, 403, 429];
2
+ export const PERSIST_STATE_KEY = 'CRAWLEE_SESSION_POOL_STATE';
3
+ export const MAX_POOL_SIZE = 1000;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @ignore
3
+ */
4
+ export declare class CookieParseError extends Error {
5
+ readonly cookieHeaderString: unknown;
6
+ constructor(cookieHeaderString: unknown);
7
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @ignore
3
+ */
4
+ export class CookieParseError extends Error {
5
+ cookieHeaderString;
6
+ constructor(cookieHeaderString) {
7
+ super(`Could not parse cookie header string: ${cookieHeaderString}`);
8
+ this.cookieHeaderString = cookieHeaderString;
9
+ Error.captureStackTrace(this, CookieParseError);
10
+ }
11
+ }
@@ -0,0 +1,9 @@
1
+ import type { SessionFingerprint } from '@crawlee/types';
2
+ /**
3
+ * Build a {@link SessionFingerprint} whose `platform` matches the host OS
4
+ * and whose `browser`/`device` are randomized within the realistic profiles for
5
+ * that platform. Used by {@link SessionPool} as the default fingerprint for
6
+ * freshly created sessions; callers can override by passing their own
7
+ * `fingerprint` in `sessionOptions`.
8
+ */
9
+ export declare function createDefaultSessionFingerprint(): SessionFingerprint;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * (browser, platform, device) combinations that correspond to setups people
3
+ * actually run. Anything not listed here (e.g. `edge` on android, `safari` on
4
+ * windows, `desktop` mobile platforms) is left out so a randomized default
5
+ * never produces a fingerprint that would itself be a giveaway.
6
+ */
7
+ const PROFILES_BY_PLATFORM = [
8
+ { browser: 'chrome', platform: 'windows', device: 'desktop' },
9
+ { browser: 'firefox', platform: 'windows', device: 'desktop' },
10
+ { browser: 'edge', platform: 'windows', device: 'desktop' },
11
+ { browser: 'chrome', platform: 'macos', device: 'desktop' },
12
+ { browser: 'firefox', platform: 'macos', device: 'desktop' },
13
+ { browser: 'safari', platform: 'macos', device: 'desktop' },
14
+ { browser: 'edge', platform: 'macos', device: 'desktop' },
15
+ { browser: 'chrome', platform: 'linux', device: 'desktop' },
16
+ { browser: 'firefox', platform: 'linux', device: 'desktop' },
17
+ { browser: 'chrome', platform: 'android', device: 'mobile' },
18
+ { browser: 'firefox', platform: 'android', device: 'mobile' },
19
+ { browser: 'safari', platform: 'ios', device: 'mobile' },
20
+ ];
21
+ /**
22
+ * Build a {@link SessionFingerprint} whose `platform` matches the host OS
23
+ * and whose `browser`/`device` are randomized within the realistic profiles for
24
+ * that platform. Used by {@link SessionPool} as the default fingerprint for
25
+ * freshly created sessions; callers can override by passing their own
26
+ * `fingerprint` in `sessionOptions`.
27
+ */
28
+ export function createDefaultSessionFingerprint() {
29
+ return { ...PROFILES_BY_PLATFORM[Math.floor(Math.random() * PROFILES_BY_PLATFORM.length)] };
30
+ }
@@ -0,0 +1,4 @@
1
+ export * from './errors.js';
2
+ export * from './session.js';
3
+ export * from './session_pool.js';
4
+ export * from './consts.js';
@@ -0,0 +1,4 @@
1
+ export * from './errors.js';
2
+ export * from './session.js';
3
+ export * from './session_pool.js';
4
+ export * from './consts.js';
@@ -0,0 +1,153 @@
1
+ import type { CrawleeLogger } from '@crawlee/core';
2
+ import type { Dictionary, ISession, ProxyInfo, SessionFingerprint, SessionState } from '@crawlee/types';
3
+ import { CookieJar } from 'tough-cookie';
4
+ export interface SessionOptions {
5
+ /** Id of session used for generating fingerprints. It is used as proxy session name. */
6
+ id?: string;
7
+ /**
8
+ * Number of seconds after which the session is considered as expired.
9
+ * @default 3000
10
+ */
11
+ maxAgeSecs?: number;
12
+ /** Object where custom user data can be stored. For example custom headers. */
13
+ userData?: Dictionary;
14
+ /**
15
+ * Maximum number of marking session as blocked usage.
16
+ * If the `errorScore` reaches the `maxErrorScore` session is marked as block and it is thrown away.
17
+ * It starts at 0. Calling the `markBad` function increases the `errorScore` by 1.
18
+ * Calling the `markGood` will decrease the `errorScore` by `errorScoreDecrement`
19
+ * @default 3
20
+ */
21
+ maxErrorScore?: number;
22
+ /**
23
+ * It is used for healing the session.
24
+ * For example: if your session is marked bad two times, but it is successful on the third attempt it's errorScore
25
+ * is decremented by this number.
26
+ * @default 0.5
27
+ */
28
+ errorScoreDecrement?: number;
29
+ /** Date of creation. */
30
+ createdAt?: Date;
31
+ /**
32
+ * Date of expiration.
33
+ * @default createdAt + maxAgeSecs
34
+ */
35
+ expiresAt?: Date;
36
+ /**
37
+ * Indicates how many times the session has been used.
38
+ * @default 0
39
+ */
40
+ usageCount?: number;
41
+ /**
42
+ * Session should be used only a limited amount of times.
43
+ * This number indicates how many times the session is going to be used, before it is thrown away.
44
+ * @default 50
45
+ */
46
+ maxUsageCount?: number;
47
+ /**
48
+ * Marks the session as already retired. Used when restoring a previously persisted session
49
+ * so that `isUsable()` reflects the terminal state regardless of error score or usage count.
50
+ * @default false
51
+ */
52
+ retired?: boolean;
53
+ log?: CrawleeLogger;
54
+ errorScore?: number;
55
+ cookieJar?: CookieJar;
56
+ proxyInfo?: ProxyInfo;
57
+ /**
58
+ * Browser / HTTP client fingerprint tied to this session. Backends use this to make
59
+ * repeated requests with the same session look consistent (same user-agent, headers,
60
+ * TLS profile). See {@link SessionFingerprint}.
61
+ */
62
+ fingerprint?: SessionFingerprint;
63
+ }
64
+ /**
65
+ * Sessions are used to store information such as cookies and can be used for generating fingerprints and proxy sessions.
66
+ * You can imagine each session as a specific user, with its own cookies, IP (via proxy) and potentially a unique browser fingerprint.
67
+ * Session internal state can be enriched with custom user data for example some authorization tokens and specific headers in general.
68
+ * @category Scaling
69
+ */
70
+ export declare class Session implements ISession {
71
+ #private;
72
+ readonly id: string;
73
+ readonly userData: Dictionary;
74
+ get errorScore(): number;
75
+ get usageCount(): number;
76
+ get maxErrorScore(): number;
77
+ get errorScoreDecrement(): number;
78
+ get expiresAt(): Date;
79
+ get createdAt(): Date;
80
+ get maxUsageCount(): number;
81
+ get cookieJar(): CookieJar;
82
+ get proxyInfo(): ProxyInfo | undefined;
83
+ get fingerprint(): SessionFingerprint | undefined;
84
+ set fingerprint(fingerprint: SessionFingerprint | undefined);
85
+ /**
86
+ * `true` once {@link Session.retire|`retire()`} has been called. Retirement is terminal:
87
+ * a retired session is never picked by the pool and cannot be revived via `markGood()`.
88
+ */
89
+ get retired(): boolean;
90
+ /**
91
+ * Session configuration.
92
+ */
93
+ constructor(options?: SessionOptions);
94
+ /**
95
+ * Indicates whether the session is blocked.
96
+ * Session is blocked once it reaches the `maxErrorScore`.
97
+ */
98
+ isBlocked(): boolean;
99
+ /**
100
+ * Indicates whether the session is expired.
101
+ * Session expiration is determined by the `maxAgeSecs`.
102
+ * Once the session is older than `createdAt + maxAgeSecs` the session is considered expired.
103
+ */
104
+ isExpired(): boolean;
105
+ /**
106
+ * Indicates whether the session is used maximum number of times.
107
+ * Session maximum usage count can be changed by `maxUsageCount` parameter.
108
+ */
109
+ isMaxUsageCountReached(): boolean;
110
+ /**
111
+ * Indicates whether the session can be used for next requests.
112
+ * Session is usable when it is not retired, not expired, not blocked and the maximum usage count has not be reached.
113
+ */
114
+ isUsable(): boolean;
115
+ /**
116
+ * This method should be called after a successful session usage.
117
+ * It increases `usageCount` and potentially lowers the `errorScore` by the `errorScoreDecrement`.
118
+ */
119
+ markGood(): void;
120
+ /**
121
+ * Gets session state for persistence in KeyValueStore.
122
+ * @internal
123
+ */
124
+ getState(): SessionState;
125
+ /**
126
+ * Permanently retires the session — `isUsable()` will return `false` from here on,
127
+ * and no `markGood()` / `markBad()` can revive it. Calling `retire()` again is a no-op.
128
+ *
129
+ * Use this when you're confident the session itself is the problem (e.g. a `403` response).
130
+ * For transient external failures (such as `5XX` responses), use `markBad()` instead.
131
+ */
132
+ retire(): void;
133
+ /**
134
+ * Increases usage and error count.
135
+ * Should be used when the session has been used unsuccessfully. For example because of timeouts.
136
+ */
137
+ markBad(): void;
138
+ /**
139
+ * Returns cookies saved with the session in the typical
140
+ * key1=value1; key2=value2 format, ready to be used in
141
+ * a cookie header or elsewhere.
142
+ * @returns Represents `Cookie` header.
143
+ */
144
+ getCookieString(url: string): Promise<string>;
145
+ /**
146
+ * Sets a cookie within this session for the specific URL.
147
+ */
148
+ setCookie(rawCookie: string, url: string): Promise<void>;
149
+ /**
150
+ * Checks if session is not usable. if it is not retires the session.
151
+ */
152
+ private maybeSelfRetire;
153
+ }
@@ -0,0 +1,221 @@
1
+ import { serviceLocator, validators } from '@crawlee/core';
2
+ import { parseArgument, schemas } from '@crawlee/utils/internal';
3
+ import { CookieJar } from 'tough-cookie';
4
+ import { z } from 'zod';
5
+ import { cryptoRandomObjectId } from '@apify/utilities';
6
+ // `schemas.anyObject` passes values through by reference (object schemas return a pruned plain
7
+ // copy), so class instances like cookie jars and loggers keep their prototype.
8
+ const sessionOptionsSchema = z.strictObject({
9
+ id: z.string().default(() => `session_${cryptoRandomObjectId(10)}`),
10
+ cookieJar: schemas.anyObject.default(() => new CookieJar()),
11
+ proxyInfo: schemas.anyObject.optional(),
12
+ maxAgeSecs: schemas.anyNumber.default(3000),
13
+ userData: schemas.anyObject.default(() => ({})),
14
+ maxErrorScore: schemas.anyNumber.default(3),
15
+ errorScoreDecrement: schemas.anyNumber.default(0.5),
16
+ createdAt: z.date().default(() => new Date()),
17
+ expiresAt: z.date().optional(),
18
+ usageCount: schemas.anyNumber.default(0),
19
+ errorScore: schemas.anyNumber.default(0),
20
+ maxUsageCount: schemas.anyNumber.default(50),
21
+ retired: z.boolean().default(false),
22
+ log: validators.logger.default(() => serviceLocator.getLogger()),
23
+ fingerprint: schemas.anyObject.optional(),
24
+ });
25
+ /**
26
+ * Sessions are used to store information such as cookies and can be used for generating fingerprints and proxy sessions.
27
+ * You can imagine each session as a specific user, with its own cookies, IP (via proxy) and potentially a unique browser fingerprint.
28
+ * Session internal state can be enriched with custom user data for example some authorization tokens and specific headers in general.
29
+ * @category Scaling
30
+ */
31
+ export class Session {
32
+ id;
33
+ userData;
34
+ #maxErrorScore;
35
+ #errorScoreDecrement;
36
+ #createdAt;
37
+ #expiresAt;
38
+ #usageCount;
39
+ #maxUsageCount;
40
+ #errorScore;
41
+ #retired = false;
42
+ #proxyInfo;
43
+ #cookieJar;
44
+ #fingerprint;
45
+ #log;
46
+ get errorScore() {
47
+ return this.#errorScore;
48
+ }
49
+ get usageCount() {
50
+ return this.#usageCount;
51
+ }
52
+ get maxErrorScore() {
53
+ return this.#maxErrorScore;
54
+ }
55
+ get errorScoreDecrement() {
56
+ return this.#errorScoreDecrement;
57
+ }
58
+ get expiresAt() {
59
+ return this.#expiresAt;
60
+ }
61
+ get createdAt() {
62
+ return this.#createdAt;
63
+ }
64
+ get maxUsageCount() {
65
+ return this.#maxUsageCount;
66
+ }
67
+ get cookieJar() {
68
+ return this.#cookieJar;
69
+ }
70
+ get proxyInfo() {
71
+ return this.#proxyInfo;
72
+ }
73
+ get fingerprint() {
74
+ return this.#fingerprint;
75
+ }
76
+ set fingerprint(fingerprint) {
77
+ this.#fingerprint = fingerprint;
78
+ }
79
+ /**
80
+ * `true` once {@link Session.retire|`retire()`} has been called. Retirement is terminal:
81
+ * a retired session is never picked by the pool and cannot be revived via `markGood()`.
82
+ */
83
+ get retired() {
84
+ return this.#retired;
85
+ }
86
+ /**
87
+ * Session configuration.
88
+ */
89
+ constructor(options = {}) {
90
+ const { id, cookieJar, proxyInfo, maxAgeSecs, userData, maxErrorScore, errorScoreDecrement, createdAt, usageCount, errorScore, maxUsageCount, retired, log, fingerprint,
91
+ // Anchored to `createdAt` rather than to "now", so the documented `createdAt + maxAgeSecs` holds.
92
+ expiresAt = new Date(createdAt.getTime() + maxAgeSecs * 1000), } = parseArgument(options, sessionOptionsSchema);
93
+ this.#log = log.child({ prefix: 'Session' });
94
+ this.#cookieJar = cookieJar.setCookie ? cookieJar : CookieJar.fromJSON(JSON.stringify(cookieJar));
95
+ this.#proxyInfo = proxyInfo;
96
+ this.#fingerprint = fingerprint;
97
+ this.id = id;
98
+ this.userData = userData;
99
+ this.#maxErrorScore = maxErrorScore;
100
+ this.#errorScoreDecrement = errorScoreDecrement;
101
+ // Internal
102
+ this.#expiresAt = expiresAt;
103
+ this.#createdAt = createdAt;
104
+ this.#usageCount = usageCount; // indicates how many times the session has been used
105
+ this.#errorScore = errorScore; // indicates number of markBaded request with the session
106
+ this.#maxUsageCount = maxUsageCount;
107
+ this.#retired = retired;
108
+ }
109
+ /**
110
+ * Indicates whether the session is blocked.
111
+ * Session is blocked once it reaches the `maxErrorScore`.
112
+ */
113
+ isBlocked() {
114
+ return this.errorScore >= this.maxErrorScore;
115
+ }
116
+ /**
117
+ * Indicates whether the session is expired.
118
+ * Session expiration is determined by the `maxAgeSecs`.
119
+ * Once the session is older than `createdAt + maxAgeSecs` the session is considered expired.
120
+ */
121
+ isExpired() {
122
+ return this.expiresAt <= new Date();
123
+ }
124
+ /**
125
+ * Indicates whether the session is used maximum number of times.
126
+ * Session maximum usage count can be changed by `maxUsageCount` parameter.
127
+ */
128
+ isMaxUsageCountReached() {
129
+ return this.usageCount >= this.maxUsageCount;
130
+ }
131
+ /**
132
+ * Indicates whether the session can be used for next requests.
133
+ * Session is usable when it is not retired, not expired, not blocked and the maximum usage count has not be reached.
134
+ */
135
+ isUsable() {
136
+ return !this.#retired && !this.isBlocked() && !this.isExpired() && !this.isMaxUsageCountReached();
137
+ }
138
+ /**
139
+ * This method should be called after a successful session usage.
140
+ * It increases `usageCount` and potentially lowers the `errorScore` by the `errorScoreDecrement`.
141
+ */
142
+ markGood() {
143
+ this.#usageCount += 1;
144
+ if (this.#errorScore > 0) {
145
+ this.#errorScore -= this.#errorScoreDecrement;
146
+ }
147
+ this.maybeSelfRetire();
148
+ }
149
+ /**
150
+ * Gets session state for persistence in KeyValueStore.
151
+ * @internal
152
+ */
153
+ getState() {
154
+ return {
155
+ id: this.id,
156
+ cookieJar: this.cookieJar.toJSON(),
157
+ proxyInfo: this.#proxyInfo,
158
+ userData: this.userData,
159
+ fingerprint: this.#fingerprint,
160
+ maxErrorScore: this.maxErrorScore,
161
+ errorScoreDecrement: this.errorScoreDecrement,
162
+ expiresAt: this.expiresAt.toISOString(),
163
+ createdAt: this.createdAt.toISOString(),
164
+ usageCount: this.usageCount,
165
+ maxUsageCount: this.maxUsageCount,
166
+ errorScore: this.errorScore,
167
+ retired: this.#retired,
168
+ };
169
+ }
170
+ /**
171
+ * Permanently retires the session — `isUsable()` will return `false` from here on,
172
+ * and no `markGood()` / `markBad()` can revive it. Calling `retire()` again is a no-op.
173
+ *
174
+ * Use this when you're confident the session itself is the problem (e.g. a `403` response).
175
+ * For transient external failures (such as `5XX` responses), use `markBad()` instead.
176
+ */
177
+ retire() {
178
+ if (this.#retired)
179
+ return;
180
+ this.#errorScore += this.#maxErrorScore;
181
+ this.#usageCount += 1;
182
+ this.#retired = true;
183
+ }
184
+ /**
185
+ * Increases usage and error count.
186
+ * Should be used when the session has been used unsuccessfully. For example because of timeouts.
187
+ */
188
+ markBad() {
189
+ this.#errorScore += 1;
190
+ this.#usageCount += 1;
191
+ this.maybeSelfRetire();
192
+ }
193
+ /**
194
+ * Returns cookies saved with the session in the typical
195
+ * key1=value1; key2=value2 format, ready to be used in
196
+ * a cookie header or elsewhere.
197
+ * @returns Represents `Cookie` header.
198
+ */
199
+ async getCookieString(url) {
200
+ return this.cookieJar.getCookieString(url, {});
201
+ }
202
+ /**
203
+ * Sets a cookie within this session for the specific URL.
204
+ */
205
+ async setCookie(rawCookie, url) {
206
+ try {
207
+ await this.cookieJar.setCookie(rawCookie, url);
208
+ }
209
+ catch (e) {
210
+ this.#log.warning('Could not set cookie.', { url, error: e.message });
211
+ }
212
+ }
213
+ /**
214
+ * Checks if session is not usable. if it is not retires the session.
215
+ */
216
+ maybeSelfRetire() {
217
+ if (!this.isUsable()) {
218
+ this.retire();
219
+ }
220
+ }
221
+ }