@crawlee/core 4.0.0-beta.266 → 4.0.0-beta.268
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/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.268",
|
|
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.13.0"
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"@apify/log": "^3.0.1",
|
|
54
54
|
"@apify/timeout": "^1.0.1",
|
|
55
55
|
"@apify/utilities": "^3.1.0",
|
|
56
|
-
"@crawlee/fs-storage": "4.0.0-beta.
|
|
57
|
-
"@crawlee/http-client": "4.0.0-beta.
|
|
58
|
-
"@crawlee/types": "4.0.0-beta.
|
|
59
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
56
|
+
"@crawlee/fs-storage": "4.0.0-beta.268",
|
|
57
|
+
"@crawlee/http-client": "4.0.0-beta.268",
|
|
58
|
+
"@crawlee/types": "4.0.0-beta.268",
|
|
59
|
+
"@crawlee/utils": "4.0.0-beta.268",
|
|
60
60
|
"@sapphire/async-queue": "^1.5.5",
|
|
61
61
|
"@standard-schema/spec": "^1.1.0",
|
|
62
62
|
"@vladfrangu/async_event_emitter": "^2.4.7",
|
|
@@ -76,5 +76,5 @@
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
|
-
"gitHead": "
|
|
79
|
+
"gitHead": "9462257a02015e7ef40ebc16d8fdd0665c9554fe"
|
|
80
80
|
}
|
package/storages/batched-adds.js
CHANGED
|
@@ -14,8 +14,10 @@ import { activeStorageTransaction, withDirectStorageAccess } from './transaction
|
|
|
14
14
|
export async function drainRequestBatches(options) {
|
|
15
15
|
const { items, batchSize, waitBetweenBatchesMillis, waitForAllRequestsToBeAdded, maxNewRequests, log, processChunk, trackBackgroundBatches, } = options;
|
|
16
16
|
const deferred = activeStorageTransaction()?.policy.requestQueue === 'deferred';
|
|
17
|
+
// Leftovers are only reported when the caller waits anyway: draining an async producer just to find
|
|
18
|
+
// them could block indefinitely.
|
|
19
|
+
const reportsOverLimit = maxNewRequests !== undefined && waitForAllRequestsToBeAdded;
|
|
17
20
|
let remainingBudget = maxNewRequests ?? Infinity;
|
|
18
|
-
const requestsOverLimit = [];
|
|
19
21
|
// Never hand a chunk more than the budget allows, so an over-large final batch cannot overshoot.
|
|
20
22
|
const effectiveChunkSize = maxNewRequests !== undefined ? () => Math.min(batchSize, remainingBudget) : batchSize;
|
|
21
23
|
const chunks = peekableAsyncIterable(chunkedAsyncIterable(items, effectiveChunkSize));
|
|
@@ -28,12 +30,14 @@ export async function drainRequestBatches(options) {
|
|
|
28
30
|
return processedRequests;
|
|
29
31
|
};
|
|
30
32
|
const buildResult = async (addedRequests, waitForAll) => {
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
if (!reportsOverLimit) {
|
|
34
|
+
return { addedRequests, waitForAllRequestsToBeAdded: waitForAll };
|
|
35
|
+
}
|
|
36
|
+
// `chunkedAsyncIterable` stops pulling once the budget-derived chunk size hits zero, so whatever
|
|
37
|
+
// is left is still sitting in `items` rather than in a chunk we have seen.
|
|
38
|
+
const requestsOverLimit = [];
|
|
39
|
+
for await (const item of items) {
|
|
40
|
+
requestsOverLimit.push(item);
|
|
37
41
|
}
|
|
38
42
|
return { addedRequests, waitForAllRequestsToBeAdded: waitForAll, requestsOverLimit };
|
|
39
43
|
};
|
|
@@ -55,9 +59,8 @@ export async function drainRequestBatches(options) {
|
|
|
55
59
|
}
|
|
56
60
|
return added;
|
|
57
61
|
};
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
const awaitsRemainder = waitForAllRequestsToBeAdded || maxNewRequests !== undefined || deferred;
|
|
62
|
+
// Under `deferred` a writer that finishes after commit would have nowhere to put its journal entries.
|
|
63
|
+
const awaitsRemainder = waitForAllRequestsToBeAdded || deferred;
|
|
61
64
|
// An un-awaited writer outlives the transaction scope it inherits, so it must not record into a
|
|
62
65
|
// transaction that may already be closed. It writes directly - its write-through additions were never
|
|
63
66
|
// going to be rolled back anyway - which means the requests it adds are not journaled.
|
|
@@ -32,7 +32,7 @@ export type RequestLoaderStatus = Exclude<RequestSourceStatus, {
|
|
|
32
32
|
status: 'stalled';
|
|
33
33
|
}>;
|
|
34
34
|
/**
|
|
35
|
-
* Combines two request sources' statuses, with the precedence `ready` > `
|
|
35
|
+
* Combines two request sources' statuses, with the precedence `ready` > `waiting` > `stalled` > `finished`.
|
|
36
36
|
*
|
|
37
37
|
* Binary rather than variadic on purpose: it is on the task loop's probe path and folding a pair allocates
|
|
38
38
|
* nothing.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Combines two request sources' statuses, with the precedence `ready` > `
|
|
2
|
+
* Combines two request sources' statuses, with the precedence `ready` > `waiting` > `stalled` > `finished`.
|
|
3
3
|
*
|
|
4
4
|
* Binary rather than variadic on purpose: it is on the task loop's probe path and folding a pair allocates
|
|
5
5
|
* nothing.
|
|
@@ -13,17 +13,9 @@ export function joinRequestSourceStatuses(a, b) {
|
|
|
13
13
|
if (b.status === 'ready') {
|
|
14
14
|
return b;
|
|
15
15
|
}
|
|
16
|
-
// `ready` outranking `stalled`
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// turns a crawl that is progressing elsewhere into a `PersistentRateLimitError`. `stalled` outranking
|
|
20
|
-
// `waiting` is the same parity - that call site fired regardless of other domains' clocks.
|
|
21
|
-
if (a.status === 'stalled') {
|
|
22
|
-
return a;
|
|
23
|
-
}
|
|
24
|
-
if (b.status === 'stalled') {
|
|
25
|
-
return b;
|
|
26
|
-
}
|
|
16
|
+
// `ready` and `waiting` outranking `stalled` mask a stalled source while the other still has work, now or
|
|
17
|
+
// later. The crawler turns `stalled` into a `PersistentRateLimitError`, so "fixing" the masking turns a crawl
|
|
18
|
+
// that can still progress elsewhere into a failure. The stall surfaces once the other source runs out of work.
|
|
27
19
|
if (a.status === 'waiting') {
|
|
28
20
|
// The earlier of the two known wake-up times - unknown only if neither source announced one.
|
|
29
21
|
if (b.status !== 'waiting' || b.readyAt === undefined) {
|
|
@@ -31,6 +23,12 @@ export function joinRequestSourceStatuses(a, b) {
|
|
|
31
23
|
}
|
|
32
24
|
return a.readyAt !== undefined && a.readyAt <= b.readyAt ? a : b;
|
|
33
25
|
}
|
|
26
|
+
if (b.status === 'waiting') {
|
|
27
|
+
return b;
|
|
28
|
+
}
|
|
29
|
+
if (a.status === 'stalled') {
|
|
30
|
+
return a;
|
|
31
|
+
}
|
|
34
32
|
// `a` is finished, so `b` decides.
|
|
35
33
|
return b;
|
|
36
34
|
}
|
|
@@ -303,13 +303,11 @@ export interface AddRequestsBatchedOptions extends RequestQueueOperationOptions
|
|
|
303
303
|
waitBetweenBatchesMillis?: number;
|
|
304
304
|
/**
|
|
305
305
|
* If set, only this many *actually new* requests (i.e. not already present in the queue) will be added.
|
|
306
|
-
* Once the budget is reached, remaining requests from the iterable
|
|
307
|
-
* {@link AddRequestsBatchedResult.requestsOverLimit|`requestsOverLimit`}
|
|
306
|
+
* Once the budget is reached, remaining requests from the iterable are left unconsumed, or collected in
|
|
307
|
+
* {@link AddRequestsBatchedResult.requestsOverLimit|`requestsOverLimit`} if
|
|
308
|
+
* {@link AddRequestsBatchedOptions.waitForAllRequestsToBeAdded|`waitForAllRequestsToBeAdded`} is set.
|
|
308
309
|
*
|
|
309
310
|
* This is useful in combination with `maxRequestsPerCrawl` to avoid duplicate URLs consuming the budget.
|
|
310
|
-
*
|
|
311
|
-
* **Note:** Setting this option implicitly enables {@link AddRequestsBatchedOptions.waitForAllRequestsToBeAdded|`waitForAllRequestsToBeAdded`},
|
|
312
|
-
* since all batches must complete before leftover requests can be accurately reported.
|
|
313
311
|
*/
|
|
314
312
|
maxNewRequests?: number;
|
|
315
313
|
}
|
|
@@ -335,7 +333,8 @@ export interface AddRequestsBatchedResult {
|
|
|
335
333
|
/**
|
|
336
334
|
* Requests from the input that were not added to the queue because the
|
|
337
335
|
* {@link AddRequestsBatchedOptions.maxNewRequests|`maxNewRequests`} budget was reached.
|
|
338
|
-
*
|
|
336
|
+
* `undefined` unless both `maxNewRequests` and
|
|
337
|
+
* {@link AddRequestsBatchedOptions.waitForAllRequestsToBeAdded|`waitForAllRequestsToBeAdded`} are set.
|
|
339
338
|
*/
|
|
340
339
|
requestsOverLimit?: Source[];
|
|
341
340
|
}
|