@crawlee/core 4.0.0-beta.193 → 4.0.0-beta.195

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.193",
3
+ "version": "4.0.0-beta.195",
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"
@@ -53,10 +53,10 @@
53
53
  "@apify/log": "^3.0.1",
54
54
  "@apify/timeout": "^1.0.1",
55
55
  "@apify/utilities": "^3.0.1",
56
- "@crawlee/fs-storage": "4.0.0-beta.193",
57
- "@crawlee/http-client": "4.0.0-beta.193",
58
- "@crawlee/types": "4.0.0-beta.193",
59
- "@crawlee/utils": "4.0.0-beta.193",
56
+ "@crawlee/fs-storage": "4.0.0-beta.195",
57
+ "@crawlee/http-client": "4.0.0-beta.195",
58
+ "@crawlee/types": "4.0.0-beta.195",
59
+ "@crawlee/utils": "4.0.0-beta.195",
60
60
  "@sapphire/async-queue": "^1.5.5",
61
61
  "@standard-schema/spec": "^1.0.0",
62
62
  "@vladfrangu/async_event_emitter": "^2.4.6",
@@ -76,5 +76,5 @@
76
76
  }
77
77
  }
78
78
  },
79
- "gitHead": "db10ed8633118f3d53dcef58118f408ce578d5d5"
79
+ "gitHead": "7ef1df353c43614acc1275a53ef8ca96e850fe3e"
80
80
  }
package/request.js CHANGED
@@ -2,11 +2,11 @@ import crypto from 'node:crypto';
2
2
  import { z } from 'zod';
3
3
  import { cryptoRandomObjectId, normalizeUrl } from '@apify/utilities';
4
4
  import { serviceLocator } from './service_locator.js';
5
- import { keys } from './typedefs.js';
6
5
  import { parseArgument, schemas } from './validators.js';
7
- const requestUrlSchema = z.object({ url: z.string() });
8
6
  // new properties on the Request object breaks serialization
9
- const requestOptionalSchemaShapes = {
7
+ // Compiled once: every `Request` runs it, and the generated fast path skips zod's interpreter.
8
+ const requestOptionsSchema = z.compile(z.looseObject({
9
+ url: z.string(),
10
10
  uniqueKey: z.string().optional(),
11
11
  method: z.string().optional(),
12
12
  payload: z.union([z.string(), z.instanceof(Uint8Array)]).optional(),
@@ -23,9 +23,7 @@ const requestOptionalSchemaShapes = {
23
23
  crawlDepth: schemas.anyNumber
24
24
  .refine((value) => value >= 0, 'Expected a number greater than or equal to 0')
25
25
  .optional(),
26
- };
27
- // Each schema is wrapped in a single-key object so validation errors carry the property name.
28
- const requestOptionalSchemas = Object.fromEntries(Object.entries(requestOptionalSchemaShapes).map(([key, schema]) => [key, z.object({ [key]: schema })]));
26
+ }));
29
27
  /**
30
28
  * Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata, together
31
29
  * with the processing state a request queue keeps about it (`retryCount`, `errorMessages`, `handledAt`, ...).
@@ -110,22 +108,7 @@ class CrawleeRequest {
110
108
  'Did you mean `new Request({ url })`?');
111
109
  }
112
110
  parseArgument(options, schemas.anyObject, 'RequestOptions');
113
- parseArgument(options, requestUrlSchema, 'RequestOptions');
114
- // Full-shape validation is slow, because it checks all predicates
115
- // even if the validated object has only 1 property.
116
- // This custom validation loop iterates only over existing
117
- // properties and speeds up the validation cca 3-fold.
118
- keys(options).forEach((prop) => {
119
- // skip url, because it is validated above
120
- if (prop === 'url') {
121
- return;
122
- }
123
- const schema = requestOptionalSchemas[prop];
124
- const value = options[prop];
125
- if (schema) {
126
- parseArgument({ [prop]: value }, schema, 'RequestOptions');
127
- }
128
- });
111
+ parseArgument(options, requestOptionsSchema, 'RequestOptions');
129
112
  const { url, uniqueKey, payload, noRetry = false, sessionId, maxRetries, headers = {}, userData = {}, label, keepUrlFragment = false, useExtendedUniqueKey = false, alwaysEnqueue = false, skipNavigation, enqueueStrategy, crawlDepth, } = options;
130
113
  let { method = 'GET' } = options;
131
114
  method = method.toUpperCase();
@@ -41,6 +41,16 @@ export interface IRequestManager extends IRequestLoader {
41
41
  * @returns `true` if anything in the composition took responsibility for the signal.
42
42
  */
43
43
  recordPacingSignal(signal: PacingSignal): boolean;
44
+ /**
45
+ * Extends the lock on a request previously handed out by `fetchNextRequest()` and still being
46
+ * processed, on storage backends that reserve requests via locking (e.g. via
47
+ * {@link CrawlingContext.extendTimeout|`context.extendTimeout`}).
48
+ *
49
+ * @returns `true` when the lock was prolonged, `false` when this manager does not lock
50
+ * requests or no longer holds this one; non-locking implementations may leave it `undefined`,
51
+ * which callers treat as `false`.
52
+ */
53
+ extendRequestProcessingTimeSecs?(request: Request, secs: number): Promise<boolean>;
44
54
  }
