@crawlee/core 4.0.0-beta.105 → 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/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 +1 -1
- package/storages/dataset.js +5 -5
- package/storages/key_value_store.d.ts +1 -4
- package/storages/key_value_store.js +21 -20
- 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 +2 -18
- package/storages/request_queue.js +37 -35
- 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 +1 -3
- package/storages/transaction.js +17 -17
- package/system-info/runtime.js +7 -7
package/owned_or_injected.js
CHANGED
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
12
12
|
export class OwnedOrInjected {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
#value;
|
|
14
|
+
#owned;
|
|
15
|
+
#present;
|
|
16
16
|
constructor(value, owned, present) {
|
|
17
|
-
this
|
|
18
|
-
this
|
|
19
|
-
this
|
|
17
|
+
this.#value = value;
|
|
18
|
+
this.#owned = owned;
|
|
19
|
+
this.#present = present;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Resolves a collaborator from an optionally-injected instance. `Injected` is the public/borrowed type exposed via
|
|
@@ -42,24 +42,24 @@ export class OwnedOrInjected {
|
|
|
42
42
|
* crawler-built defaults, `false` for user-injected instances.
|
|
43
43
|
*/
|
|
44
44
|
get isOwned() {
|
|
45
|
-
return this
|
|
45
|
+
return this.#owned;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* Whether a value is currently available. `false` for an owned slot whose default hasn't been built yet
|
|
49
49
|
* (e.g. a lazily-opened request queue before its first use).
|
|
50
50
|
*/
|
|
51
51
|
get isPresent() {
|
|
52
|
-
return this
|
|
52
|
+
return this.#present;
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* The resolved instance, typed as the public `Injected` type. Throws if the value is not present yet — callers that
|
|
56
56
|
* expect a lazily-filled owned slot should read {@link OwnedOrInjected.maybeValue|`maybeValue`} instead.
|
|
57
57
|
*/
|
|
58
58
|
get value() {
|
|
59
|
-
if (!this
|
|
59
|
+
if (!this.#present) {
|
|
60
60
|
throw new Error('OwnedOrInjected value is not initialized yet');
|
|
61
61
|
}
|
|
62
|
-
return this
|
|
62
|
+
return this.#value;
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* The resolved instance, or `undefined` when a lazily-filled owned slot hasn't been built yet. The non-throwing
|
|
@@ -67,7 +67,7 @@ export class OwnedOrInjected {
|
|
|
67
67
|
* a possibly-empty slot without the `isPresent ? value : …` dance.
|
|
68
68
|
*/
|
|
69
69
|
get maybeValue() {
|
|
70
|
-
return this
|
|
70
|
+
return this.#present ? this.#value : undefined;
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
73
|
* Fills the (owned) slot with the crawler-built default, returning it for convenience. Only valid on an owned,
|
|
@@ -75,14 +75,14 @@ export class OwnedOrInjected {
|
|
|
75
75
|
* would silently orphan the previous instance's lifecycle).
|
|
76
76
|
*/
|
|
77
77
|
set(value) {
|
|
78
|
-
if (!this
|
|
78
|
+
if (!this.#owned) {
|
|
79
79
|
throw new Error('Cannot set() a borrowed OwnedOrInjected value');
|
|
80
80
|
}
|
|
81
|
-
if (this
|
|
81
|
+
if (this.#present) {
|
|
82
82
|
throw new Error('OwnedOrInjected value is already initialized');
|
|
83
83
|
}
|
|
84
|
-
this
|
|
85
|
-
this
|
|
84
|
+
this.#value = value;
|
|
85
|
+
this.#present = true;
|
|
86
86
|
return value;
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
@@ -90,9 +90,9 @@ export class OwnedOrInjected {
|
|
|
90
90
|
* owns a present instance — a no-op for a borrowed instance or an owned-but-not-yet-built slot.
|
|
91
91
|
*/
|
|
92
92
|
async ifOwned(fn) {
|
|
93
|
-
if (!this
|
|
93
|
+
if (!this.#owned || !this.#present) {
|
|
94
94
|
return undefined;
|
|
95
95
|
}
|
|
96
|
-
return fn(this
|
|
96
|
+
return fn(this.#value);
|
|
97
97
|
}
|
|
98
98
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/core",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.106",
|
|
4
4
|
"description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.0.0"
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"@apify/log": "^2.5.18",
|
|
53
53
|
"@apify/timeout": "^0.4.4",
|
|
54
54
|
"@apify/utilities": "^2.15.5",
|
|
55
|
-
"@crawlee/fs-storage": "4.0.0-beta.
|
|
56
|
-
"@crawlee/types": "4.0.0-beta.
|
|
57
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
55
|
+
"@crawlee/fs-storage": "4.0.0-beta.106",
|
|
56
|
+
"@crawlee/types": "4.0.0-beta.106",
|
|
57
|
+
"@crawlee/utils": "4.0.0-beta.106",
|
|
58
58
|
"@sapphire/async-queue": "^1.5.5",
|
|
59
59
|
"@sapphire/shapeshift": "^4.0.0",
|
|
60
60
|
"@vladfrangu/async_event_emitter": "^2.4.6",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "c622f1fc65e65221ea245817c58ecc0ffb4a5cb0"
|
|
82
82
|
}
|
package/proxy_configuration.d.ts
CHANGED
|
@@ -70,10 +70,8 @@ export interface IProxyConfiguration {
|
|
|
70
70
|
* @category Scaling
|
|
71
71
|
*/
|
|
72
72
|
export declare class ProxyConfiguration implements IProxyConfiguration {
|
|
73
|
+
#private;
|
|
73
74
|
readonly isManInTheMiddle = false;
|
|
74
|
-
private nextCustomUrlIndex;
|
|
75
|
-
private proxyUrls?;
|
|
76
|
-
private newUrlFunction?;
|
|
77
75
|
/**
|
|
78
76
|
* Creates a {@link ProxyConfiguration} instance based on the provided options. Proxy servers are used to prevent target websites from
|
|
79
77
|
* blocking your crawlers based on IP address rate limits or blacklists. Setting proxy configuration in your crawlers automatically configures
|
package/proxy_configuration.js
CHANGED
|
@@ -29,9 +29,9 @@ import ow from 'ow';
|
|
|
29
29
|
*/
|
|
30
30
|
export class ProxyConfiguration {
|
|
31
31
|
isManInTheMiddle = false;
|
|
32
|
-
nextCustomUrlIndex = 0;
|
|
33
|
-
proxyUrls;
|
|
34
|
-
newUrlFunction;
|
|
32
|
+
#nextCustomUrlIndex = 0;
|
|
33
|
+
#proxyUrls;
|
|
34
|
+
#newUrlFunction;
|
|
35
35
|
/**
|
|
36
36
|
* Creates a {@link ProxyConfiguration} instance based on the provided options. Proxy servers are used to prevent target websites from
|
|
37
37
|
* blocking your crawlers based on IP address rate limits or blacklists. Setting proxy configuration in your crawlers automatically configures
|
|
@@ -67,8 +67,8 @@ export class ProxyConfiguration {
|
|
|
67
67
|
this.throwCannotCombineCustomMethods();
|
|
68
68
|
if (!proxyUrls && !newUrlFunction && validateRequired)
|
|
69
69
|
this.throwNoOptionsProvided();
|
|
70
|
-
this
|
|
71
|
-
this
|
|
70
|
+
this.#proxyUrls = proxyUrls;
|
|
71
|
+
this.#newUrlFunction = newUrlFunction;
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* This function creates a new {@link ProxyInfo} info object.
|
|
@@ -99,19 +99,19 @@ export class ProxyConfiguration {
|
|
|
99
99
|
* For example, `http://bob:password123@proxy.example.com:8000`
|
|
100
100
|
*/
|
|
101
101
|
async newUrl(options) {
|
|
102
|
-
if (this
|
|
102
|
+
if (this.#newUrlFunction) {
|
|
103
103
|
return (await this.callNewUrlFunction({ request: options?.request })) ?? undefined;
|
|
104
104
|
}
|
|
105
105
|
return this.handleProxyUrlsList() ?? undefined;
|
|
106
106
|
}
|
|
107
107
|
handleProxyUrlsList() {
|
|
108
|
-
return this
|
|
108
|
+
return this.#proxyUrls[this.#nextCustomUrlIndex++ % this.#proxyUrls.length];
|
|
109
109
|
}
|
|
110
110
|
/**
|
|
111
111
|
* Calls the custom newUrlFunction and checks format of its return value
|
|
112
112
|
*/
|
|
113
113
|
async callNewUrlFunction(options) {
|
|
114
|
-
const proxyUrl = await this
|
|
114
|
+
const proxyUrl = await this.#newUrlFunction(options);
|
|
115
115
|
try {
|
|
116
116
|
if (proxyUrl) {
|
|
117
117
|
new URL(proxyUrl); // eslint-disable-line no-new
|
package/recoverable_state.d.ts
CHANGED
|
@@ -59,16 +59,7 @@ export interface RecoverableStateOptions<TStateModel = Record<string, unknown>>
|
|
|
59
59
|
* The class automatically hooks into the event system to persist state when needed.
|
|
60
60
|
*/
|
|
61
61
|
export declare class RecoverableState<TStateModel = Record<string, unknown>> {
|
|
62
|
-
private
|
|
63
|
-
private state;
|
|
64
|
-
private readonly persistenceEnabled;
|
|
65
|
-
private readonly persistStateKey;
|
|
66
|
-
private readonly persistStateKvsName?;
|
|
67
|
-
private readonly persistStateKvsId?;
|
|
68
|
-
private keyValueStore;
|
|
69
|
-
private readonly log;
|
|
70
|
-
private readonly serialize;
|
|
71
|
-
private readonly deserialize;
|
|
62
|
+
#private;
|
|
72
63
|
/**
|
|
73
64
|
* Initialize a new recoverable state object.
|
|
74
65
|
*
|
package/recoverable_state.js
CHANGED
|
@@ -10,30 +10,30 @@ import { KeyValueStore, serviceLocator } from '@crawlee/core';
|
|
|
10
10
|
* The class automatically hooks into the event system to persist state when needed.
|
|
11
11
|
*/
|
|
12
12
|
export class RecoverableState {
|
|
13
|
-
defaultState;
|
|
14
|
-
state = null;
|
|
15
|
-
persistenceEnabled;
|
|
16
|
-
persistStateKey;
|
|
17
|
-
persistStateKvsName;
|
|
18
|
-
persistStateKvsId;
|
|
19
|
-
keyValueStore = null;
|
|
20
|
-
log;
|
|
21
|
-
serialize;
|
|
22
|
-
deserialize;
|
|
13
|
+
#defaultState;
|
|
14
|
+
#state = null;
|
|
15
|
+
#persistenceEnabled;
|
|
16
|
+
#persistStateKey;
|
|
17
|
+
#persistStateKvsName;
|
|
18
|
+
#persistStateKvsId;
|
|
19
|
+
#keyValueStore = null;
|
|
20
|
+
#log;
|
|
21
|
+
#serialize;
|
|
22
|
+
#deserialize;
|
|
23
23
|
/**
|
|
24
24
|
* Initialize a new recoverable state object.
|
|
25
25
|
*
|
|
26
26
|
* @param options Configuration options for the recoverable state
|
|
27
27
|
*/
|
|
28
28
|
constructor(options) {
|
|
29
|
-
this
|
|
30
|
-
this
|
|
31
|
-
this
|
|
32
|
-
this
|
|
33
|
-
this
|
|
34
|
-
this
|
|
35
|
-
this
|
|
36
|
-
this
|
|
29
|
+
this.#defaultState = options.defaultState;
|
|
30
|
+
this.#persistStateKey = options.persistStateKey;
|
|
31
|
+
this.#persistenceEnabled = options.persistenceEnabled ?? false;
|
|
32
|
+
this.#persistStateKvsName = options.persistStateKvsName;
|
|
33
|
+
this.#persistStateKvsId = options.persistStateKvsId;
|
|
34
|
+
this.#log = options.logger ?? serviceLocator.getLogger().child({ prefix: 'RecoverableState' });
|
|
35
|
+
this.#serialize = options.serialize ?? JSON.stringify;
|
|
36
|
+
this.#deserialize = options.deserialize ?? JSON.parse;
|
|
37
37
|
this.persistState = this.persistState.bind(this);
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
@@ -45,21 +45,21 @@ export class RecoverableState {
|
|
|
45
45
|
* @returns The loaded state object
|
|
46
46
|
*/
|
|
47
47
|
async initialize() {
|
|
48
|
-
if (this
|
|
48
|
+
if (this.#state !== null && this.#state !== undefined) {
|
|
49
49
|
return this.currentValue;
|
|
50
50
|
}
|
|
51
|
-
if (!this
|
|
52
|
-
this
|
|
51
|
+
if (!this.#persistenceEnabled) {
|
|
52
|
+
this.#state = this.#deserialize(this.#serialize(this.#defaultState));
|
|
53
53
|
return this.currentValue;
|
|
54
54
|
}
|
|
55
55
|
let kvsIdentifier = null;
|
|
56
|
-
if (this
|
|
57
|
-
kvsIdentifier = { name: this
|
|
56
|
+
if (this.#persistStateKvsName) {
|
|
57
|
+
kvsIdentifier = { name: this.#persistStateKvsName };
|
|
58
58
|
}
|
|
59
|
-
else if (this
|
|
60
|
-
kvsIdentifier = { id: this
|
|
59
|
+
else if (this.#persistStateKvsId) {
|
|
60
|
+
kvsIdentifier = { id: this.#persistStateKvsId };
|
|
61
61
|
}
|
|
62
|
-
this
|
|
62
|
+
this.#keyValueStore = await KeyValueStore.open(kvsIdentifier, {
|
|
63
63
|
configuration: serviceLocator.getConfiguration(),
|
|
64
64
|
});
|
|
65
65
|
await this.loadSavedState();
|
|
@@ -75,7 +75,7 @@ export class RecoverableState {
|
|
|
75
75
|
* and persists the current state one last time.
|
|
76
76
|
*/
|
|
77
77
|
async teardown() {
|
|
78
|
-
if (!this
|
|
78
|
+
if (!this.#persistenceEnabled || !this.persistState) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
const eventManager = serviceLocator.getEventManager();
|
|
@@ -86,10 +86,10 @@ export class RecoverableState {
|
|
|
86
86
|
* Get the current state.
|
|
87
87
|
*/
|
|
88
88
|
get currentValue() {
|
|
89
|
-
if (this
|
|
89
|
+
if (this.#state === null) {
|
|
90
90
|
throw new Error('Recoverable state has not yet been loaded');
|
|
91
91
|
}
|
|
92
|
-
return this
|
|
92
|
+
return this.#state;
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
95
95
|
* Reset the state to the default values and clear any persisted state.
|
|
@@ -98,12 +98,12 @@ export class RecoverableState {
|
|
|
98
98
|
* clears the persisted state from the KeyValueStore.
|
|
99
99
|
*/
|
|
100
100
|
async reset() {
|
|
101
|
-
this
|
|
102
|
-
if (this
|
|
103
|
-
if (this
|
|
101
|
+
this.#state = this.#deserialize(this.#serialize(this.#defaultState));
|
|
102
|
+
if (this.#persistenceEnabled) {
|
|
103
|
+
if (this.#keyValueStore === null) {
|
|
104
104
|
throw new Error('Recoverable state has not yet been initialized');
|
|
105
105
|
}
|
|
106
|
-
await this
|
|
106
|
+
await this.#keyValueStore.setValue(this.#persistStateKey, null);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
@@ -115,12 +115,12 @@ export class RecoverableState {
|
|
|
115
115
|
* @param eventData Optional data associated with a PERSIST_STATE event
|
|
116
116
|
*/
|
|
117
117
|
async persistState(eventData) {
|
|
118
|
-
this
|
|
119
|
-
if (this
|
|
118
|
+
this.#log.debug(`Persisting state of the RecoverableState (eventData=${JSON.stringify(eventData)}).`);
|
|
119
|
+
if (this.#keyValueStore === null || this.#state === null) {
|
|
120
120
|
throw new Error('Recoverable state has not yet been initialized');
|
|
121
121
|
}
|
|
122
|
-
if (this
|
|
123
|
-
await this
|
|
122
|
+
if (this.#persistenceEnabled) {
|
|
123
|
+
await this.#keyValueStore.setValue(this.#persistStateKey, this.#serialize(this.#state), {
|
|
124
124
|
contentType: 'text/plain', // HACK - the result is expected to be JSON, but we do this to avoid the implicit JSON.parse in `KeyValueStore.getValue`
|
|
125
125
|
});
|
|
126
126
|
}
|
|
@@ -129,15 +129,15 @@ export class RecoverableState {
|
|
|
129
129
|
* Load the saved state from the KeyValueStore
|
|
130
130
|
*/
|
|
131
131
|
async loadSavedState() {
|
|
132
|
-
if (this
|
|
132
|
+
if (this.#keyValueStore === null) {
|
|
133
133
|
throw new Error('Recoverable state has not yet been initialized');
|
|
134
134
|
}
|
|
135
|
-
const storedState = await this
|
|
135
|
+
const storedState = await this.#keyValueStore.getValue(this.#persistStateKey);
|
|
136
136
|
if (storedState === null || storedState === undefined) {
|
|
137
|
-
this
|
|
137
|
+
this.#state = this.#deserialize(this.#serialize(this.#defaultState));
|
|
138
138
|
}
|
|
139
139
|
else {
|
|
140
|
-
this
|
|
140
|
+
this.#state = this.#deserialize(storedState);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
}
|
package/request.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare enum RequestState {
|
|
|
44
44
|
* @category Sources
|
|
45
45
|
*/
|
|
46
46
|
declare class CrawleeRequest<UserData extends Dictionary = Dictionary> {
|
|
47
|
+
#private;
|
|
47
48
|
/** Request ID */
|
|
48
49
|
id?: string;
|
|
49
50
|
/** URL of the web page to crawl. */
|
|
@@ -74,8 +75,6 @@ declare class CrawleeRequest<UserData extends Dictionary = Dictionary> {
|
|
|
74
75
|
errorMessages: string[];
|
|
75
76
|
/** Object with HTTP headers. Key is header name, value is the value. */
|
|
76
77
|
headers?: Record<string, string>;
|
|
77
|
-
/** Private store for the custom user data assigned to the request. */
|
|
78
|
-
private _userData;
|
|
79
78
|
/**
|
|
80
79
|
* Custom user data assigned to the request.
|
|
81
80
|
*
|
package/request.js
CHANGED
|
@@ -101,7 +101,7 @@ class CrawleeRequest {
|
|
|
101
101
|
/** Object with HTTP headers. Key is header name, value is the value. */
|
|
102
102
|
headers;
|
|
103
103
|
/** Private store for the custom user data assigned to the request. */
|
|
104
|
-
|
|
104
|
+
#userData = {};
|
|
105
105
|
/**
|
|
106
106
|
* Custom user data assigned to the request.
|
|
107
107
|
*
|
|
@@ -167,36 +167,33 @@ class CrawleeRequest {
|
|
|
167
167
|
if (label) {
|
|
168
168
|
userData.label = label;
|
|
169
169
|
}
|
|
170
|
+
this.#userData = { __crawlee: {}, ...userData };
|
|
171
|
+
// `userData` must stay an enumerable own accessor — serialization in the storages relies on it
|
|
170
172
|
Object.defineProperties(this, {
|
|
171
|
-
_userData: {
|
|
172
|
-
value: { __crawlee: {}, ...userData },
|
|
173
|
-
enumerable: false,
|
|
174
|
-
writable: true,
|
|
175
|
-
},
|
|
176
173
|
userData: {
|
|
177
|
-
get: () => this
|
|
174
|
+
get: () => this.#userData,
|
|
178
175
|
set: (value) => {
|
|
179
176
|
Object.defineProperties(value, {
|
|
180
177
|
__crawlee: {
|
|
181
|
-
value: this.
|
|
178
|
+
value: this.#userData.__crawlee,
|
|
182
179
|
enumerable: false,
|
|
183
180
|
writable: true,
|
|
184
181
|
},
|
|
185
182
|
toJSON: {
|
|
186
183
|
value: () => {
|
|
187
|
-
if (Object.keys(this.
|
|
184
|
+
if (Object.keys(this.#userData.__crawlee).length > 0) {
|
|
188
185
|
return {
|
|
189
|
-
...this
|
|
190
|
-
__crawlee: this.
|
|
186
|
+
...this.#userData,
|
|
187
|
+
__crawlee: this.#userData.__crawlee,
|
|
191
188
|
};
|
|
192
189
|
}
|
|
193
|
-
return this
|
|
190
|
+
return this.#userData;
|
|
194
191
|
},
|
|
195
192
|
enumerable: false,
|
|
196
193
|
writable: true,
|
|
197
194
|
},
|
|
198
195
|
});
|
|
199
|
-
this
|
|
196
|
+
this.#userData = value;
|
|
200
197
|
},
|
|
201
198
|
enumerable: true,
|
|
202
199
|
},
|
package/router.d.ts
CHANGED
|
@@ -215,10 +215,7 @@ export type RouterRoutes<Context, Routes extends Record<keyof Routes, Dictionary
|
|
|
215
215
|
* ```
|
|
216
216
|
*/
|
|
217
217
|
export declare class Router<Context extends Omit<RestrictedCrawlingContext, 'enqueueLinks'>, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>> {
|
|
218
|
-
private
|
|
219
|
-
private readonly schemas;
|
|
220
|
-
private readonly timeouts;
|
|
221
|
-
private readonly middlewares;
|
|
218
|
+
#private;
|
|
222
219
|
/**
|
|
223
220
|
* use Router.create() instead!
|
|
224
221
|
* @ignore
|
package/router.js
CHANGED
|
@@ -161,10 +161,10 @@ export async function validateUserData(label, schema, userData) {
|
|
|
161
161
|
* ```
|
|
162
162
|
*/
|
|
163
163
|
export class Router {
|
|
164
|
-
routes = new Map();
|
|
165
|
-
schemas = new Map();
|
|
166
|
-
timeouts = new Map();
|
|
167
|
-
middlewares = [];
|
|
164
|
+
#routes = new Map();
|
|
165
|
+
#schemas = new Map();
|
|
166
|
+
#timeouts = new Map();
|
|
167
|
+
#middlewares = [];
|
|
168
168
|
/**
|
|
169
169
|
* use Router.create() instead!
|
|
170
170
|
* @ignore
|
|
@@ -172,9 +172,9 @@ export class Router {
|
|
|
172
172
|
constructor() { }
|
|
173
173
|
addHandler(label, handler, options = {}) {
|
|
174
174
|
this.validate(label);
|
|
175
|
-
this
|
|
175
|
+
this.#routes.set(label, handler);
|
|
176
176
|
if (options.requestHandlerTimeoutSecs !== undefined) {
|
|
177
|
-
this
|
|
177
|
+
this.#timeouts.set(label, options.requestHandlerTimeoutSecs);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
/**
|
|
@@ -187,9 +187,9 @@ export class Router {
|
|
|
187
187
|
*/
|
|
188
188
|
addDefaultHandler(handler, options = {}) {
|
|
189
189
|
this.validate(defaultRoute);
|
|
190
|
-
this
|
|
190
|
+
this.#routes.set(defaultRoute, handler);
|
|
191
191
|
if (options.requestHandlerTimeoutSecs !== undefined) {
|
|
192
|
-
this
|
|
192
|
+
this.#timeouts.set(defaultRoute, options.requestHandlerTimeoutSecs);
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
/**
|
|
@@ -199,25 +199,25 @@ export class Router {
|
|
|
199
199
|
*/
|
|
200
200
|
getSchema(label) {
|
|
201
201
|
if (label != null) {
|
|
202
|
-
const schema = this
|
|
202
|
+
const schema = this.#schemas.get(label);
|
|
203
203
|
if (schema) {
|
|
204
204
|
return schema;
|
|
205
205
|
}
|
|
206
206
|
// A label with its own route is fully specified; don't fall back to the default-route schema.
|
|
207
|
-
if (this
|
|
207
|
+
if (this.#routes.has(label)) {
|
|
208
208
|
return undefined;
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
// Requests with no route of their own fall through to the default handler, so validate their
|
|
212
212
|
// `userData` against the default-route schema, if one was registered.
|
|
213
|
-
return this
|
|
213
|
+
return this.#schemas.get(defaultRoute);
|
|
214
214
|
}
|
|
215
215
|
/**
|
|
216
216
|
* Registers a middleware that will be fired before the matching route handler.
|
|
217
217
|
* Multiple middlewares can be registered, they will be fired in the same order.
|
|
218
218
|
*/
|
|
219
219
|
use(middleware) {
|
|
220
|
-
this
|
|
220
|
+
this.#middlewares.push(middleware);
|
|
221
221
|
}
|
|
222
222
|
/**
|
|
223
223
|
* Returns the `requestHandlerTimeoutSecs` registered for a label, or `undefined` when the route did not
|
|
@@ -226,27 +226,27 @@ export class Router {
|
|
|
226
226
|
* the default route asked for. Used by the crawler; not meant to be called directly.
|
|
227
227
|
*/
|
|
228
228
|
getTimeoutSecs(label) {
|
|
229
|
-
if (label && this
|
|
230
|
-
return this
|
|
229
|
+
if (label && this.#routes.has(label)) {
|
|
230
|
+
return this.#timeouts.get(label);
|
|
231
231
|
}
|
|
232
|
-
return this
|
|
232
|
+
return this.#timeouts.get(defaultRoute);
|
|
233
233
|
}
|
|
234
234
|
/**
|
|
235
235
|
* The longest `requestHandlerTimeoutSecs` any route asked for, or `undefined` when no route overrides it.
|
|
236
236
|
* The crawler needs an upper bound up front, before it knows which routes a run will actually hit.
|
|
237
237
|
*/
|
|
238
238
|
getMaxTimeoutSecs() {
|
|
239
|
-
return this
|
|
239
|
+
return this.#timeouts.size > 0 ? Math.max(...this.#timeouts.values()) : undefined;
|
|
240
240
|
}
|
|
241
241
|
/**
|
|
242
242
|
* Returns route handler for given label. If no label is provided, the default request handler will be returned.
|
|
243
243
|
*/
|
|
244
244
|
getHandler(label) {
|
|
245
|
-
if (label && this
|
|
246
|
-
return this
|
|
245
|
+
if (label && this.#routes.has(label)) {
|
|
246
|
+
return this.#routes.get(label);
|
|
247
247
|
}
|
|
248
|
-
if (this
|
|
249
|
-
return this
|
|
248
|
+
if (this.#routes.has(defaultRoute)) {
|
|
249
|
+
return this.#routes.get(defaultRoute);
|
|
250
250
|
}
|
|
251
251
|
throw new MissingRouteError(`Route not found for label '${String(label)}'.` +
|
|
252
252
|
' You must set up a route for this label or a default route.' +
|
|
@@ -267,7 +267,7 @@ export class Router {
|
|
|
267
267
|
* Throws when the label already exists in our registry.
|
|
268
268
|
*/
|
|
269
269
|
validate(label) {
|
|
270
|
-
if (this
|
|
270
|
+
if (this.#routes.has(label)) {
|
|
271
271
|
const message = label === defaultRoute
|
|
272
272
|
? `Default route is already defined!`
|
|
273
273
|
: `Route for label '${String(label)}' is already defined!`;
|
|
@@ -291,14 +291,14 @@ export class Router {
|
|
|
291
291
|
router.addHandler(label, value);
|
|
292
292
|
}
|
|
293
293
|
else {
|
|
294
|
-
router
|
|
294
|
+
router.#schemas.set(label, value);
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
const func = async function (context) {
|
|
298
298
|
const { url, loadedUrl, label } = context.request;
|
|
299
299
|
context.log.debug('Page opened.', { label, url: loadedUrl ?? url });
|
|
300
300
|
await router.validateRequest(context);
|
|
301
|
-
for (const middleware of router
|
|
301
|
+
for (const middleware of router.#middlewares) {
|
|
302
302
|
await middleware(context);
|
|
303
303
|
}
|
|
304
304
|
return router.getHandler(label)(context);
|
package/serialization.js
CHANGED
|
@@ -11,32 +11,31 @@ const pipeline = util.promisify(streamPipeline);
|
|
|
11
11
|
* @internal
|
|
12
12
|
*/
|
|
13
13
|
class ArrayToJson extends Readable {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
#offset = 0;
|
|
15
|
+
#batchSize;
|
|
16
|
+
#data;
|
|
17
17
|
constructor(data, options = {}) {
|
|
18
18
|
super({
|
|
19
19
|
...options,
|
|
20
20
|
autoDestroy: true,
|
|
21
21
|
emitClose: true,
|
|
22
22
|
});
|
|
23
|
-
this.data = data;
|
|
24
23
|
const { batchSize = 10000 } = options;
|
|
25
|
-
this
|
|
26
|
-
this
|
|
24
|
+
this.#batchSize = batchSize;
|
|
25
|
+
this.#data = data;
|
|
27
26
|
this.push('[');
|
|
28
27
|
}
|
|
29
28
|
_read() {
|
|
30
29
|
try {
|
|
31
|
-
const items = this
|
|
30
|
+
const items = this.#data.slice(this.#offset, this.#offset + this.#batchSize);
|
|
32
31
|
if (items.length) {
|
|
33
32
|
const json = JSON.stringify(items);
|
|
34
33
|
// Strip brackets to flatten the batch.
|
|
35
34
|
const itemString = json.substring(1, json.length - 1);
|
|
36
|
-
if (this
|
|
35
|
+
if (this.#offset > 0)
|
|
37
36
|
this.push(',', 'utf8');
|
|
38
37
|
this.push(itemString, 'utf8');
|
|
39
|
-
this
|
|
38
|
+
this.#offset += this.#batchSize;
|
|
40
39
|
}
|
|
41
40
|
else {
|
|
42
41
|
this.push(']');
|
package/service_locator.d.ts
CHANGED
|
@@ -101,16 +101,7 @@ interface ServiceLocatorInterface {
|
|
|
101
101
|
* ```
|
|
102
102
|
*/
|
|
103
103
|
export declare class ServiceLocator implements ServiceLocatorInterface {
|
|
104
|
-
private
|
|
105
|
-
private eventManager?;
|
|
106
|
-
private storageBackend?;
|
|
107
|
-
private logger?;
|
|
108
|
-
/**
|
|
109
|
-
* Unified storage instance manager for Dataset, KeyValueStore, and RequestQueue.
|
|
110
|
-
* Shared across all ServiceLocator instances (global singleton), matching crawlee-python.
|
|
111
|
-
* Per-crawler isolation is achieved via `clientCacheKey`, not separate manager instances.
|
|
112
|
-
*/
|
|
113
|
-
private static storageInstanceManager?;
|
|
104
|
+
#private;
|
|
114
105
|
/**
|
|
115
106
|
* Creates a new ServiceLocator instance.
|
|
116
107
|
*
|