@crawlee/basic 4.0.0-beta.170 → 4.0.0-beta.172

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.
Files changed (73) hide show
  1. package/index.d.ts +10 -0
  2. package/index.js +10 -0
  3. package/internals/autoscaling/autoscaled_pool.d.ts +196 -0
  4. package/internals/autoscaling/autoscaled_pool.js +385 -0
  5. package/internals/autoscaling/concurrency_system.d.ts +267 -0
  6. package/internals/autoscaling/concurrency_system.js +364 -0
  7. package/internals/autoscaling/cpu_load_signal.d.ts +43 -0
  8. package/internals/autoscaling/cpu_load_signal.js +46 -0
  9. package/internals/autoscaling/event_loop_load_signal.d.ts +51 -0
  10. package/internals/autoscaling/event_loop_load_signal.js +60 -0
  11. package/internals/autoscaling/index.d.ts +9 -0
  12. package/internals/autoscaling/index.js +9 -0
  13. package/internals/autoscaling/load_signal.d.ts +98 -0
  14. package/internals/autoscaling/load_signal.js +103 -0
  15. package/internals/autoscaling/memory_load_signal.d.ts +47 -0
  16. package/internals/autoscaling/memory_load_signal.js +104 -0
  17. package/internals/autoscaling/snapshotter.d.ts +84 -0
  18. package/internals/autoscaling/snapshotter.js +67 -0
  19. package/internals/autoscaling/storage_backend_load_signal.d.ts +56 -0
  20. package/internals/autoscaling/storage_backend_load_signal.js +73 -0
  21. package/internals/autoscaling/system_status.d.ts +122 -0
  22. package/internals/autoscaling/system_status.js +139 -0
  23. package/internals/autoscaling/weighted_avg.d.ts +5 -0
  24. package/internals/autoscaling/weighted_avg.js +14 -0
  25. package/internals/basic-crawler.d.ts +11 -2
  26. package/internals/basic-crawler.js +13 -1
  27. package/internals/cookie_utils.d.ts +44 -0
  28. package/internals/cookie_utils.js +122 -0
  29. package/internals/crawler-run.d.ts +3 -1
  30. package/internals/crawler-run.js +1 -1
  31. package/internals/crawlers/context_pipeline.d.ts +70 -0
  32. package/internals/crawlers/context_pipeline.js +122 -0
  33. package/internals/crawlers/crawler_commons.d.ts +193 -0
  34. package/internals/crawlers/crawler_commons.js +1 -0
  35. package/internals/crawlers/error_snapshotter.d.ts +57 -0
  36. package/internals/crawlers/error_snapshotter.js +117 -0
  37. package/internals/crawlers/error_tracker.d.ts +54 -0
  38. package/internals/crawlers/error_tracker.js +308 -0
  39. package/internals/crawlers/index.d.ts +5 -0
  40. package/internals/crawlers/index.js +4 -0
  41. package/internals/crawlers/internals/types.d.ts +7 -0
  42. package/internals/crawlers/internals/types.js +1 -0
  43. package/internals/crawlers/statistics.d.ts +318 -0
  44. package/internals/crawlers/statistics.js +525 -0
  45. package/internals/enqueue_links/enqueue_links.d.ts +152 -0
  46. package/internals/enqueue_links/enqueue_links.js +76 -0
  47. package/internals/enqueue_links/index.d.ts +2 -0
  48. package/internals/enqueue_links/index.js +2 -0
  49. package/internals/enqueue_links/shared.d.ts +98 -0
  50. package/internals/enqueue_links/shared.js +259 -0
  51. package/internals/errors.d.ts +58 -0
  52. package/internals/errors.js +72 -0
  53. package/internals/http.d.ts +9 -0
  54. package/internals/http.js +28 -0
  55. package/internals/router.d.ts +306 -0
  56. package/internals/router.js +310 -0
  57. package/internals/session_pool/consts.d.ts +3 -0
  58. package/internals/session_pool/consts.js +3 -0
  59. package/internals/session_pool/errors.d.ts +7 -0
  60. package/internals/session_pool/errors.js +11 -0
  61. package/internals/session_pool/fingerprint.d.ts +9 -0
  62. package/internals/session_pool/fingerprint.js +30 -0
  63. package/internals/session_pool/index.d.ts +4 -0
  64. package/internals/session_pool/index.js +4 -0
  65. package/internals/session_pool/session.d.ts +153 -0
  66. package/internals/session_pool/session.js +221 -0
  67. package/internals/session_pool/session_pool.d.ts +242 -0
  68. package/internals/session_pool/session_pool.js +429 -0
  69. package/internals/sitemap_request_loader.d.ts +196 -0
  70. package/internals/sitemap_request_loader.js +432 -0
  71. package/internals/throttling_request_manager.d.ts +217 -0
  72. package/internals/throttling_request_manager.js +829 -0
  73. package/package.json +11 -7
