@crawlee/core 4.0.0-rc.0 → 4.0.0-rc.1

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 (122) hide show
  1. package/README.md +1 -1
  2. package/configuration.d.ts +15 -46
  3. package/configuration.js +8 -20
  4. package/errors.d.ts +12 -53
  5. package/errors.js +13 -66
  6. package/events/index.d.ts +1 -0
  7. package/events/local_event_manager.d.ts +0 -7
  8. package/events/local_event_manager.js +8 -8
  9. package/events/system_info.d.ts +38 -0
  10. package/index.d.ts +2 -8
  11. package/index.js +4 -8
  12. package/internal.d.ts +8 -0
  13. package/internal.js +9 -0
  14. package/log.d.ts +10 -11
  15. package/log.js +53 -25
  16. package/memory-storage/memory-storage.d.ts +12 -7
  17. package/memory-storage/memory-storage.js +45 -17
  18. package/memory-storage/resource-clients/dataset.d.ts +0 -5
  19. package/memory-storage/resource-clients/dataset.js +17 -20
  20. package/memory-storage/resource-clients/key-value-store.d.ts +0 -9
  21. package/memory-storage/resource-clients/key-value-store.js +11 -33
  22. package/memory-storage/resource-clients/request-queue.d.ts +0 -22
  23. package/memory-storage/resource-clients/request-queue.js +57 -53
  24. package/package.json +16 -18
  25. package/proxy_configuration.d.ts +20 -23
  26. package/proxy_configuration.js +18 -12
  27. package/recoverable_state.d.ts +28 -6
  28. package/recoverable_state.js +51 -14
  29. package/request.d.ts +18 -104
  30. package/request.js +41 -220
  31. package/serialization.js +3 -3
  32. package/service_locator.d.ts +3 -0
  33. package/service_locator.js +2 -0
  34. package/storages/dataset.d.ts +9 -15
  35. package/storages/dataset.js +22 -14
  36. package/storages/index.d.ts +3 -4
  37. package/storages/index.js +1 -4
  38. package/storages/key_value_store.d.ts +12 -46
  39. package/storages/key_value_store.js +31 -47
  40. package/storages/key_value_store_codec.js +6 -11
  41. package/storages/request_dedup_cache.d.ts +0 -2
  42. package/storages/request_dedup_cache.js +8 -8
  43. package/storages/request_list.d.ts +6 -82
  44. package/storages/request_list.js +175 -179
  45. package/storages/request_loader.d.ts +48 -22
  46. package/storages/request_loader.js +36 -1
  47. package/storages/request_manager.d.ts +86 -0
  48. package/storages/request_manager_tandem.d.ts +13 -28
  49. package/storages/request_manager_tandem.js +46 -43
  50. package/storages/request_queue.d.ts +19 -49
  51. package/storages/request_queue.js +92 -88
  52. package/storages/storage_instance_manager.d.ts +1 -2
  53. package/storages/storage_instance_manager.js +4 -4
  54. package/storages/transaction.d.ts +27 -9
  55. package/storages/transaction.js +56 -11
  56. package/storages/utils.d.ts +2 -2
  57. package/validators.d.ts +3 -2
  58. package/validators.js +3 -2
  59. package/autoscaling/autoscaled_pool.d.ts +0 -195
  60. package/autoscaling/autoscaled_pool.js +0 -386
  61. package/autoscaling/concurrency_system.d.ts +0 -268
  62. package/autoscaling/concurrency_system.js +0 -362
  63. package/autoscaling/cpu_load_signal.d.ts +0 -43
  64. package/autoscaling/cpu_load_signal.js +0 -47
  65. package/autoscaling/event_loop_load_signal.d.ts +0 -51
  66. package/autoscaling/event_loop_load_signal.js +0 -60
  67. package/autoscaling/index.d.ts +0 -9
  68. package/autoscaling/index.js +0 -9
  69. package/autoscaling/load_signal.d.ts +0 -100
  70. package/autoscaling/load_signal.js +0 -105
  71. package/autoscaling/memory_load_signal.d.ts +0 -47
  72. package/autoscaling/memory_load_signal.js +0 -106
  73. package/autoscaling/snapshotter.d.ts +0 -84
  74. package/autoscaling/snapshotter.js +0 -67
  75. package/autoscaling/storage_backend_load_signal.d.ts +0 -56
  76. package/autoscaling/storage_backend_load_signal.js +0 -73
  77. package/autoscaling/system_status.d.ts +0 -159
  78. package/autoscaling/system_status.js +0 -139
  79. package/autoscaling/weighted_avg.d.ts +0 -5
  80. package/autoscaling/weighted_avg.js +0 -14
  81. package/cookie_utils.d.ts +0 -44
  82. package/cookie_utils.js +0 -122
  83. package/crawlers/context_pipeline.d.ts +0 -70
  84. package/crawlers/context_pipeline.js +0 -122
  85. package/crawlers/crawler_commons.d.ts +0 -159
  86. package/crawlers/error_snapshotter.d.ts +0 -57
  87. package/crawlers/error_snapshotter.js +0 -117
  88. package/crawlers/error_tracker.d.ts +0 -54
  89. package/crawlers/error_tracker.js +0 -308
  90. package/crawlers/index.d.ts +0 -5
  91. package/crawlers/index.js +0 -4
  92. package/crawlers/internals/types.d.ts +0 -7
  93. package/crawlers/internals/types.js +0 -1
  94. package/crawlers/statistics.d.ts +0 -328
  95. package/crawlers/statistics.js +0 -536
  96. package/enqueue_links/enqueue_links.d.ts +0 -156
  97. package/enqueue_links/enqueue_links.js +0 -78
  98. package/enqueue_links/index.d.ts +0 -2
  99. package/enqueue_links/index.js +0 -2
  100. package/enqueue_links/shared.d.ts +0 -93
  101. package/enqueue_links/shared.js +0 -239
  102. package/http.d.ts +0 -9
  103. package/http.js +0 -28
  104. package/router.d.ts +0 -306
  105. package/router.js +0 -309
  106. package/session_pool/consts.d.ts +0 -3
  107. package/session_pool/consts.js +0 -3
  108. package/session_pool/errors.d.ts +0 -7
  109. package/session_pool/errors.js +0 -11
  110. package/session_pool/fingerprint.d.ts +0 -9
  111. package/session_pool/fingerprint.js +0 -30
  112. package/session_pool/index.d.ts +0 -4
  113. package/session_pool/index.js +0 -4
  114. package/session_pool/session.d.ts +0 -150
  115. package/session_pool/session.js +0 -220
  116. package/session_pool/session_pool.d.ts +0 -240
  117. package/session_pool/session_pool.js +0 -394
  118. package/storages/sitemap_request_loader.d.ts +0 -201
  119. package/storages/sitemap_request_loader.js +0 -438
  120. package/storages/throttling_request_manager.d.ts +0 -239
  121. package/storages/throttling_request_manager.js +0 -646
  122. /package/{crawlers/crawler_commons.js → events/system_info.js} +0 -0