45
55
  /**
46
56
  * How much of the URL space a {@link PacingSignal} covers.
@@ -92,4 +92,5 @@ export declare class RequestManagerTandem implements IRequestManager {
92
92
  * @inheritdoc
93
93
  */
94
94
  recordPacingSignal(signal: PacingSignal): boolean;
95
+ extendRequestProcessingTimeSecs(request: Request, secs: number): Promise<boolean>;
95
96
  }
@@ -196,4 +196,7 @@ export class RequestManagerTandem {
196
196
  recordPacingSignal(signal) {
197
197
  return this.#resolvedRequestManager?.recordPacingSignal(signal) ?? false;
198
198
  }
199
+ async extendRequestProcessingTimeSecs(request, secs) {
200
+ return (await this.getRequestManager()).extendRequestProcessingTimeSecs?.(request, secs) ?? false;
201
+ }
199
202
  }
@@ -195,6 +195,12 @@ export declare class RequestQueue implements IStorage, IRequestManager {
195
195
  * short the reservation of a long-lived one and have its in-flight request stolen.
196
196
  */
197
197
  setExpectedRequestProcessingTimeSecs(secs: number): Promise<void>;
198
+ /**
199
+ * @inheritdoc
200
+ * Unlike {@link RequestQueue.setExpectedRequestProcessingTimeSecs}, which sizes every future lock,
201
+ * this only touches the one request it is given.
202
+ */
203
+ extendRequestProcessingTimeSecs(request: Request, secs: number): Promise<boolean>;
198
204
  /**
199
205
  * Caches information about request to beware of unneeded addRequest() calls.
200
206
  */
@@ -38,19 +38,20 @@ const addRequestsBatchedOptionsSchema = z.strictObject({
38
38
  waitBetweenBatchesMillis: schemas.anyNumber.default(1000),
39
39
  maxNewRequests: schemas.anyNumber.optional(),
40
40
  });
41
- const newRequestLikeSchema = z.looseObject({
41
+ // Compiled: these run once per request.
42
+ const newRequestLikeSchema = z.compile(z.looseObject({
42
43
  url: z.string(),
43
44
  id: z.undefined().optional(),
44
- });
45
- const handledRequestSchema = z.looseObject({
45
+ }));
46
+ const handledRequestSchema = z.compile(z.looseObject({
46
47
  id: z.string(),
47
48
  uniqueKey: z.string(),
48
49
  handledAt: z.string().optional(),
49
- });
50
- const reclaimedRequestSchema = z.looseObject({
50
+ }));
51
+ const reclaimedRequestSchema = z.compile(z.looseObject({
51
52
  id: z.string(),
52
53
  uniqueKey: z.string(),
53
- });
54
+ }));
54
55
  const uniqueKeySchema = z.string();
55
56
  const openOptionsSchema = z.strictObject({
56
57
  configuration: z.instanceof(Configuration).optional(),
@@ -674,6 +675,17 @@ export class RequestQueue {
674
675
  this.#expectedRequestProcessingSecs = secs;
675
676
  await this.backend.setExpectedRequestProcessingTimeSecs?.(secs);
676
677
  }
678
+ /**
679
+ * @inheritdoc
680
+ * Unlike {@link RequestQueue.setExpectedRequestProcessingTimeSecs}, which sizes every future lock,
681
+ * this only touches the one request it is given.
682
+ */
683
+ async extendRequestProcessingTimeSecs(request, secs) {
684
+ if (!request.id) {
685
+ return false;
686
+ }
687
+ return (await this.backend.extendRequestProcessingTimeSecs?.(request.id, secs)) ?? false;
688
+ }
677
689
  /**
678
690
  * Caches information about request to beware of unneeded addRequest() calls.
679
691
  */