@@ -0,0 +1,122 @@
1
+ import { serviceLocator } from '@crawlee/core';
2
+ import { Cookie, CookieJar } from 'tough-cookie';
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,4 +1,6 @@
1
- import type { AutoscaledPoolOptions, CrawleeLogger, IConcurrencySystem } from '@crawlee/core';
1
+ import type { CrawleeLogger } from '@crawlee/core';
2
+ import type { AutoscaledPoolOptions } from './autoscaling/autoscaled_pool.js';
3
+ import type { IConcurrencySystem } from './autoscaling/concurrency_system.js';
2
4
  /** Everything a run needs to exist. Whatever a crawler resolves per run is resolved before the run is built. */
3
5
  export interface CrawlerRunSetup {
4
6
  log: CrawleeLogger;
@@ -1,4 +1,4 @@
1
- import { AutoscaledPool } from '@crawlee/core';
1
+ import { AutoscaledPool } from './autoscaling/autoscaled_pool.js';
2
2
  /**
3
3
  * Everything that belongs to a single {@link BasicCrawler.run|`crawler.run()`} — the task loop dispatching its
4
4
  * requests, the concurrency governor those requests are booked against, and the bookkeeping that must not survive
@@ -0,0 +1,70 @@
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
+ }
@@ -0,0 +1,122 @@
1
+ import { serviceLocator, SessionError } from '@crawlee/core';
2
+ import { ContextPipelineCleanupError, ContextPipelineInitializationError, ContextPipelineInterruptedError, RequestHandlerError, } from '../errors.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
+ }
@@ -0,0 +1,193 @@
1
+ import type { Awaitable, Dictionary, HttpRequestOptions, ISession, ProxyInfo, SendRequestOptions } from '@crawlee/types';
2
+ import type { ReadonlyDeep } from 'type-fest';
3
+ import type { AddRequestsBatchedResult, CrawleeLogger, Dataset, KeyValueStore, Request, RequestOptions, Source, StorageIdentifier } from '@crawlee/core';
4
+ import type { EnqueueUrlsOptions } from '../enqueue_links/index.js';
5
+ /** @internal */
6
+ export type IsAny<T> = 0 extends 1 & T ? true : false;
7
+ /**
8
+ * A request input (URL string, request-options object, or {@link Request}) whose `userData` is typed
9
+ * according to its `label`, based on a router's route map.
10
+ *
11
+ * When the route map is open (the default `Record<string, ...>`), this is just the regular loose
12
+ * {@link Source} input. When the map declares concrete labels, providing a `label` requires the matching
13
+ * `userData` shape and rejects labels not present in the map; unlabeled requests keep loose `userData`.
14
+ */
15
+ export type LabeledSource<Routes extends Record<keyof Routes, Dictionary>> = string extends keyof Routes ? string | Source : string | Request | ({
16
+ requestsFromUrl?: string;
17
+ regex?: RegExp;
18
+ } & ({
19
+ [Label in keyof Routes & string]: Omit<Partial<RequestOptions<Routes[Label]>>, 'label'> & {
20
+ label: Label;
21
+ };
22
+ }[keyof Routes & string] | (Omit<Partial<RequestOptions>, 'label'> & {
23
+ label?: undefined;
24
+ })));
25
+ /**
26
+ * The iterable/array of {@link LabeledSource} inputs accepted by the label-aware `addRequests`/`run`
27
+ * methods of a crawler bound to a typed router.
28
+ */
29
+ export type TypedRequestsLike<Routes extends Record<keyof Routes, Dictionary>> = AsyncIterable<LabeledSource<Routes>> | Iterable<LabeledSource<Routes>> | LabeledSource<Routes>[];
30
+ /**
31
+ * The label-aware `addRequests` method signature exposed on a request handler's context when the crawler is
32
+ * bound to a typed router. Mirrors {@link RestrictedCrawlingContext.addRequests} with typed sources.
33
+ */
34
+ export type TypedContextAddRequests<Routes extends Record<keyof Routes, Dictionary>> = (requestsLike: ReadonlyDeep<LabeledSource<Routes>[]>, options?: ReadonlyDeep<EnqueueUrlsOptions>) => Promise<AddRequestsBatchedResult>;
35
+ /**
36
+ * An `enqueueLinks`-options object with its `label`/`userData` retyped according to a router's route map: a
37
+ * declared `label` requires the matching `userData` shape (unknown labels are rejected), while unlabeled
38
+ * calls keep loose `userData`. Returns the options unchanged when the route map is open (the default).
39
+ */
40
+ type TypedEnqueueLinksOptions<Options, Routes extends Record<keyof Routes, Dictionary>> = string extends keyof Routes ? Options : Omit<Options, 'label' | 'userData'> & ({
41
+ [Label in keyof Routes & string]: {
42
+ label: Label;
43
+ userData?: Routes[Label];
44
+ };
45
+ }[keyof Routes & string] | {
46
+ label?: undefined;
47
+ userData?: Dictionary;
48
+ });
49
+ /**
50
+ * Transforms a context's existing `enqueueLinks` method so that the `label`/`userData` in its options follow
51
+ * the router's route map, while preserving everything else about the signature (argument optionality and
52
+ * return type, which differ between crawler types).
53
+ */
54
+ export type TypedContextEnqueueLinks<EnqueueLinks, Routes extends Record<keyof Routes, Dictionary>> = EnqueueLinks extends (options?: infer Options) => infer Result ? (options?: TypedEnqueueLinksOptions<Options, Routes>) => Result : EnqueueLinks extends (options: infer Options) => infer Result ? (options: TypedEnqueueLinksOptions<Options, Routes>) => Result : EnqueueLinks;
55
+ export type WithRequired<T, K extends keyof T> = T & {
56
+ [P in K]-?: T[P];
57
+ };
58
+ export type LoadedRequest<R extends Request> = WithRequired<R, 'id' | 'loadedUrl'>;
59
+ /** @internal */
60
+ export type LoadedContext<Context extends RestrictedCrawlingContext> = IsAny<Context> extends true ? Context : {
61
+ request: LoadedRequest<Context['request']>;
62
+ } & Omit<Context, 'request'>;
63
+ export interface RestrictedCrawlingContext<UserData extends Dictionary = Dictionary> {
64
+ id: string;
65
+ session: ISession;
66
+ /**
67
+ * An object with information about currently used proxy by the crawler
68
+ * and configured by the {@link ProxyConfiguration} class.
69
+ */
70
+ proxyInfo?: ProxyInfo;
71
+ /**
72
+ * The original {@link Request} object.
73
+ */
74
+ request: Request<UserData>;
75
+ /**
76
+ * This function allows you to push data to a {@link Dataset} specified by name, or the one currently used by the crawler.
77
+ *
78
+ * Shortcut for `crawler.pushData()`.
79
+ *
80
+ * @param [data] Data to be pushed to the default dataset.
81
+ */
82
+ pushData(data: ReadonlyDeep<Parameters<Dataset['pushData']>[0]>, datasetIdentifier?: string | StorageIdentifier): Promise<void>;
83
+ /**
84
+ * Add requests directly to the request queue currently used by the crawler.
85
+ *
86
+ * Optionally, the function allows you to filter the target URLs using an array of glob or regexp patterns,
87
+ * the same way {@link CrawlingContext.enqueueLinks|`enqueueLinks`} does for extracted links.
88
+ *
89
+ * @param requests The requests to add
90
+ * @param options Options for the request queue
91
+ */
92
+ addRequests: (requestsLike: ReadonlyDeep<(string | Source)[]>, options?: ReadonlyDeep<EnqueueUrlsOptions>) => Promise<AddRequestsBatchedResult>;
93
+ /**
94
+ * Returns the state - a piece of mutable persistent data shared across all the request handler runs.
95
+ */
96
+ useState: <State extends Dictionary = Dictionary>(defaultValue?: State) => Promise<State>;
97
+ /**
98
+ * Get a key-value store with given name or id, or the default one for the crawler.
99
+ */
100
+ getKeyValueStore: (identifier?: string | StorageIdentifier) => Promise<Pick<KeyValueStore, 'id' | 'name' | 'getValue' | 'getAutoSavedValue' | 'setValue' | 'getPublicUrl'>>;
101
+ /**
102
+ * A preconfigured logger for the request handler.
103
+ */
104
+ log: CrawleeLogger;
105
+ }
106
+ export interface CrawlingContext<UserData extends Dictionary = Dictionary> extends RestrictedCrawlingContext<UserData> {
107
+ /**
108
+ * Fires HTTP request via the internal HTTP client, allowing to override the request options on the fly.
109
+ *
110
+ * This is handy when you work with a browser crawler but want to execute some requests outside it (e.g. API requests).
111
+ * Check the [Skipping navigations for certain requests](https://crawlee.dev/js/docs/examples/skip-navigation) example for
112
+ * more detailed explanation of how to do that.
113
+ *
114
+ * ```ts
115
+ * async requestHandler({ sendRequest }) {
116
+ * const { body } = await sendRequest({
117
+ * // override headers only
118
+ * headers: { ... },
119
+ * });
120
+ * },
121
+ * ```
122
+ */
123
+ sendRequest: (requestOverrides?: Partial<HttpRequestOptions>, optionsOverrides?: SendRequestOptions) => Promise<Response>;
124
+ /**
125
+ * 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.
126
+ *
127
+ * The callback runs *outside* the request's storage transaction, so storage writes made here are
128
+ * applied immediately and are **not** rolled back when the request fails. In
129
+ * {@link AdaptivePlaywrightCrawler} it also runs once per request handler attempt, so a write
130
+ * here can land more than once for a single request. Push results from the request handler itself.
131
+ */
132
+ registerDeferredCleanup(cleanup: () => Promise<unknown>): void;
133
+ /**
134
+ * Registers `callback` to run once the request's storage writes have been committed, or once
135
+ * committing them has failed - the point where a write made in the handler can finally be reacted
136
+ * to where it was made. Two things belong here: side effects that must agree with what actually
137
+ * landed (result counters, progress reporting), and handling of a write that did not land at all.
138
+ *
139
+ * ```ts
140
+ * async requestHandler({ pushData, afterStorageCommit, useState }) {
141
+ * const state = await useState({ itemCount: 0 });
142
+ *
143
+ * await pushData(item);
144
+ * afterStorageCommit((error) => {
145
+ * if (error) {
146
+ * throw new NonRetryableError('The page is too big to store', { cause: error });
147
+ * }
148
+ * state.itemCount++;
149
+ * });
150
+ * }
151
+ * ```
152
+ *
153
+ * `useState()` is deliberately not transactional, which is what makes the counter case necessary:
154
+ * incrementing in the handler would keep the increment for a request that pushes an item and then
155
+ * fails, while the item itself is rolled back.
156
+ *
157
+ * Callbacks run in registration order, after the commit and before the request is marked as handled.
158
+ * The first one to throw propagates and the rest do not run; on a failed commit its error replaces
159
+ * the commit error, so a callback can decide what the request fails with. After a *successful*
160
+ * commit, a callback that throws fails the request **without a retry** whatever it throws - the
161
+ * writes are already durable and retrying would duplicate them, so anything that is not already a
162
+ * {@link NonRetryableError} is wrapped in an {@link AfterCommitError}.
163
+ *
164
+ * Callbacks are *not* run when the request handler itself fails - nothing was written, and
165
+ * `errorHandler` / `failedRequestHandler` cover that.
166
+ *
167
+ * Throws when storage is not transactional (`transactionalStorage: false`) - writes are then applied
168
+ * as they are made and throw at the call site on their own. In {@link AdaptivePlaywrightCrawler}
169
+ * the callback belongs to the current request handler attempt, so it runs only for the attempt whose
170
+ * writes are the ones being committed.
171
+ */
172
+ afterStorageCommit(callback: (error?: Error) => Awaitable<void>): void;
173
+ /**
174
+ * Gives the current request `secs` more seconds to finish, for when how long it needs is only apparent
175
+ * once it is already running - a listing page that turns out to have far more to scroll through than
176
+ * usual, say. Prefer `requestHandlerTimeoutSecs`, or a per-route override via
177
+ * {@link Router.addHandler|`router.addHandler`}, whenever the time needed is known up front.
178
+ *
179
+ * ```ts
180
+ * router.addHandler('LIST', async ({ extendTimeout, page }) => {
181
+ * const pageCount = await countPages(page);
182
+ * extendTimeout(pageCount * 10);
183
+ * await scrapeAllPages(page);
184
+ * });
185
+ * ```
186
+ *
187
+ * Extends the request handler's own timeout and the crawler's internal one together, so the extension
188
+ * is not immediately undone by the latter. Calling it from a handler that has already timed out does
189
+ * nothing.
190
+ */
191
+ extendTimeout(secs: number): void;
192
+ }
193
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,57 @@
1
+ import type { KeyValueStore } from '@crawlee/core';
2
+ import type { CrawlingContext } from '../crawlers/crawler_commons.js';
3
+ import type { ErrnoException } from './error_tracker.js';
4
+ import type { SnapshottableProperties } from './internals/types.js';
5
+ interface BrowserCrawlingContext {
6
+ saveSnapshot: (options: {
7
+ key: string;
8
+ }) => Promise<void>;
9
+ }
10
+ export interface SnapshotResult {
11
+ screenshotFileName?: string;
12
+ htmlFileName?: string;
13
+ }
14
+ interface ErrorSnapshot {
15
+ screenshotFileName?: string;
16
+ screenshotFileUrl?: string;
17
+ htmlFileName?: string;
18
+ htmlFileUrl?: string;
19
+ }
20
+ /**
21
+ * ErrorSnapshotter class is used to capture a screenshot of the page and a snapshot of the HTML when an error occurs during web crawling.
22
+ *
23
+ * This functionality is opt-in, and can be enabled via the crawler options:
24
+ *
25
+ * ```ts
26
+ * const crawler = new BasicCrawler({
27
+ * // ...
28
+ * statistics: new Statistics({ saveErrorSnapshots: true }),
29
+ * });
30
+ * ```
31
+ */
32
+ export declare class ErrorSnapshotter {
33
+ static readonly MAX_ERROR_CHARACTERS = 30;
34
+ static readonly MAX_HASH_LENGTH = 30;
35
+ static readonly MAX_FILENAME_LENGTH = 250;
36
+ static readonly BASE_MESSAGE = "An error occurred";
37
+ static readonly SNAPSHOT_PREFIX = "ERROR_SNAPSHOT";
38
+ /**
39
+ * Capture a snapshot of the error context.
40
+ */
41
+ captureSnapshot(error: ErrnoException, context: CrawlingContext & SnapshottableProperties): Promise<ErrorSnapshot>;
42
+ /**
43
+ * Captures a snapshot of the current page using the context.saveSnapshot function.
44
+ * This function is applicable for browser contexts only.
45
+ * Returns an object containing the filenames of the screenshot and HTML file.
46
+ */
47
+ contextCaptureSnapshot(context: BrowserCrawlingContext, fileName: string): Promise<SnapshotResult | undefined>;
48
+ /**
49
+ * Save the HTML snapshot of the page, and return the key it was stored under.
50
+ */
51
+ saveHTMLSnapshot(html: string, keyValueStore: Pick<KeyValueStore, 'setValue'>, fileName: string): Promise<string | undefined>;
52
+ /**
53
+ * Generate a unique fileName for each error snapshot.
54
+ */
55
+ generateFilename(error: ErrnoException): string;
56
+ }
57
+ export {};