@@ -1,159 +0,0 @@
1
- import type { LoadSignal } from './load_signal.js';
2
- import type { Snapshotter } from './snapshotter.js';
3
- /**
4
- * Represents the current status of the system.
5
- */
6
- export interface SystemInfo {
7
- /** If false, system is being overloaded. */
8
- isSystemIdle: boolean;
9
- memInfo: LoadSignalInfo;
10
- eventLoopInfo: LoadSignalInfo;
11
- cpuInfo: LoadSignalInfo;
12
- storageBackendInfo: LoadSignalInfo;
13
- memTotalBytes?: number;
14
- memCurrentBytes?: number;
15
- /**
16
- * Platform only property
17
- * @internal
18
- */
19
- cpuCurrentUsage?: number;
20
- /**
21
- * Platform only property
22
- * @internal
23
- */
24
- isCpuOverloaded?: boolean;
25
- /**
26
- * Platform only property
27
- * @internal
28
- */
29
- createdAt?: Date;
30
- /**
31
- * Status of additional load signals beyond the built-in four.
32
- * Keys are `LoadSignal.name` values, values are overload info.
33
- */
34
- loadSignalInfo?: Record<string, LoadSignalInfo>;
35
- }
36
- /**
37
- * How far back the *current* system status looks by default — the window that gates task dispatch.
38
- * @internal
39
- */
40
- export declare const DEFAULT_CURRENT_HISTORY_SECS = 5;
41
- /**
42
- * How far back the *historical* system status looks by default — the window autoscaling decisions are based on, and
43
- * therefore how much history the signals are asked to retain.
44
- * @internal
45
- */
46
- export declare const DEFAULT_SNAPSHOT_HISTORY_SECS = 30;
47
- /**
48
- * An implementation detail of the {@link ConcurrencySystem} — configure it through
49
- * {@link ConcurrencySystemOptions} (`loadSignals`, `currentHistorySecs` and `snapshotHistorySecs`).
50
- * @internal
51
- */
52
- export interface SystemStatusOptions {
53
- /**
54
- * Defines max age of snapshots used in the {@link SystemStatus.getCurrentStatus} measurement.
55
- * @default 5
56
- */
57
- currentHistorySecs?: number;
58
- /**
59
- * Defines max age of snapshots used in the {@link SystemStatus.getHistoricalStatus} measurement — the window
60
- * autoscaling decisions are based on. Applied uniformly to every signal, built-in or custom, so that a signal's
61
- * private retention cannot silently widen the window.
62
- * @default 30
63
- */
64
- historySecs?: number;
65
- /**
66
- * The `Snapshotter` whose built-in signals are evaluated.
67
- */
68
- snapshotter: Snapshotter;
69
- /**
70
- * Additional load signals to include in the system status evaluation.
71
- * These are evaluated alongside the built-in memory, CPU, event loop,
72
- * and storage backend signals. If any signal reports overload, the system
73
- * is considered overloaded. Each signal carries its own overload ratio.
74
- */
75
- loadSignals?: LoadSignal[];
76
- }
77
- export interface LoadSignalInfo {
78
- isOverloaded: boolean;
79
- limitRatio: number;
80
- actualRatio: number;
81
- }
82
- export interface FinalStatistics {
83
- requestsFinished: number;
84
- requestsFailed: number;
85
- retryHistogram: number[];
86
- requestAvgFailedDurationMillis: number;
87
- requestAvgFinishedDurationMillis: number;
88
- requestsFinishedPerMinute: number;
89
- requestsFailedPerMinute: number;
90
- requestTotalDurationMillis: number;
91
- requestsTotal: number;
92
- crawlerRuntimeMillis: number;
93
- }
94
- /**
95
- * Reads the overload verdict of every signal — the {@link Snapshotter}'s built-in four plus any custom ones — and
96
- * combines them into a {@link SystemInfo}: each signal is a time-weighted average of its snapshots, and the system
97
- * is overloaded whenever at least one of them is.
98
- *
99
- * Evaluated over two windows, both requested explicitly from every signal so that a signal's private retention cannot
100
- * widen what it contributes: a short `currentHistorySecs` one ({@link SystemStatus.getCurrentStatus}, gating task
101
- * dispatch) and a longer `historySecs` one ({@link SystemStatus.getHistoricalStatus}, driving autoscaling).
102
- *
103
- * An implementation detail of the {@link ConcurrencySystem}, configured through
104
- * {@link ConcurrencySystemOptions}.
105
- * @internal
106
- */
107
- export declare class SystemStatus {
108
- #private;
109
- constructor(options: SystemStatusOptions);
110
- /**
111
- * The widest window any signal will be queried with, and therefore exactly how much history the signals are asked
112
- * to retain when they start. Derived here, where the windows are resolved, so nothing has to reapply their
113
- * defaults.
114
- */
115
- get maxSampleWindowMillis(): number;
116
- /**
117
- * Signal names are the keys of the reported {@link SystemInfo}, so a duplicate would leave a status object that
118
- * contradicts actual behavior: both signals are still evaluated (any overloaded one holds concurrency down), but
119
- * only the last is reported.
120
- */
121
- private assertUniqueSignalNames;
122
- /**
123
- * Returns an {@link SystemInfo} object with the following structure:
124
- *
125
- * ```javascript
126
- * {
127
- * isSystemIdle: Boolean,
128
- * memInfo: Object,
129
- * eventLoopInfo: Object,
130
- * cpuInfo: Object
131
- * }
132
- * ```
133
- *
134
- * Where the `isSystemIdle` property is set to `false` if the system
135
- * has been overloaded in the last `options.currentHistorySecs` seconds,
136
- * and `true` otherwise.
137
- */
138
- getCurrentStatus(): SystemInfo;
139
- /**
140
- * Returns an {@link SystemInfo} object with the following structure:
141
- *
142
- * ```javascript
143
- * {
144
- * isSystemIdle: Boolean,
145
- * memInfo: Object,
146
- * eventLoopInfo: Object,
147
- * cpuInfo: Object
148
- * }
149
- * ```
150
- *
151
- * Where the `isSystemIdle` property is set to `false` if the system has been overloaded within the last
152
- * `historySecs` seconds and `true` otherwise.
153
- */
154
- getHistoricalStatus(): SystemInfo;
155
- /**
156
- * Returns a system status object.
157
- */
158
- private isSystemIdle;
159
- }
@@ -1,139 +0,0 @@
1
- import { evaluateLoadSignalSample } from './load_signal.js';
2
- /**
3
- * How far back the *current* system status looks by default — the window that gates task dispatch.
4
- * @internal
5
- */
6
- export const DEFAULT_CURRENT_HISTORY_SECS = 5;
7
- /**
8
- * How far back the *historical* system status looks by default — the window autoscaling decisions are based on, and
9
- * therefore how much history the signals are asked to retain.
10
- * @internal
11
- */
12
- export const DEFAULT_SNAPSHOT_HISTORY_SECS = 30;
13
- /** The four built-in signal names that map to typed `SystemInfo` fields, and the option that switches each off. */
14
- const BUILTIN_SIGNAL_OPTION_KEYS = {
15
- memInfo: 'memory',
16
- eventLoopInfo: 'eventLoop',
17
- cpuInfo: 'cpu',
18
- storageBackendInfo: 'storageBackend',
19
- };
20
- const BUILTIN_SIGNAL_NAMES = new Set(Object.keys(BUILTIN_SIGNAL_OPTION_KEYS));
21
- /**
22
- * Reads the overload verdict of every signal — the {@link Snapshotter}'s built-in four plus any custom ones — and
23
- * combines them into a {@link SystemInfo}: each signal is a time-weighted average of its snapshots, and the system
24
- * is overloaded whenever at least one of them is.
25
- *
26
- * Evaluated over two windows, both requested explicitly from every signal so that a signal's private retention cannot
27
- * widen what it contributes: a short `currentHistorySecs` one ({@link SystemStatus.getCurrentStatus}, gating task
28
- * dispatch) and a longer `historySecs` one ({@link SystemStatus.getHistoricalStatus}, driving autoscaling).
29
- *
30
- * An implementation detail of the {@link ConcurrencySystem}, configured through
31
- * {@link ConcurrencySystemOptions}.
32
- * @internal
33
- */
34
- export class SystemStatus {
35
- #currentHistoryMillis;
36
- #historyMillis;
37
- #signals;
38
- constructor(options) {
39
- const { currentHistorySecs = DEFAULT_CURRENT_HISTORY_SECS, historySecs = DEFAULT_SNAPSHOT_HISTORY_SECS, snapshotter, loadSignals = [], } = options;
40
- this.#currentHistoryMillis = currentHistorySecs * 1000;
41
- this.#historyMillis = historySecs * 1000;
42
- this.#signals = [...snapshotter.getLoadSignals(), ...loadSignals];
43
- this.assertUniqueSignalNames();
44
- }
45
- /**
46
- * The widest window any signal will be queried with, and therefore exactly how much history the signals are asked
47
- * to retain when they start. Derived here, where the windows are resolved, so nothing has to reapply their
48
- * defaults.
49
- */
50
- get maxSampleWindowMillis() {
51
- return Math.max(this.#currentHistoryMillis, this.#historyMillis);
52
- }
53
- /**
54
- * Signal names are the keys of the reported {@link SystemInfo}, so a duplicate would leave a status object that
55
- * contradicts actual behavior: both signals are still evaluated (any overloaded one holds concurrency down), but
56
- * only the last is reported.
57
- */
58
- assertUniqueSignalNames() {
59
- const seen = new Set();
60
- for (const { name } of this.#signals) {
61
- if (!seen.has(name)) {
62
- seen.add(name);
63
- continue;
64
- }
65
- const hint = BUILTIN_SIGNAL_NAMES.has(name)
66
- ? `it is the name of a built-in signal. To replace that signal, switch it off with \`loadSignals: { ${BUILTIN_SIGNAL_OPTION_KEYS[name]}: false }\` and keep your implementation in \`loadSignals.custom\`; to run yours alongside it, give it a different name.`
67
- : 'two custom signals cannot share a name - rename one of them.';
68
- throw new Error(`Duplicate load signal name ${JSON.stringify(name)}: ${hint}`);
69
- }
70
- }
71
- /**
72
- * Returns an {@link SystemInfo} object with the following structure:
73
- *
74
- * ```javascript
75
- * {
76
- * isSystemIdle: Boolean,
77
- * memInfo: Object,
78
- * eventLoopInfo: Object,
79
- * cpuInfo: Object
80
- * }
81
- * ```
82
- *
83
- * Where the `isSystemIdle` property is set to `false` if the system
84
- * has been overloaded in the last `options.currentHistorySecs` seconds,
85
- * and `true` otherwise.
86
- */
87
- getCurrentStatus() {
88
- return this.isSystemIdle(this.#currentHistoryMillis);
89
- }
90
- /**
91
- * Returns an {@link SystemInfo} object with the following structure:
92
- *
93
- * ```javascript
94
- * {
95
- * isSystemIdle: Boolean,
96
- * memInfo: Object,
97
- * eventLoopInfo: Object,
98
- * cpuInfo: Object
99
- * }
100
- * ```
101
- *
102
- * Where the `isSystemIdle` property is set to `false` if the system has been overloaded within the last
103
- * `historySecs` seconds and `true` otherwise.
104
- */
105
- getHistoricalStatus() {
106
- return this.isSystemIdle(this.#historyMillis);
107
- }
108
- /**
109
- * Returns a system status object.
110
- */
111
- isSystemIdle(sampleDurationMillis) {
112
- const result = {
113
- isSystemIdle: true,
114
- memInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
115
- eventLoopInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
116
- cpuInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
117
- storageBackendInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
118
- };
119
- let loadSignalInfo;
120
- for (const signal of this.#signals) {
121
- const sample = signal.getSample(sampleDurationMillis);
122
- const info = evaluateLoadSignalSample(sample, signal.overloadedRatio);
123
- if (info.isOverloaded) {
124
- result.isSystemIdle = false;
125
- }
126
- if (BUILTIN_SIGNAL_NAMES.has(signal.name)) {
127
- result[signal.name] = info;
128
- }
129
- else {
130
- loadSignalInfo ??= {};
131
- loadSignalInfo[signal.name] = info;
132
- }
133
- }
134
- if (loadSignalInfo) {
135
- result.loadSignalInfo = loadSignalInfo;
136
- }
137
- return result;
138
- }
139
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * Computes a weighted average of an array of numbers, complemented by an array of weights.
3
- * @ignore
4
- */
5
- export declare function weightedAvg(arrValues: number[], arrWeights: number[]): number;
@@ -1,14 +0,0 @@
1
- /**
2
- * Computes a weighted average of an array of numbers, complemented by an array of weights.
3
- * @ignore
4
- */
5
- export function weightedAvg(arrValues, arrWeights) {
6
- const result = arrValues
7
- .map((value, i) => {
8
- const weight = arrWeights[i];
9
- const sum = value * weight;
10
- return [sum, weight];
11
- })
12
- .reduce((p, c) => [p[0] + c[0], p[1] + c[1]], [0, 0]);
13
- return result[0] / result[1];
14
- }
package/cookie_utils.d.ts DELETED
@@ -1,44 +0,0 @@
1
- import type { Cookie as CookieObject } from '@crawlee/types';
2
- import { Cookie } from 'tough-cookie';
3
- export interface ResponseLike {
4
- url?: string | (() => string);
5
- headers?: Record<string, string | string[] | undefined> | (() => Record<string, string | string[] | undefined>);
6
- }
7
- /**
8
- * @internal
9
- */
10
- export declare function getCookiesFromResponse(response: Response): Cookie[];
11
- /**
12
- * Calculate cookie expiration date
13
- * @param maxAgeSecs
14
- * @returns Calculated date by session max age seconds.
15
- * @internal
16
- */
17
- export declare function getDefaultCookieExpirationDate(maxAgeSecs: number): Date;
18
- /**
19
- * Transforms tough-cookie to puppeteer cookie.
20
- * @param toughCookie Cookie from CookieJar
21
- * @return Cookie compatible with browser pool
22
- * @internal
23
- */
24
- export declare function toughCookieToBrowserPoolCookie(toughCookie: Cookie): CookieObject;
25
- /**
26
- * Transforms browser-pool cookie to tough-cookie.
27
- * @param cookieObject Cookie object (for instance from the `page.cookies` method).
28
- * @param maxAgeSecs Fallback expiration in seconds when the cookie itself has no `expires`.
29
- * When omitted, such a cookie is stored as a session cookie (no automatic expiration).
30
- * @internal
31
- */
32
- export declare function browserPoolCookieToToughCookie(cookieObject: CookieObject, maxAgeSecs?: number): Cookie;
33
- /**
34
- * @internal
35
- * @param cookieString The cookie string to attempt parsing
36
- * @returns Browser pool compatible cookie, or null if cookie cannot be parsed
37
- */
38
- export declare function cookieStringToToughCookie(cookieString: string): CookieObject | null;
39
- /**
40
- * Merges multiple cookie strings. Keys are compared case-sensitively, warning will be logged
41
- * if we see two cookies with same keys but different casing.
42
- * @internal
43
- */
44
- export declare function mergeCookies(url: string, sourceCookies: string[]): string;
package/cookie_utils.js DELETED
@@ -1,122 +0,0 @@
1
- import { Cookie, CookieJar } from 'tough-cookie';
2
- import { serviceLocator } from './service_locator.js';
3
- import { CookieParseError } from './session_pool/errors.js';
4
- /**
5
- * @internal
6
- */
7
- export function getCookiesFromResponse(response) {
8
- const headers = response.headers;
9
- const cookieHeaders = headers.getSetCookie();
10
- try {
11
- return cookieHeaders.map((cookie) => Cookie.parse(cookie));
12
- }
13
- catch (e) {
14
- throw new CookieParseError(cookieHeaders);
15
- }
16
- }
17
- /**
18
- * Calculate cookie expiration date
19
- * @param maxAgeSecs
20
- * @returns Calculated date by session max age seconds.
21
- * @internal
22
- */
23
- export function getDefaultCookieExpirationDate(maxAgeSecs) {
24
- return new Date(Date.now() + maxAgeSecs * 1000);
25
- }
26
- /**
27
- * Transforms tough-cookie to puppeteer cookie.
28
- * @param toughCookie Cookie from CookieJar
29
- * @return Cookie compatible with browser pool
30
- * @internal
31
- */
32
- export function toughCookieToBrowserPoolCookie(toughCookie) {
33
- return {
34
- name: toughCookie.key,
35
- value: toughCookie.value,
36
- // Puppeteer and Playwright expect 'expires' to be 'Unix time in seconds', not ms
37
- // If there is no expires date (so defaults to Infinity), we don't provide it to the browsers
38
- expires: toughCookie.expires == null || toughCookie.expires === 'Infinity'
39
- ? undefined
40
- : toughCookie.expires.getTime() / 1000,
41
- domain: toughCookie.domain ? `${toughCookie.hostOnly ? '' : '.'}${toughCookie.domain}` : undefined,
42
- path: toughCookie.path ?? undefined,
43
- secure: toughCookie.secure,
44
- httpOnly: toughCookie.httpOnly,
45
- };
46
- }
47
- /**
48
- * Transforms browser-pool cookie to tough-cookie.
49
- * @param cookieObject Cookie object (for instance from the `page.cookies` method).
50
- * @param maxAgeSecs Fallback expiration in seconds when the cookie itself has no `expires`.
51
- * When omitted, such a cookie is stored as a session cookie (no automatic expiration).
52
- * @internal
53
- */
54
- export function browserPoolCookieToToughCookie(cookieObject, maxAgeSecs) {
55
- const isExpiresValid = cookieObject.expires && typeof cookieObject.expires === 'number' && cookieObject.expires > 0;
56
- let expires;
57
- if (isExpiresValid) {
58
- expires = new Date(cookieObject.expires * 1000);
59
- }
60
- else if (maxAgeSecs != null) {
61
- expires = getDefaultCookieExpirationDate(maxAgeSecs);
62
- }
63
- const domainHasLeadingDot = cookieObject.domain?.startsWith?.('.');
64
- const domain = domainHasLeadingDot ? cookieObject.domain?.slice?.(1) : cookieObject.domain;
65
- return new Cookie({
66
- key: cookieObject.name,
67
- value: cookieObject.value,
68
- ...(expires !== undefined && { expires }),
69
- domain,
70
- path: cookieObject.path,
71
- secure: cookieObject.secure,
72
- httpOnly: cookieObject.httpOnly,
73
- hostOnly: !domainHasLeadingDot,
74
- });
75
- }
76
- /**
77
- * @internal
78
- * @param cookieString The cookie string to attempt parsing
79
- * @returns Browser pool compatible cookie, or null if cookie cannot be parsed
80
- */
81
- export function cookieStringToToughCookie(cookieString) {
82
- const parsed = Cookie.parse(cookieString);
83
- if (parsed) {
84
- return toughCookieToBrowserPoolCookie(parsed);
85
- }
86
- return null;
87
- }
88
- /**
89
- * Merges multiple cookie strings. Keys are compared case-sensitively, warning will be logged
90
- * if we see two cookies with same keys but different casing.
91
- * @internal
92
- */
93
- export function mergeCookies(url, sourceCookies) {
94
- const jar = new CookieJar();
95
- // ignore empty cookies
96
- for (const sourceCookieString of sourceCookies) {
97
- // ignore empty cookies
98
- if (!sourceCookieString)
99
- continue;
100
- const cookies = sourceCookieString.split(/ *; */);
101
- for (const cookieString of cookies) {
102
- // ignore extra spaces
103
- if (!cookieString)
104
- continue;
105
- const cookie = Cookie.parse(cookieString);
106
- if (!cookie) {
107
- serviceLocator.getLogger().warning(`Skipping malformed cookie fragment: '${cookieString}'`);
108
- continue;
109
- }
110
- const similarKeyCookie = jar.getCookiesSync(url).find((c) => {
111
- return cookie.key !== c.key && cookie.key.toLowerCase() === c.key.toLowerCase();
112
- });
113
- if (similarKeyCookie) {
114
- serviceLocator
115
- .getLogger()
116
- .warningOnce(`Found cookies with similar name during cookie merging: '${cookie.key}' and '${similarKeyCookie.key}'`);
117
- }
118
- jar.setCookieSync(cookie, url);
119
- }
120
- }
121
- return jar.getCookieStringSync(url);
122
- }
@@ -1,70 +0,0 @@
1
- import type { Awaitable } from '@crawlee/types';
2
- /**
3
- * Represents a middleware step in the context pipeline.
4
- *
5
- * @template TCrawlingContext - The input context type for this middleware
6
- * @template TCrawlingContextExtension - The enhanced output context type
7
- */
8
- export interface ContextMiddleware<TCrawlingContext, TCrawlingContextExtension> {
9
- /** The main middleware function that enhances the context */
10
- action: (context: TCrawlingContext) => Awaitable<TCrawlingContextExtension>;
11
- /** Optional cleanup function called after the consumer finishes or fails */
12
- cleanup?: (context: TCrawlingContext & TCrawlingContextExtension, error?: unknown) => Awaitable<void>;
13
- }
14
- /**
15
- * Encapsulates the logic of gradually enhancing the crawling context with additional information and utilities.
16
- *
17
- * The enhancement is done by a chain of middlewares that are added to the pipeline after its creation.
18
- * This class provides a type-safe way to build a pipeline of context transformations where each step
19
- * can enhance the context with additional properties or utilities.
20
- *
21
- * @template TContextBase - The base context type that serves as the starting point
22
- * @template TCrawlingContext - The final context type after all middleware transformations
23
- */
24
- export declare abstract class ContextPipeline<TContextBase, TCrawlingContext extends TContextBase> {
25
- /**
26
- * Creates a new empty context pipeline.
27
- *
28
- * @template TContextBase - The base context type for the pipeline
29
- * @returns A new ContextPipeline instance with no transformations
30
- */
31
- static create<TContextBase>(): ContextPipeline<TContextBase, TContextBase>;
32
- /**
33
- * Adds a middleware to the pipeline, creating a new pipeline instance.
34
- *
35
- * This method provides a fluent interface for building context transformation pipelines.
36
- * Each middleware can enhance the context with additional properties or utilities.
37
- *
38
- * @template TCrawlingContextExtension - The enhanced context type produced by this middleware
39
- * @param middleware - The middleware to add to the pipeline
40
- * @returns A new ContextPipeline instance with the added middleware
41
- */
42
- abstract compose<TCrawlingContextExtension>(middleware: ContextMiddleware<TCrawlingContext, TCrawlingContextExtension>): ContextPipeline<TContextBase, TCrawlingContext & TCrawlingContextExtension>;
43
- /**
44
- * Chains another pipeline onto this one. The other pipeline's base context must match
45
- * this pipeline's output context. Returns a new pipeline that runs this pipeline's
46
- * middlewares first, then the other pipeline's middlewares.
47
- *
48
- * @template TFinalContext - The final context type after the chained pipeline's transformations
49
- * @param other - The pipeline to append after this one
50
- * @returns A new ContextPipeline combining both pipelines' middlewares
51
- */
52
- abstract chain<TFinalContext extends TCrawlingContext>(other: ContextPipeline<TCrawlingContext, TFinalContext>): ContextPipeline<TContextBase, TFinalContext>;
53
- /**
54
- * Executes the middleware pipeline and passes the final context to a consumer function.
55
- *
56
- * This method runs the crawling context through the entire middleware chain, enhancing it
57
- * at each step, and then passes the final enhanced context to the provided consumer function.
58
- * Proper cleanup is performed even if exceptions occur during processing.
59
- *
60
- * @param crawlingContext - The initial context to process through the pipeline
61
- * @param finalContextConsumer - The function that will receive the final enhanced context
62
- *
63
- * @throws {ContextPipelineInitializationError} When a middleware fails during initialization
64
- * @throws {ContextPipelineInterruptedError} When the pipeline is intentionally interrupted during initialization
65
- * @throws {RequestHandlerError} When the final context consumer throws an exception
66
- * @throws {ContextPipelineCleanupError} When cleanup operations fail
67
- * @throws {SessionError} Session errors are re-thrown as-is for special handling
68
- */
69
- abstract call(crawlingContext: TContextBase, finalContextConsumer: (finalContext: TCrawlingContext) => Awaitable<unknown>): Promise<void>;
70
- }
@@ -1,122 +0,0 @@
1
- import { ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, RequestHandlerError, SessionError, } from '../errors.js';
2
- import { serviceLocator } from '../service_locator.js';
3
- /**
4
- * Encapsulates the logic of gradually enhancing the crawling context with additional information and utilities.
5
- *
6
- * The enhancement is done by a chain of middlewares that are added to the pipeline after its creation.
7
- * This class provides a type-safe way to build a pipeline of context transformations where each step
8
- * can enhance the context with additional properties or utilities.
9
- *
10
- * @template TContextBase - The base context type that serves as the starting point
11
- * @template TCrawlingContext - The final context type after all middleware transformations
12
- */
13
- export class ContextPipeline {
14
- /**
15
- * Creates a new empty context pipeline.
16
- *
17
- * @template TContextBase - The base context type for the pipeline
18
- * @returns A new ContextPipeline instance with no transformations
19
- */
20
- static create() {
21
- return new ContextPipelineImpl({ action: async (context) => context });
22
- }
23
- }
24
- /**
25
- * Implementation of the `ContextPipeline` logic. This hides implementation details such as the `middleware` and `parent`
26
- * properties from the `ContextPipeline` interface, making type checking more reliable.
27
- */
28
- class ContextPipelineImpl extends ContextPipeline {
29
- #middleware;
30
- #parent;
31
- constructor(middleware, parent) {
32
- super();
33
- this.#middleware = middleware;
34
- this.#parent = parent;
35
- }
36
- /**
37
- * @inheritdoc
38
- */
39
- compose(middleware) {
40
- return new ContextPipelineImpl(middleware, this);
41
- }
42
- chain(other) {
43
- const otherMiddlewares = Array.from(other.middlewareChain()).reverse();
44
- let result = this;
45
- for (const middleware of otherMiddlewares) {
46
- result = result.compose(middleware);
47
- }
48
- return result;
49
- }
50
- *middlewareChain() {
51
- let step = this;
52
- while (step !== undefined) {
53
- yield step.#middleware;
54
- step = step.#parent;
55
- }
56
- }
57
- /**
58
- * @inheritdoc
59
- */
60
- async call(crawlingContext, finalContextConsumer) {
61
- const middlewares = Array.from(this.middlewareChain()).reverse();
62
- const cleanupStack = [];
63
- let consumerException;
64
- try {
65
- for (const { action, cleanup } of middlewares) {
66
- try {
67
- const contextExtension = await action(crawlingContext);
68
- const extensionNames = [
69
- ...Object.getOwnPropertyNames(contextExtension),
70
- ...Object.getOwnPropertySymbols(contextExtension),
71
- ];
72
- for (const key of extensionNames) {
73
- try {
74
- if (Object.getOwnPropertyDescriptor(crawlingContext, key)?.configurable !== false) {
75
- Object.defineProperty(crawlingContext, key, Object.getOwnPropertyDescriptor(contextExtension, key));
76
- }
77
- }
78
- catch (error) {
79
- serviceLocator
80
- .getLogger()
81
- .debug(`Context pipeline failed to define property ${key.toString()}:`, error);
82
- }
83
- }
84
- if (cleanup) {
85
- cleanupStack.push(cleanup);
86
- }
87
- }
88
- catch (exception) {
89
- if (exception instanceof SessionError) {
90
- throw exception; // Session errors are re-thrown as-is
91
- }
92
- if (exception instanceof ContextPipelineInterruptedError) {
93
- throw exception;
94
- }
95
- throw new ContextPipelineInitializationError(exception);
96
- }
97
- }
98
- try {
99
- await finalContextConsumer(crawlingContext);
100
- }
101
- catch (exception) {
102
- if (exception instanceof SessionError) {
103
- consumerException = exception;
104
- throw exception; // Session errors are re-thrown as-is
105
- }
106
- consumerException = exception;
107
- throw new RequestHandlerError(exception);
108
- }
109
- }
110
- finally {
111
- try {
112
- for (const cleanup of cleanupStack.reverse()) {
113
- await cleanup(crawlingContext, consumerException);
114
- }
115
- }
116
- catch (exception) {
117
- // eslint-disable-next-line no-unsafe-finally
118
- throw new ContextPipelineCleanupError(exception);
119
- }
120
- }
121
- }
122
- }