@crawlee/core 4.0.0-beta.104 → 4.0.0-beta.106
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/autoscaling/autoscaled_pool.d.ts +3 -21
- package/autoscaling/autoscaled_pool.js +85 -85
- package/autoscaling/client_load_signal.d.ts +1 -5
- package/autoscaling/client_load_signal.js +20 -20
- package/autoscaling/concurrency_system.d.ts +5 -20
- package/autoscaling/concurrency_system.js +81 -80
- package/autoscaling/cpu_load_signal.d.ts +1 -2
- package/autoscaling/cpu_load_signal.js +10 -10
- package/autoscaling/event_loop_load_signal.d.ts +1 -4
- package/autoscaling/event_loop_load_signal.js +18 -18
- package/autoscaling/load_signal.d.ts +1 -1
- package/autoscaling/load_signal.js +12 -11
- package/autoscaling/memory_load_signal.d.ts +3 -12
- package/autoscaling/memory_load_signal.js +40 -41
- package/autoscaling/snapshotter.d.ts +1 -4
- package/autoscaling/snapshotter.js +12 -12
- package/autoscaling/system_status.d.ts +1 -3
- package/autoscaling/system_status.js +11 -11
- package/configuration.d.ts +1 -1
- package/configuration.js +3 -3
- package/crawlers/context_pipeline.js +6 -6
- package/crawlers/crawler_commons.d.ts +6 -56
- package/crawlers/crawler_commons.js +1 -107
- package/crawlers/index.d.ts +1 -1
- package/crawlers/index.js +0 -1
- package/crawlers/statistics.d.ts +1 -8
- package/crawlers/statistics.js +45 -44
- package/events/event_manager.d.ts +1 -1
- package/events/event_manager.js +3 -3
- package/events/local_event_manager.d.ts +1 -1
- package/events/local_event_manager.js +3 -3
- package/log.js +5 -1
- package/memory-storage/memory-storage.d.ts +1 -5
- package/memory-storage/memory-storage.js +2 -2
- package/memory-storage/resource-clients/dataset.d.ts +1 -1
- package/memory-storage/resource-clients/dataset.js +6 -5
- package/memory-storage/resource-clients/key-value-store.d.ts +1 -1
- package/memory-storage/resource-clients/key-value-store.js +13 -12
- package/memory-storage/resource-clients/request-queue.d.ts +4 -23
- package/memory-storage/resource-clients/request-queue.js +59 -58
- package/owned_or_injected.d.ts +1 -3
- package/owned_or_injected.js +17 -17
- package/package.json +5 -5
- package/proxy_configuration.d.ts +1 -3
- package/proxy_configuration.js +8 -8
- package/recoverable_state.d.ts +1 -10
- package/recoverable_state.js +41 -41
- package/request.d.ts +1 -2
- package/request.js +10 -13
- package/router.d.ts +1 -4
- package/router.js +23 -23
- package/serialization.js +8 -9
- package/service_locator.d.ts +1 -10
- package/service_locator.js +48 -48
- package/session_pool/session.d.ts +1 -12
- package/session_pool/session.js +50 -50
- package/session_pool/session_pool.d.ts +2 -11
- package/session_pool/session_pool.js +59 -58
- package/storages/dataset.d.ts +12 -1
- package/storages/dataset.js +121 -22
- package/storages/index.d.ts +1 -1
- package/storages/index.js +1 -1
- package/storages/key_value_store.d.ts +19 -4
- package/storages/key_value_store.js +174 -48
- package/storages/request_dedup_cache.d.ts +1 -2
- package/storages/request_dedup_cache.js +9 -9
- package/storages/request_list.d.ts +2 -22
- package/storages/request_list.js +74 -73
- package/storages/request_manager_tandem.d.ts +1 -10
- package/storages/request_manager_tandem.js +27 -27
- package/storages/request_queue.d.ts +21 -18
- package/storages/request_queue.js +256 -53
- package/storages/sitemap_request_loader.d.ts +1 -44
- package/storages/sitemap_request_loader.js +87 -87
- package/storages/storage_instance_manager.d.ts +1 -2
- package/storages/storage_instance_manager.js +17 -17
- package/storages/storage_stats.d.ts +1 -1
- package/storages/storage_stats.js +4 -4
- package/storages/transaction.d.ts +252 -0
- package/storages/transaction.js +251 -0
- package/system-info/runtime.js +7 -7
- package/storages/access_checking.d.ts +0 -12
- package/storages/access_checking.js +0 -17
|
@@ -15,92 +15,91 @@ const CRITICAL_OVERLOAD_RATE_LIMIT_MILLIS = 10_000;
|
|
|
15
15
|
export class MemoryLoadSignal {
|
|
16
16
|
name = 'memInfo';
|
|
17
17
|
overloadedRatio;
|
|
18
|
-
store = new SnapshotStore();
|
|
19
|
-
maxUsedRatio;
|
|
18
|
+
#store = new SnapshotStore();
|
|
19
|
+
#maxUsedRatio;
|
|
20
20
|
/** All resolved in `start()`, before anything that reads them can fire. */
|
|
21
|
-
config;
|
|
22
|
-
log;
|
|
23
|
-
maxMemoryBytes;
|
|
24
|
-
events;
|
|
25
|
-
maxMemoryRatio;
|
|
26
|
-
lastLoggedCriticalMemoryOverloadAt = null;
|
|
21
|
+
#config;
|
|
22
|
+
#log;
|
|
23
|
+
#maxMemoryBytes;
|
|
24
|
+
#events;
|
|
25
|
+
#maxMemoryRatio;
|
|
26
|
+
#lastLoggedCriticalMemoryOverloadAt = null;
|
|
27
27
|
constructor(options = {}) {
|
|
28
|
-
this
|
|
28
|
+
this.#maxUsedRatio = options.maxUsedRatio ?? 0.9;
|
|
29
29
|
this.overloadedRatio = options.overloadedRatio ?? 0.2;
|
|
30
30
|
this.handle = this.handle.bind(this);
|
|
31
31
|
}
|
|
32
32
|
async start(context) {
|
|
33
|
-
this
|
|
33
|
+
this.#store.useSampleWindow(context.maxSampleWindowMillis);
|
|
34
34
|
// A new session starts from a clean slate, so it is not judged on measurements from before the downtime.
|
|
35
|
-
this
|
|
35
|
+
this.#store.clear();
|
|
36
36
|
// Resolved here rather than in the constructor: an instance built ahead of time (to be wrapped, or shared
|
|
37
37
|
// between systems) must not capture whichever services happened to be registered at that moment.
|
|
38
|
-
this
|
|
39
|
-
this
|
|
40
|
-
this
|
|
41
|
-
const memoryMbytes = this
|
|
38
|
+
this.#config = serviceLocator.getConfiguration();
|
|
39
|
+
this.#events = serviceLocator.getEventManager();
|
|
40
|
+
this.#log = serviceLocator.getLogger().child({ prefix: 'MemoryLoadSignal' });
|
|
41
|
+
const memoryMbytes = this.#config.memoryMbytes ?? 0;
|
|
42
42
|
if (memoryMbytes > 0) {
|
|
43
|
-
this
|
|
43
|
+
this.#maxMemoryBytes = memoryMbytes * 1024 * 1024;
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
|
-
this
|
|
47
|
-
if (!this
|
|
46
|
+
this.#maxMemoryRatio = this.#config.availableMemoryRatio;
|
|
47
|
+
if (!this.#maxMemoryRatio) {
|
|
48
48
|
throw new Error('availableMemoryRatio is not set in configuration.');
|
|
49
49
|
}
|
|
50
50
|
else {
|
|
51
|
-
this
|
|
51
|
+
this.#log.debug(`Setting max memory of this run to ${this.#maxMemoryRatio * 100} % of available memory. ` +
|
|
52
52
|
'Use the CRAWLEE_MEMORY_MBYTES or CRAWLEE_AVAILABLE_MEMORY_RATIO environment variable to override it.');
|
|
53
53
|
}
|
|
54
54
|
// Fallback memory measurement in case memTotalBytes is missing from SystemInfo.
|
|
55
|
-
this
|
|
55
|
+
this.#maxMemoryBytes = await this.getTotalMemoryBytes();
|
|
56
56
|
}
|
|
57
|
-
this
|
|
57
|
+
this.#events.on("systemInfo" /* EventType.SYSTEM_INFO */, this.handle);
|
|
58
58
|
}
|
|
59
59
|
async stop() {
|
|
60
|
-
this
|
|
61
|
-
this
|
|
60
|
+
this.#events?.off("systemInfo" /* EventType.SYSTEM_INFO */, this.handle);
|
|
61
|
+
this.#events = undefined;
|
|
62
62
|
}
|
|
63
63
|
getSample(sampleDurationMillis) {
|
|
64
|
-
return this
|
|
64
|
+
return this.#store.getSample(sampleDurationMillis);
|
|
65
65
|
}
|
|
66
66
|
/** @internal Records a snapshot from a `SYSTEM_INFO` payload. Exposed for tests. */
|
|
67
67
|
handle(systemInfo) {
|
|
68
68
|
const createdAt = systemInfo.createdAt ? new Date(systemInfo.createdAt) : new Date();
|
|
69
69
|
const { memCurrentBytes, memTotalBytes } = systemInfo;
|
|
70
|
-
let maxMemoryBytes = this
|
|
71
|
-
if (this
|
|
72
|
-
maxMemoryBytes = this
|
|
70
|
+
let maxMemoryBytes = this.#maxMemoryBytes;
|
|
71
|
+
if (this.#maxMemoryRatio !== undefined && this.#maxMemoryRatio > 0) {
|
|
72
|
+
maxMemoryBytes = this.#maxMemoryRatio * (memTotalBytes ?? this.#maxMemoryBytes);
|
|
73
73
|
}
|
|
74
74
|
const snapshot = {
|
|
75
75
|
createdAt,
|
|
76
|
-
isOverloaded: memCurrentBytes / maxMemoryBytes > this
|
|
76
|
+
isOverloaded: memCurrentBytes / maxMemoryBytes > this.#maxUsedRatio,
|
|
77
77
|
usedBytes: memCurrentBytes,
|
|
78
78
|
};
|
|
79
|
-
this
|
|
80
|
-
this.
|
|
79
|
+
this.#store.push(snapshot, createdAt);
|
|
80
|
+
this.memoryOverloadWarning(systemInfo, maxMemoryBytes);
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const effectiveMax = maxMemoryBytes ?? this.maxMemoryBytes;
|
|
82
|
+
memoryOverloadWarning(systemInfo, maxMemoryBytes) {
|
|
83
|
+
const effectiveMax = maxMemoryBytes ?? this.#maxMemoryBytes;
|
|
85
84
|
const { memCurrentBytes } = systemInfo;
|
|
86
85
|
const createdAt = systemInfo.createdAt ? new Date(systemInfo.createdAt) : new Date();
|
|
87
|
-
if (this
|
|
88
|
-
+createdAt < +this
|
|
86
|
+
if (this.#lastLoggedCriticalMemoryOverloadAt &&
|
|
87
|
+
+createdAt < +this.#lastLoggedCriticalMemoryOverloadAt + CRITICAL_OVERLOAD_RATE_LIMIT_MILLIS)
|
|
89
88
|
return;
|
|
90
|
-
const maxDesiredMemoryBytes = this
|
|
91
|
-
const reserveMemory = effectiveMax * (1 - this
|
|
89
|
+
const maxDesiredMemoryBytes = this.#maxUsedRatio * effectiveMax;
|
|
90
|
+
const reserveMemory = effectiveMax * (1 - this.#maxUsedRatio) * RESERVE_MEMORY_RATIO;
|
|
92
91
|
const criticalOverloadBytes = maxDesiredMemoryBytes + reserveMemory;
|
|
93
92
|
const isCriticalOverload = memCurrentBytes > criticalOverloadBytes;
|
|
94
93
|
if (isCriticalOverload) {
|
|
95
94
|
const usedPercentage = Math.round((memCurrentBytes / effectiveMax) * 100);
|
|
96
95
|
const toMb = (bytes) => Math.round(bytes / 1024 ** 2);
|
|
97
|
-
this
|
|
96
|
+
this.#log.warning('Memory is critically overloaded. ' +
|
|
98
97
|
`Using ${toMb(memCurrentBytes)} MB of ${toMb(effectiveMax)} MB (${usedPercentage}%). Consider increasing available memory.`);
|
|
99
|
-
this
|
|
98
|
+
this.#lastLoggedCriticalMemoryOverloadAt = createdAt;
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
|
-
async
|
|
103
|
-
const containerized = this
|
|
101
|
+
async getTotalMemoryBytes() {
|
|
102
|
+
const containerized = this.#config.containerized ?? (await isContainerized());
|
|
104
103
|
return (await getMemoryInfo({ containerized, logger: serviceLocator.getLogger() })).totalBytes;
|
|
105
104
|
}
|
|
106
105
|
}
|
|
@@ -61,10 +61,7 @@ export type SnapshotterOptions = Omit<LoadSignalsOptions, 'custom'>;
|
|
|
61
61
|
* @internal
|
|
62
62
|
*/
|
|
63
63
|
export declare class Snapshotter {
|
|
64
|
-
private
|
|
65
|
-
private readonly eventLoopSignal?;
|
|
66
|
-
private readonly cpuSignal?;
|
|
67
|
-
private readonly clientSignal?;
|
|
64
|
+
#private;
|
|
68
65
|
/**
|
|
69
66
|
* Returns the enabled built-in signals, so `SystemStatus` can iterate them alongside any custom `LoadSignal`
|
|
70
67
|
* instances. Signals switched off through the options are simply absent — the system status reports them as
|
|
@@ -13,10 +13,10 @@ import { MemoryLoadSignal } from './memory_load_signal.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export class Snapshotter {
|
|
15
15
|
// Absent when switched off through the corresponding option (e.g. `client: false`).
|
|
16
|
-
memorySignal;
|
|
17
|
-
eventLoopSignal;
|
|
18
|
-
cpuSignal;
|
|
19
|
-
clientSignal;
|
|
16
|
+
#memorySignal;
|
|
17
|
+
#eventLoopSignal;
|
|
18
|
+
#cpuSignal;
|
|
19
|
+
#clientSignal;
|
|
20
20
|
/**
|
|
21
21
|
* Returns the enabled built-in signals, so `SystemStatus` can iterate them alongside any custom `LoadSignal`
|
|
22
22
|
* instances. Signals switched off through the options are simply absent — the system status reports them as
|
|
@@ -24,10 +24,10 @@ export class Snapshotter {
|
|
|
24
24
|
*/
|
|
25
25
|
getLoadSignals() {
|
|
26
26
|
const builtin = [
|
|
27
|
-
this
|
|
28
|
-
this
|
|
29
|
-
this
|
|
30
|
-
this
|
|
27
|
+
this.#memorySignal,
|
|
28
|
+
this.#eventLoopSignal,
|
|
29
|
+
this.#cpuSignal,
|
|
30
|
+
this.#clientSignal,
|
|
31
31
|
];
|
|
32
32
|
return builtin.filter((signal) => signal !== undefined);
|
|
33
33
|
}
|
|
@@ -39,13 +39,13 @@ export class Snapshotter {
|
|
|
39
39
|
// Each signal resolves its own ambient dependencies when started, and is told the window it will be sampled
|
|
40
40
|
// over then too - so there is nothing to thread in here beyond the caller's tuning.
|
|
41
41
|
if (memory !== false)
|
|
42
|
-
this
|
|
42
|
+
this.#memorySignal = new MemoryLoadSignal(memory);
|
|
43
43
|
if (eventLoop !== false)
|
|
44
|
-
this
|
|
44
|
+
this.#eventLoopSignal = new EventLoopLoadSignal(eventLoop);
|
|
45
45
|
if (cpu !== false)
|
|
46
|
-
this
|
|
46
|
+
this.#cpuSignal = new CpuLoadSignal(cpu);
|
|
47
47
|
if (client !== false)
|
|
48
|
-
this
|
|
48
|
+
this.#clientSignal = new ClientLoadSignal(client);
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Starts capturing snapshots at configured intervals. The `context` carries the sample window the signals will
|
|
@@ -105,9 +105,7 @@ export interface FinalStatistics {
|
|
|
105
105
|
* @internal
|
|
106
106
|
*/
|
|
107
107
|
export declare class SystemStatus {
|
|
108
|
-
private
|
|
109
|
-
private readonly historyMillis;
|
|
110
|
-
private readonly signals;
|
|
108
|
+
#private;
|
|
111
109
|
constructor(options: SystemStatusOptions);
|
|
112
110
|
/**
|
|
113
111
|
* The widest window any signal will be queried with, and therefore exactly how much history the signals are asked
|
|
@@ -32,14 +32,14 @@ const BUILTIN_SIGNAL_NAMES = new Set(Object.keys(BUILTIN_SIGNAL_OPTION_KEYS));
|
|
|
32
32
|
* @internal
|
|
33
33
|
*/
|
|
34
34
|
export class SystemStatus {
|
|
35
|
-
currentHistoryMillis;
|
|
36
|
-
historyMillis;
|
|
37
|
-
signals;
|
|
35
|
+
#currentHistoryMillis;
|
|
36
|
+
#historyMillis;
|
|
37
|
+
#signals;
|
|
38
38
|
constructor(options) {
|
|
39
39
|
const { currentHistorySecs = DEFAULT_CURRENT_HISTORY_SECS, historySecs = DEFAULT_SNAPSHOT_HISTORY_SECS, snapshotter, loadSignals = [], } = options;
|
|
40
|
-
this
|
|
41
|
-
this
|
|
42
|
-
this
|
|
40
|
+
this.#currentHistoryMillis = currentHistorySecs * 1000;
|
|
41
|
+
this.#historyMillis = historySecs * 1000;
|
|
42
|
+
this.#signals = [...snapshotter.getLoadSignals(), ...loadSignals];
|
|
43
43
|
this.assertUniqueSignalNames();
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
@@ -48,7 +48,7 @@ export class SystemStatus {
|
|
|
48
48
|
* defaults.
|
|
49
49
|
*/
|
|
50
50
|
get maxSampleWindowMillis() {
|
|
51
|
-
return Math.max(this
|
|
51
|
+
return Math.max(this.#currentHistoryMillis, this.#historyMillis);
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Signal names are the keys of the reported {@link SystemInfo}, so a duplicate would leave a status object that
|
|
@@ -57,7 +57,7 @@ export class SystemStatus {
|
|
|
57
57
|
*/
|
|
58
58
|
assertUniqueSignalNames() {
|
|
59
59
|
const seen = new Set();
|
|
60
|
-
for (const { name } of this
|
|
60
|
+
for (const { name } of this.#signals) {
|
|
61
61
|
if (!seen.has(name)) {
|
|
62
62
|
seen.add(name);
|
|
63
63
|
continue;
|
|
@@ -85,7 +85,7 @@ export class SystemStatus {
|
|
|
85
85
|
* and `true` otherwise.
|
|
86
86
|
*/
|
|
87
87
|
getCurrentStatus() {
|
|
88
|
-
return this.isSystemIdle(this
|
|
88
|
+
return this.isSystemIdle(this.#currentHistoryMillis);
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
91
|
* Returns an {@link SystemInfo} object with the following structure:
|
|
@@ -103,7 +103,7 @@ export class SystemStatus {
|
|
|
103
103
|
* `historySecs` seconds and `true` otherwise.
|
|
104
104
|
*/
|
|
105
105
|
getHistoricalStatus() {
|
|
106
|
-
return this.isSystemIdle(this
|
|
106
|
+
return this.isSystemIdle(this.#historyMillis);
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
109
|
* Returns a system status object.
|
|
@@ -117,7 +117,7 @@ export class SystemStatus {
|
|
|
117
117
|
clientInfo: { isOverloaded: false, limitRatio: 0, actualRatio: 0 },
|
|
118
118
|
};
|
|
119
119
|
let loadSignalInfo;
|
|
120
|
-
for (const signal of this
|
|
120
|
+
for (const signal of this.#signals) {
|
|
121
121
|
const sample = signal.getSample(sampleDurationMillis);
|
|
122
122
|
const info = evaluateLoadSignalSample(sample, signal.overloadedRatio);
|
|
123
123
|
if (info.isOverloaded) {
|
package/configuration.d.ts
CHANGED
|
@@ -123,12 +123,12 @@ export interface Configuration extends ResolvedConfigValues {
|
|
|
123
123
|
* `containerized` | `CRAWLEE_CONTAINERIZED` | -
|
|
124
124
|
*/
|
|
125
125
|
export declare class Configuration {
|
|
126
|
+
#private;
|
|
126
127
|
/**
|
|
127
128
|
* Field definitions for this configuration class.
|
|
128
129
|
* Subclasses override this to register additional fields.
|
|
129
130
|
*/
|
|
130
131
|
protected static fields: Record<string, ConfigField>;
|
|
131
|
-
private resolvedValues;
|
|
132
132
|
/**
|
|
133
133
|
* Creates new `Configuration` instance with provided options.
|
|
134
134
|
* Constructor options take precedence over environment variables, which take precedence
|
package/configuration.js
CHANGED
|
@@ -147,7 +147,7 @@ export class Configuration {
|
|
|
147
147
|
* Subclasses override this to register additional fields.
|
|
148
148
|
*/
|
|
149
149
|
static fields = crawleeConfigFields;
|
|
150
|
-
resolvedValues;
|
|
150
|
+
#resolvedValues;
|
|
151
151
|
/**
|
|
152
152
|
* Creates new `Configuration` instance with provided options.
|
|
153
153
|
* Constructor options take precedence over environment variables, which take precedence
|
|
@@ -156,7 +156,7 @@ export class Configuration {
|
|
|
156
156
|
constructor(options = {}) {
|
|
157
157
|
const fields = this.constructor.fields;
|
|
158
158
|
const fileOptions = Configuration.loadFileOptions();
|
|
159
|
-
this
|
|
159
|
+
this.#resolvedValues = Configuration.resolveAll(fields, options, fileOptions);
|
|
160
160
|
this.registerAccessors();
|
|
161
161
|
// Set the log level
|
|
162
162
|
const logLevel = this.logLevel;
|
|
@@ -209,7 +209,7 @@ export class Configuration {
|
|
|
209
209
|
const descriptors = {};
|
|
210
210
|
for (const key of Object.keys(fields)) {
|
|
211
211
|
descriptors[key] = {
|
|
212
|
-
get: () => this
|
|
212
|
+
get: () => this.#resolvedValues[key],
|
|
213
213
|
set() {
|
|
214
214
|
throw new TypeError('Configuration is immutable. Pass options via the constructor instead.');
|
|
215
215
|
},
|
|
@@ -26,12 +26,12 @@ export class ContextPipeline {
|
|
|
26
26
|
* properties from the `ContextPipeline` interface, making type checking more reliable.
|
|
27
27
|
*/
|
|
28
28
|
class ContextPipelineImpl extends ContextPipeline {
|
|
29
|
-
middleware;
|
|
30
|
-
parent;
|
|
29
|
+
#middleware;
|
|
30
|
+
#parent;
|
|
31
31
|
constructor(middleware, parent) {
|
|
32
32
|
super();
|
|
33
|
-
this
|
|
34
|
-
this
|
|
33
|
+
this.#middleware = middleware;
|
|
34
|
+
this.#parent = parent;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
37
|
* @inheritdoc
|
|
@@ -50,8 +50,8 @@ class ContextPipelineImpl extends ContextPipeline {
|
|
|
50
50
|
*middlewareChain() {
|
|
51
51
|
let step = this;
|
|
52
52
|
while (step !== undefined) {
|
|
53
|
-
yield step
|
|
54
|
-
step = step
|
|
53
|
+
yield step.#middleware;
|
|
54
|
+
step = step.#parent;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { Dictionary, HttpRequestOptions, ISession, ProxyInfo, SendRequestOptions } from '@crawlee/types';
|
|
2
2
|
import type { ReadonlyDeep, SetRequired } from 'type-fest';
|
|
3
|
-
import type { Configuration } from '../configuration.js';
|
|
4
3
|
import type { EnqueueLinksOptions } from '../enqueue_links/enqueue_links.js';
|
|
5
4
|
import type { CrawleeLogger } from '../log.js';
|
|
6
5
|
import type { Request, RequestOptions, Source } from '../request.js';
|
|
7
6
|
import type { StorageIdentifier } from '../storages/storage_instance_manager.js';
|
|
8
7
|
import type { Dataset } from '../storages/dataset.js';
|
|
9
|
-
import { KeyValueStore
|
|
8
|
+
import type { KeyValueStore } from '../storages/key_value_store.js';
|
|
10
9
|
import type { RequestQueueOperationOptions } from '../storages/request_queue.js';
|
|
11
10
|
/** @internal */
|
|
12
11
|
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
@@ -176,6 +175,11 @@ export interface CrawlingContext<UserData extends Dictionary = Dictionary> exten
|
|
|
176
175
|
sendRequest: (requestOverrides?: Partial<HttpRequestOptions>, optionsOverrides?: SendRequestOptions) => Promise<Response>;
|
|
177
176
|
/**
|
|
178
177
|
* Register a function to be called at the very end of the request handling process. This is useful for resources that should be accessible to error handlers, for instance.
|
|
178
|
+
*
|
|
179
|
+
* The callback runs *outside* the request's storage transaction, so storage writes made here are
|
|
180
|
+
* applied immediately and are **not** rolled back when the request fails. In
|
|
181
|
+
* {@link AdaptivePlaywrightCrawler} it also runs once per request handler attempt, so a write
|
|
182
|
+
* here can land more than once for a single request. Push results from the request handler itself.
|
|
179
183
|
*/
|
|
180
184
|
registerDeferredCleanup(cleanup: () => Promise<unknown>): void;
|
|
181
185
|
/**
|
|
@@ -198,58 +202,4 @@ export interface CrawlingContext<UserData extends Dictionary = Dictionary> exten
|
|
|
198
202
|
*/
|
|
199
203
|
extendTimeout(secs: number): void;
|
|
200
204
|
}
|
|
201
|
-
/**
|
|
202
|
-
* A partial implementation of {@link RestrictedCrawlingContext} that stores parameters of calls to context methods for later inspection.
|
|
203
|
-
*
|
|
204
|
-
* @experimental
|
|
205
|
-
*/
|
|
206
|
-
export declare class RequestHandlerResult {
|
|
207
|
-
private configuration;
|
|
208
|
-
private crawleeStateKey;
|
|
209
|
-
private _keyValueStoreChanges;
|
|
210
|
-
private pushDataCalls;
|
|
211
|
-
private addRequestsCalls;
|
|
212
|
-
constructor(configuration: Configuration, crawleeStateKey: string);
|
|
213
|
-
/**
|
|
214
|
-
* A record of calls to {@link RestrictedCrawlingContext.pushData}, {@link RestrictedCrawlingContext.addRequests}, {@link RestrictedCrawlingContext.enqueueLinks} made by a request handler.
|
|
215
|
-
*/
|
|
216
|
-
get calls(): ReadonlyDeep<{
|
|
217
|
-
pushData: Parameters<RestrictedCrawlingContext['pushData']>[];
|
|
218
|
-
addRequests: Parameters<RestrictedCrawlingContext['addRequests']>[];
|
|
219
|
-
}>;
|
|
220
|
-
/**
|
|
221
|
-
* A record of changes made to key-value stores by a request handler.
|
|
222
|
-
*/
|
|
223
|
-
get keyValueStoreChanges(): ReadonlyDeep<Record<string, Record<string, {
|
|
224
|
-
changedValue: unknown;
|
|
225
|
-
options?: RecordOptions;
|
|
226
|
-
}>>>;
|
|
227
|
-
/**
|
|
228
|
-
* Items added to datasets by a request handler.
|
|
229
|
-
*/
|
|
230
|
-
get datasetItems(): ReadonlyDeep<{
|
|
231
|
-
item: Dictionary;
|
|
232
|
-
datasetIdentifier?: string | StorageIdentifier;
|
|
233
|
-
}[]>;
|
|
234
|
-
/**
|
|
235
|
-
* URLs enqueued to the request queue by a request handler, either via {@link RestrictedCrawlingContext.addRequests} or {@link RestrictedCrawlingContext.enqueueLinks}
|
|
236
|
-
*/
|
|
237
|
-
get enqueuedUrls(): ReadonlyDeep<{
|
|
238
|
-
url: string;
|
|
239
|
-
label?: string;
|
|
240
|
-
}[]>;
|
|
241
|
-
/**
|
|
242
|
-
* URL lists enqueued to the request queue by a request handler via {@link RestrictedCrawlingContext.addRequests} using the `requestsFromUrl` option.
|
|
243
|
-
*/
|
|
244
|
-
get enqueuedUrlLists(): ReadonlyDeep<{
|
|
245
|
-
listUrl: string;
|
|
246
|
-
label?: string;
|
|
247
|
-
}[]>;
|
|
248
|
-
pushData: RestrictedCrawlingContext['pushData'];
|
|
249
|
-
addRequests: RestrictedCrawlingContext['addRequests'];
|
|
250
|
-
useState: RestrictedCrawlingContext['useState'];
|
|
251
|
-
getKeyValueStore: RestrictedCrawlingContext['getKeyValueStore'];
|
|
252
|
-
private getKeyValueStoreChangedValue;
|
|
253
|
-
private setKeyValueStoreChangedValue;
|
|
254
|
-
}
|
|
255
205
|
export {};
|
|
@@ -1,107 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* A partial implementation of {@link RestrictedCrawlingContext} that stores parameters of calls to context methods for later inspection.
|
|
4
|
-
*
|
|
5
|
-
* @experimental
|
|
6
|
-
*/
|
|
7
|
-
export class RequestHandlerResult {
|
|
8
|
-
configuration;
|
|
9
|
-
crawleeStateKey;
|
|
10
|
-
_keyValueStoreChanges = {};
|
|
11
|
-
pushDataCalls = [];
|
|
12
|
-
addRequestsCalls = [];
|
|
13
|
-
constructor(configuration, crawleeStateKey) {
|
|
14
|
-
this.configuration = configuration;
|
|
15
|
-
this.crawleeStateKey = crawleeStateKey;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* A record of calls to {@link RestrictedCrawlingContext.pushData}, {@link RestrictedCrawlingContext.addRequests}, {@link RestrictedCrawlingContext.enqueueLinks} made by a request handler.
|
|
19
|
-
*/
|
|
20
|
-
get calls() {
|
|
21
|
-
return {
|
|
22
|
-
pushData: this.pushDataCalls,
|
|
23
|
-
addRequests: this.addRequestsCalls,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* A record of changes made to key-value stores by a request handler.
|
|
28
|
-
*/
|
|
29
|
-
get keyValueStoreChanges() {
|
|
30
|
-
return this._keyValueStoreChanges;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Items added to datasets by a request handler.
|
|
34
|
-
*/
|
|
35
|
-
get datasetItems() {
|
|
36
|
-
return this.pushDataCalls.flatMap(([data, datasetIdentifier]) => (Array.isArray(data) ? data : [data]).map((item) => ({ item, datasetIdentifier })));
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* URLs enqueued to the request queue by a request handler, either via {@link RestrictedCrawlingContext.addRequests} or {@link RestrictedCrawlingContext.enqueueLinks}
|
|
40
|
-
*/
|
|
41
|
-
get enqueuedUrls() {
|
|
42
|
-
const result = [];
|
|
43
|
-
for (const [requests] of this.addRequestsCalls) {
|
|
44
|
-
for (const request of requests) {
|
|
45
|
-
if (typeof request === 'object' &&
|
|
46
|
-
(!('requestsFromUrl' in request) || request.requestsFromUrl !== undefined) &&
|
|
47
|
-
request.url !== undefined) {
|
|
48
|
-
result.push({ url: request.url, label: request.label });
|
|
49
|
-
}
|
|
50
|
-
else if (typeof request === 'string') {
|
|
51
|
-
result.push({ url: request });
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* URL lists enqueued to the request queue by a request handler via {@link RestrictedCrawlingContext.addRequests} using the `requestsFromUrl` option.
|
|
59
|
-
*/
|
|
60
|
-
get enqueuedUrlLists() {
|
|
61
|
-
const result = [];
|
|
62
|
-
for (const [requests] of this.addRequestsCalls) {
|
|
63
|
-
for (const request of requests) {
|
|
64
|
-
if (typeof request === 'object' &&
|
|
65
|
-
'requestsFromUrl' in request &&
|
|
66
|
-
request.requestsFromUrl !== undefined) {
|
|
67
|
-
result.push({ listUrl: request.requestsFromUrl, label: request.label });
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
pushData = async (data, datasetIdOrName) => {
|
|
74
|
-
this.pushDataCalls.push([data, datasetIdOrName]);
|
|
75
|
-
};
|
|
76
|
-
addRequests = async (requests, options = {}) => {
|
|
77
|
-
this.addRequestsCalls.push([requests, options]);
|
|
78
|
-
};
|
|
79
|
-
useState = async (defaultValue) => {
|
|
80
|
-
const store = await this.getKeyValueStore(undefined);
|
|
81
|
-
return await store.getAutoSavedValue(this.crawleeStateKey, defaultValue);
|
|
82
|
-
};
|
|
83
|
-
getKeyValueStore = async (identifier) => {
|
|
84
|
-
const store = await KeyValueStore.open(identifier, { configuration: this.configuration });
|
|
85
|
-
const storeId = store.id;
|
|
86
|
-
return {
|
|
87
|
-
id: storeId ?? this.configuration.defaultKeyValueStoreId,
|
|
88
|
-
name: store.name,
|
|
89
|
-
getValue: async (key) => this.getKeyValueStoreChangedValue(storeId, key) ?? (await store.getValue(key)),
|
|
90
|
-
setValue: async (key, value, options) => {
|
|
91
|
-
this.setKeyValueStoreChangedValue(storeId, key, value, options);
|
|
92
|
-
},
|
|
93
|
-
getAutoSavedValue: store.getAutoSavedValue.bind(store),
|
|
94
|
-
getPublicUrl: store.getPublicUrl.bind(store),
|
|
95
|
-
};
|
|
96
|
-
};
|
|
97
|
-
getKeyValueStoreChangedValue = (storeKey, key) => {
|
|
98
|
-
const id = storeKey ?? this.configuration.defaultKeyValueStoreId;
|
|
99
|
-
this._keyValueStoreChanges[id] ??= {};
|
|
100
|
-
return this.keyValueStoreChanges[id][key]?.changedValue ?? null;
|
|
101
|
-
};
|
|
102
|
-
setKeyValueStoreChangedValue = (storeKey, key, changedValue, options) => {
|
|
103
|
-
const id = storeKey ?? this.configuration.defaultKeyValueStoreId;
|
|
104
|
-
this._keyValueStoreChanges[id] ??= {};
|
|
105
|
-
this._keyValueStoreChanges[id][key] = { changedValue, options };
|
|
106
|
-
};
|
|
107
|
-
}
|
|
1
|
+
export {};
|
package/crawlers/index.d.ts
CHANGED
package/crawlers/index.js
CHANGED
package/crawlers/statistics.d.ts
CHANGED
|
@@ -81,6 +81,7 @@ export interface CalculatedStatistics {
|
|
|
81
81
|
* @category Crawlers
|
|
82
82
|
*/
|
|
83
83
|
export declare class Statistics implements IStatistics {
|
|
84
|
+
#private;
|
|
84
85
|
private static id;
|
|
85
86
|
/**
|
|
86
87
|
* An error tracker for final retry errors.
|
|
@@ -104,15 +105,7 @@ export declare class Statistics implements IStatistics {
|
|
|
104
105
|
readonly requestRetryHistogram: number[];
|
|
105
106
|
protected keyValueStore?: KeyValueStore;
|
|
106
107
|
protected readonly persistStateKey: string;
|
|
107
|
-
private logIntervalMillis;
|
|
108
|
-
private logMessage;
|
|
109
|
-
private listener;
|
|
110
|
-
private requestsInProgress;
|
|
111
108
|
private readonly log;
|
|
112
|
-
private instanceStart;
|
|
113
|
-
private logInterval;
|
|
114
|
-
private _events?;
|
|
115
|
-
private persistenceOptions;
|
|
116
109
|
private get events();
|
|
117
110
|
/**
|
|
118
111
|
* Construct a statistics instance to pass to a crawler via its `statistics` option, e.g. to preconfigure
|