@crawlee/core 4.0.0-beta.99 → 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.
- package/README.md +1 -1
- package/configuration.d.ts +16 -47
- package/configuration.js +13 -25
- package/debug.js +4 -4
- package/errors.d.ts +28 -38
- package/errors.js +33 -47
- package/events/event_manager.d.ts +2 -2
- package/events/event_manager.js +7 -6
- package/events/index.d.ts +1 -0
- package/events/local_event_manager.d.ts +1 -8
- package/events/local_event_manager.js +13 -13
- package/events/system_info.d.ts +38 -0
- package/index.d.ts +2 -8
- package/index.js +4 -8
- package/internal.d.ts +8 -0
- package/internal.js +9 -0
- package/log.d.ts +10 -11
- package/log.js +52 -20
- package/memory-storage/memory-storage.d.ts +15 -18
- package/memory-storage/memory-storage.js +80 -58
- package/memory-storage/resource-clients/dataset.d.ts +1 -6
- package/memory-storage/resource-clients/dataset.js +23 -31
- package/memory-storage/resource-clients/key-value-store.d.ts +1 -10
- package/memory-storage/resource-clients/key-value-store.js +43 -67
- package/memory-storage/resource-clients/request-queue.d.ts +1 -42
- package/memory-storage/resource-clients/request-queue.js +109 -117
- package/owned_or_injected.d.ts +1 -3
- package/owned_or_injected.js +17 -17
- package/package.json +17 -20
- package/proxy_configuration.d.ts +21 -26
- package/proxy_configuration.js +35 -25
- package/recoverable_state.d.ts +104 -47
- package/recoverable_state.js +199 -74
- package/request.d.ts +20 -107
- package/request.js +78 -244
- package/serialization.js +17 -16
- package/service_locator.d.ts +22 -10
- package/service_locator.js +59 -48
- package/storages/batched_adds.d.ts +37 -0
- package/storages/batched_adds.js +73 -0
- package/storages/dataset.d.ts +13 -8
- package/storages/dataset.js +149 -40
- package/storages/index.d.ts +4 -4
- package/storages/index.js +2 -4
- package/storages/key_value_store.d.ts +16 -35
- package/storages/key_value_store.js +223 -110
- package/storages/key_value_store_codec.js +6 -11
- package/storages/request_dedup_cache.d.ts +1 -4
- package/storages/request_dedup_cache.js +15 -15
- package/storages/request_list.d.ts +9 -104
- package/storages/request_list.js +236 -233
- package/storages/request_loader.d.ts +49 -18
- package/storages/request_loader.js +36 -1
- package/storages/request_manager.d.ts +86 -0
- package/storages/request_manager_tandem.d.ts +14 -38
- package/storages/request_manager_tandem.js +67 -64
- package/storages/request_queue.d.ts +23 -50
- package/storages/request_queue.js +371 -226
- package/storages/storage_instance_manager.d.ts +2 -4
- package/storages/storage_instance_manager.js +21 -21
- package/storages/storage_stats.d.ts +1 -1
- package/storages/storage_stats.js +4 -4
- package/storages/transaction.d.ts +270 -0
- package/storages/transaction.js +296 -0
- package/storages/utils.d.ts +6 -3
- package/storages/utils.js +11 -2
- package/system-info/runtime.js +7 -7
- package/url.d.ts +9 -0
- package/url.js +11 -0
- package/validators.d.ts +23 -25
- package/validators.js +14 -25
- package/autoscaling/autoscaled_pool.d.ts +0 -213
- package/autoscaling/autoscaled_pool.js +0 -378
- package/autoscaling/client_load_signal.d.ts +0 -59
- package/autoscaling/client_load_signal.js +0 -73
- package/autoscaling/concurrency_system.d.ts +0 -283
- package/autoscaling/concurrency_system.js +0 -350
- package/autoscaling/cpu_load_signal.d.ts +0 -44
- package/autoscaling/cpu_load_signal.js +0 -46
- package/autoscaling/event_loop_load_signal.d.ts +0 -54
- package/autoscaling/event_loop_load_signal.js +0 -60
- package/autoscaling/index.d.ts +0 -9
- package/autoscaling/index.js +0 -9
- package/autoscaling/load_signal.d.ts +0 -99
- package/autoscaling/load_signal.js +0 -103
- package/autoscaling/memory_load_signal.d.ts +0 -56
- package/autoscaling/memory_load_signal.js +0 -106
- package/autoscaling/snapshotter.d.ts +0 -87
- package/autoscaling/snapshotter.js +0 -67
- package/autoscaling/system_status.d.ts +0 -161
- package/autoscaling/system_status.js +0 -139
- package/autoscaling/weighted_avg.d.ts +0 -5
- package/autoscaling/weighted_avg.js +0 -14
- package/cookie_utils.d.ts +0 -44
- package/cookie_utils.js +0 -122
- package/crawlers/context_pipeline.d.ts +0 -70
- package/crawlers/context_pipeline.js +0 -122
- package/crawlers/crawler_commons.d.ts +0 -257
- package/crawlers/crawler_commons.js +0 -107
- package/crawlers/error_snapshotter.d.ts +0 -59
- package/crawlers/error_snapshotter.js +0 -117
- package/crawlers/error_tracker.d.ts +0 -54
- package/crawlers/error_tracker.js +0 -308
- package/crawlers/index.d.ts +0 -5
- package/crawlers/index.js +0 -5
- package/crawlers/internals/types.d.ts +0 -7
- package/crawlers/statistics.d.ts +0 -209
- package/crawlers/statistics.js +0 -350
- package/enqueue_links/enqueue_links.d.ts +0 -264
- package/enqueue_links/enqueue_links.js +0 -271
- package/enqueue_links/index.d.ts +0 -2
- package/enqueue_links/index.js +0 -2
- package/enqueue_links/shared.d.ts +0 -83
- package/enqueue_links/shared.js +0 -221
- package/router.d.ts +0 -309
- package/router.js +0 -309
- package/session_pool/consts.d.ts +0 -3
- package/session_pool/consts.js +0 -3
- package/session_pool/errors.d.ts +0 -7
- package/session_pool/errors.js +0 -11
- package/session_pool/fingerprint.d.ts +0 -9
- package/session_pool/fingerprint.js +0 -30
- package/session_pool/index.d.ts +0 -4
- package/session_pool/index.js +0 -4
- package/session_pool/session.d.ts +0 -161
- package/session_pool/session.js +0 -218
- package/session_pool/session_pool.d.ts +0 -246
- package/session_pool/session_pool.js +0 -386
- package/storages/access_checking.d.ts +0 -12
- package/storages/access_checking.js +0 -17
- package/storages/sitemap_request_loader.d.ts +0 -249
- package/storages/sitemap_request_loader.js +0 -432
- /package/{crawlers/internals/types.js → events/system_info.js} +0 -0
|
@@ -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
|
-
clientInfo: 'client',
|
|
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
|
-
clientInfo: { 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,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
|
-
}
|