@crawlee/core 4.0.0-beta.2 → 4.0.0-beta.200

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 (259) hide show
  1. package/README.md +17 -13
  2. package/byte_utils.d.ts +17 -0
  3. package/byte_utils.js +42 -0
  4. package/configuration.d.ts +88 -234
  5. package/configuration.js +162 -230
  6. package/debug.d.ts +36 -0
  7. package/debug.js +70 -0
  8. package/errors.d.ts +43 -10
  9. package/errors.js +57 -12
  10. package/events/event_manager.d.ts +35 -9
  11. package/events/event_manager.js +12 -13
  12. package/events/index.d.ts +1 -1
  13. package/events/index.js +0 -1
  14. package/events/local_event_manager.d.ts +15 -5
  15. package/events/local_event_manager.js +39 -42
  16. package/events/system_info.d.ts +38 -0
  17. package/events/system_info.js +1 -0
  18. package/index.d.ts +8 -11
  19. package/index.js +9 -10
  20. package/internal.d.ts +8 -0
  21. package/internal.js +9 -0
  22. package/iterables.d.ts +79 -0
  23. package/iterables.js +134 -0
  24. package/log.d.ts +81 -3
  25. package/log.js +134 -1
  26. package/memory-storage/consts.d.ts +4 -0
  27. package/memory-storage/consts.js +4 -0
  28. package/memory-storage/index.d.ts +1 -0
  29. package/memory-storage/index.js +1 -0
  30. package/memory-storage/memory-storage.d.ts +43 -0
  31. package/memory-storage/memory-storage.js +158 -0
  32. package/memory-storage/resource-clients/common/base-client.d.ts +4 -0
  33. package/memory-storage/resource-clients/common/base-client.js +6 -0
  34. package/memory-storage/resource-clients/dataset.d.ts +40 -0
  35. package/memory-storage/resource-clients/dataset.js +106 -0
  36. package/memory-storage/resource-clients/key-value-store.d.ts +56 -0
  37. package/memory-storage/resource-clients/key-value-store.js +180 -0
  38. package/memory-storage/resource-clients/request-queue.d.ts +77 -0
  39. package/memory-storage/resource-clients/request-queue.js +414 -0
  40. package/memory-storage/utils.d.ts +16 -0
  41. package/memory-storage/utils.js +41 -0
  42. package/owned_or_injected.d.ts +58 -0
  43. package/owned_or_injected.js +98 -0
  44. package/package.json +19 -20
  45. package/proxy_configuration.d.ts +29 -155
  46. package/proxy_configuration.js +38 -183
  47. package/recoverable_state.d.ts +177 -0
  48. package/recoverable_state.js +268 -0
  49. package/request.d.ts +62 -81
  50. package/request.js +125 -220
  51. package/serialization.d.ts +0 -1
  52. package/serialization.js +18 -18
  53. package/service_locator.d.ts +168 -0
  54. package/service_locator.js +255 -0
  55. package/storages/batched_adds.d.ts +37 -0
  56. package/storages/batched_adds.js +73 -0
  57. package/storages/dataset.d.ts +120 -56
  58. package/storages/dataset.js +299 -148
  59. package/storages/index.d.ts +9 -8
  60. package/storages/index.js +5 -8
  61. package/storages/key_value_store.d.ts +192 -65
  62. package/storages/key_value_store.js +453 -193
  63. package/storages/key_value_store_codec.d.ts +32 -0
  64. package/storages/key_value_store_codec.js +113 -0
  65. package/storages/request_dedup_cache.d.ts +22 -0
  66. package/storages/request_dedup_cache.js +48 -0
  67. package/storages/request_list.d.ts +55 -137
  68. package/storages/request_list.js +272 -245
  69. package/storages/request_loader.d.ts +127 -0
  70. package/storages/request_loader.js +36 -0
  71. package/storages/request_manager.d.ts +119 -0
  72. package/storages/request_manager.js +1 -0
  73. package/storages/request_manager_tandem.d.ts +94 -0
  74. package/storages/request_manager_tandem.js +200 -0
  75. package/storages/request_queue.d.ts +293 -48
  76. package/storages/request_queue.js +766 -216
  77. package/storages/storage_instance_manager.d.ts +86 -0
  78. package/storages/storage_instance_manager.js +256 -0
  79. package/storages/storage_stats.d.ts +48 -0
  80. package/storages/storage_stats.js +29 -0
  81. package/storages/transaction.d.ts +271 -0
  82. package/storages/transaction.js +296 -0
  83. package/storages/utils.d.ts +59 -11
  84. package/storages/utils.js +75 -15
  85. package/system-info/cpu-info.d.ts +67 -0
  86. package/system-info/cpu-info.js +216 -0
  87. package/system-info/memory-info.d.ts +31 -0
  88. package/system-info/memory-info.js +115 -0
  89. package/system-info/ps-tree.d.ts +17 -0
  90. package/system-info/ps-tree.js +144 -0
  91. package/system-info/runtime.d.ts +14 -0
  92. package/system-info/runtime.js +80 -0
  93. package/typedefs.d.ts +0 -6
  94. package/typedefs.js +0 -1
  95. package/url.d.ts +9 -0
  96. package/url.js +11 -0
  97. package/validators.d.ts +23 -18
  98. package/validators.js +14 -18
  99. package/autoscaling/autoscaled_pool.d.ts +0 -300
  100. package/autoscaling/autoscaled_pool.d.ts.map +0 -1
  101. package/autoscaling/autoscaled_pool.js +0 -533
  102. package/autoscaling/autoscaled_pool.js.map +0 -1
  103. package/autoscaling/index.d.ts +0 -4
  104. package/autoscaling/index.d.ts.map +0 -1
  105. package/autoscaling/index.js +0 -4
  106. package/autoscaling/index.js.map +0 -1
  107. package/autoscaling/snapshotter.d.ts +0 -189
  108. package/autoscaling/snapshotter.d.ts.map +0 -1
  109. package/autoscaling/snapshotter.js +0 -292
  110. package/autoscaling/snapshotter.js.map +0 -1
  111. package/autoscaling/system_status.d.ts +0 -181
  112. package/autoscaling/system_status.d.ts.map +0 -1
  113. package/autoscaling/system_status.js +0 -169
  114. package/autoscaling/system_status.js.map +0 -1
  115. package/configuration.d.ts.map +0 -1
  116. package/configuration.js.map +0 -1
  117. package/cookie_utils.d.ts +0 -43
  118. package/cookie_utils.d.ts.map +0 -1
  119. package/cookie_utils.js +0 -113
  120. package/cookie_utils.js.map +0 -1
  121. package/crawlers/crawler_commons.d.ts +0 -199
  122. package/crawlers/crawler_commons.d.ts.map +0 -1
  123. package/crawlers/crawler_commons.js +0 -116
  124. package/crawlers/crawler_commons.js.map +0 -1
  125. package/crawlers/crawler_extension.d.ts +0 -12
  126. package/crawlers/crawler_extension.d.ts.map +0 -1
  127. package/crawlers/crawler_extension.js +0 -14
  128. package/crawlers/crawler_extension.js.map +0 -1
  129. package/crawlers/crawler_utils.d.ts +0 -10
  130. package/crawlers/crawler_utils.d.ts.map +0 -1
  131. package/crawlers/crawler_utils.js +0 -12
  132. package/crawlers/crawler_utils.js.map +0 -1
  133. package/crawlers/error_snapshotter.d.ts +0 -59
  134. package/crawlers/error_snapshotter.d.ts.map +0 -1
  135. package/crawlers/error_snapshotter.js +0 -118
  136. package/crawlers/error_snapshotter.js.map +0 -1
  137. package/crawlers/error_tracker.d.ts +0 -54
  138. package/crawlers/error_tracker.d.ts.map +0 -1
  139. package/crawlers/error_tracker.js +0 -309
  140. package/crawlers/error_tracker.js.map +0 -1
  141. package/crawlers/index.d.ts +0 -7
  142. package/crawlers/index.d.ts.map +0 -1
  143. package/crawlers/index.js +0 -7
  144. package/crawlers/index.js.map +0 -1
  145. package/crawlers/statistics.d.ts +0 -204
  146. package/crawlers/statistics.d.ts.map +0 -1
  147. package/crawlers/statistics.js +0 -346
  148. package/crawlers/statistics.js.map +0 -1
  149. package/enqueue_links/enqueue_links.d.ts +0 -242
  150. package/enqueue_links/enqueue_links.d.ts.map +0 -1
  151. package/enqueue_links/enqueue_links.js +0 -250
  152. package/enqueue_links/enqueue_links.js.map +0 -1
  153. package/enqueue_links/index.d.ts +0 -3
  154. package/enqueue_links/index.d.ts.map +0 -1
  155. package/enqueue_links/index.js +0 -3
  156. package/enqueue_links/index.js.map +0 -1
  157. package/enqueue_links/shared.d.ts +0 -67
  158. package/enqueue_links/shared.d.ts.map +0 -1
  159. package/enqueue_links/shared.js +0 -190
  160. package/enqueue_links/shared.js.map +0 -1
  161. package/errors.d.ts.map +0 -1
  162. package/errors.js.map +0 -1
  163. package/events/event_manager.d.ts.map +0 -1
  164. package/events/event_manager.js.map +0 -1
  165. package/events/index.d.ts.map +0 -1
  166. package/events/index.js.map +0 -1
  167. package/events/local_event_manager.d.ts.map +0 -1
  168. package/events/local_event_manager.js.map +0 -1
  169. package/http_clients/base-http-client.d.ts +0 -134
  170. package/http_clients/base-http-client.d.ts.map +0 -1
  171. package/http_clients/base-http-client.js +0 -33
  172. package/http_clients/base-http-client.js.map +0 -1
  173. package/http_clients/form-data-like.d.ts +0 -67
  174. package/http_clients/form-data-like.d.ts.map +0 -1
  175. package/http_clients/form-data-like.js +0 -5
  176. package/http_clients/form-data-like.js.map +0 -1
  177. package/http_clients/got-scraping-http-client.d.ts +0 -15
  178. package/http_clients/got-scraping-http-client.d.ts.map +0 -1
  179. package/http_clients/got-scraping-http-client.js +0 -69
  180. package/http_clients/got-scraping-http-client.js.map +0 -1
  181. package/http_clients/index.d.ts +0 -3
  182. package/http_clients/index.d.ts.map +0 -1
  183. package/http_clients/index.js +0 -3
  184. package/http_clients/index.js.map +0 -1
  185. package/index.d.ts.map +0 -1
  186. package/index.js.map +0 -1
  187. package/log.d.ts.map +0 -1
  188. package/log.js.map +0 -1
  189. package/proxy_configuration.d.ts.map +0 -1
  190. package/proxy_configuration.js.map +0 -1
  191. package/request.d.ts.map +0 -1
  192. package/request.js.map +0 -1
  193. package/router.d.ts +0 -134
  194. package/router.d.ts.map +0 -1
  195. package/router.js +0 -164
  196. package/router.js.map +0 -1
  197. package/serialization.d.ts.map +0 -1
  198. package/serialization.js.map +0 -1
  199. package/session_pool/consts.d.ts +0 -4
  200. package/session_pool/consts.d.ts.map +0 -1
  201. package/session_pool/consts.js +0 -4
  202. package/session_pool/consts.js.map +0 -1
  203. package/session_pool/errors.d.ts +0 -8
  204. package/session_pool/errors.d.ts.map +0 -1
  205. package/session_pool/errors.js +0 -12
  206. package/session_pool/errors.js.map +0 -1
  207. package/session_pool/events.d.ts +0 -3
  208. package/session_pool/events.d.ts.map +0 -1
  209. package/session_pool/events.js +0 -3
  210. package/session_pool/events.js.map +0 -1
  211. package/session_pool/index.d.ts +0 -6
  212. package/session_pool/index.d.ts.map +0 -1
  213. package/session_pool/index.js +0 -6
  214. package/session_pool/index.js.map +0 -1
  215. package/session_pool/session.d.ts +0 -199
  216. package/session_pool/session.d.ts.map +0 -1
  217. package/session_pool/session.js +0 -271
  218. package/session_pool/session.js.map +0 -1
  219. package/session_pool/session_pool.d.ts +0 -251
  220. package/session_pool/session_pool.d.ts.map +0 -1
  221. package/session_pool/session_pool.js +0 -375
  222. package/session_pool/session_pool.js.map +0 -1
  223. package/storages/access_checking.d.ts +0 -13
  224. package/storages/access_checking.d.ts.map +0 -1
  225. package/storages/access_checking.js +0 -14
  226. package/storages/access_checking.js.map +0 -1
  227. package/storages/dataset.d.ts.map +0 -1
  228. package/storages/dataset.js.map +0 -1
  229. package/storages/index.d.ts.map +0 -1
  230. package/storages/index.js.map +0 -1
  231. package/storages/key_value_store.d.ts.map +0 -1
  232. package/storages/key_value_store.js.map +0 -1
  233. package/storages/request_list.d.ts.map +0 -1
  234. package/storages/request_list.js.map +0 -1
  235. package/storages/request_provider.d.ts +0 -307
  236. package/storages/request_provider.d.ts.map +0 -1
  237. package/storages/request_provider.js +0 -555
  238. package/storages/request_provider.js.map +0 -1
  239. package/storages/request_queue.d.ts.map +0 -1
  240. package/storages/request_queue.js.map +0 -1
  241. package/storages/request_queue_v2.d.ts +0 -87
  242. package/storages/request_queue_v2.d.ts.map +0 -1
  243. package/storages/request_queue_v2.js +0 -438
  244. package/storages/request_queue_v2.js.map +0 -1
  245. package/storages/sitemap_request_list.d.ts +0 -245
  246. package/storages/sitemap_request_list.d.ts.map +0 -1
  247. package/storages/sitemap_request_list.js +0 -430
  248. package/storages/sitemap_request_list.js.map +0 -1
  249. package/storages/storage_manager.d.ts +0 -58
  250. package/storages/storage_manager.d.ts.map +0 -1
  251. package/storages/storage_manager.js +0 -105
  252. package/storages/storage_manager.js.map +0 -1
  253. package/storages/utils.d.ts.map +0 -1
  254. package/storages/utils.js.map +0 -1
  255. package/tsconfig.build.tsbuildinfo +0 -1
  256. package/typedefs.d.ts.map +0 -1
  257. package/typedefs.js.map +0 -1
  258. package/validators.d.ts.map +0 -1
  259. package/validators.js.map +0 -1
package/request.js CHANGED
@@ -1,45 +1,33 @@
1
1
  import crypto from 'node:crypto';
2
- import util from 'node:util';
3
- import ow from 'ow';
4
- import { normalizeUrl } from '@apify/utilities';
5
- import { log as defaultLog } from './log.js';
6
- import { keys } from './typedefs.js';
2
+ import { z } from 'zod';
3
+ import { cryptoRandomObjectId, normalizeUrl } from '@apify/utilities';
4
+ import { serviceLocator } from './service_locator.js';
5
+ import { parseArgument, schemas } from './validators.js';
7
6
  // new properties on the Request object breaks serialization
8
- const log = defaultLog.child({ prefix: 'Request' });
9
- const requestOptionalPredicates = {
10
- id: ow.optional.string,
11
- loadedUrl: ow.optional.string.url,
12
- uniqueKey: ow.optional.string,
13
- method: ow.optional.string,
14
- payload: ow.optional.any(ow.string, ow.uint8Array),
15
- noRetry: ow.optional.boolean,
16
- retryCount: ow.optional.number,
17
- sessionRotationCount: ow.optional.number,
18
- maxRetries: ow.optional.number,
19
- errorMessages: ow.optional.array.ofType(ow.string),
20
- headers: ow.optional.object,
21
- userData: ow.optional.object,
22
- label: ow.optional.string,
23
- handledAt: ow.optional.any(ow.string.date, ow.date),
24
- keepUrlFragment: ow.optional.boolean,
25
- useExtendedUniqueKey: ow.optional.boolean,
26
- skipNavigation: ow.optional.boolean,
27
- state: ow.optional.number.greaterThanOrEqual(0).lessThanOrEqual(6),
28
- };
29
- export var RequestState;
30
- (function (RequestState) {
31
- RequestState[RequestState["UNPROCESSED"] = 0] = "UNPROCESSED";
32
- RequestState[RequestState["BEFORE_NAV"] = 1] = "BEFORE_NAV";
33
- RequestState[RequestState["AFTER_NAV"] = 2] = "AFTER_NAV";
34
- RequestState[RequestState["REQUEST_HANDLER"] = 3] = "REQUEST_HANDLER";
35
- RequestState[RequestState["DONE"] = 4] = "DONE";
36
- RequestState[RequestState["ERROR_HANDLER"] = 5] = "ERROR_HANDLER";
37
- RequestState[RequestState["ERROR"] = 6] = "ERROR";
38
- RequestState[RequestState["SKIPPED"] = 7] = "SKIPPED";
39
- })(RequestState || (RequestState = {}));
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
+ uniqueKey: z.string().optional(),
11
+ method: z.string().optional(),
12
+ payload: z.union([z.string(), z.instanceof(Uint8Array)]).optional(),
13
+ noRetry: z.boolean().optional(),
14
+ sessionId: z.string().optional(),
15
+ maxRetries: schemas.anyNumber.optional(),
16
+ headers: z.looseObject({}).optional(),
17
+ userData: z.looseObject({}).optional(),
18
+ label: z.string().optional(),
19
+ keepUrlFragment: z.boolean().optional(),
20
+ useExtendedUniqueKey: z.boolean().optional(),
21
+ alwaysEnqueue: z.boolean().optional(),
22
+ skipNavigation: z.boolean().optional(),
23
+ crawlDepth: schemas.anyNumber
24
+ .refine((value) => value >= 0, 'Expected a number greater than or equal to 0')
25
+ .optional(),
26
+ }));
40
27
  /**
41
- * Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata.
42
- * The `Request` object also stores information about errors that occurred during processing of the request.
28
+ * Represents a URL to be crawled, optionally including HTTP method, headers, payload and other metadata, together
29
+ * with the processing state a request queue keeps about it (`retryCount`, `errorMessages`, `handledAt`, ...).
30
+ * Inside a crawler, requests are {@link CrawlingRequest}s, which add the crawler's own per-request settings.
43
31
  *
44
32
  * Each `Request` instance has the `uniqueKey` property, which can be either specified
45
33
  * manually in the constructor or generated automatically from the URL. Two requests with the same `uniqueKey`
@@ -55,21 +43,15 @@ export var RequestState;
55
43
  * const request = new Request({
56
44
  * url: 'http://www.example.com',
57
45
  * headers: { Accept: 'application/json' },
46
+ * userData: { foo: 'bar' },
58
47
  * });
59
48
  *
60
- * ...
61
- *
62
- * request.userData.foo = 'bar';
63
- * request.pushErrorMessage(new Error('Request failed!'));
64
- *
65
- * ...
66
- *
67
- * const foo = request.userData.foo;
49
+ * await requestQueue.addRequest(request);
68
50
  * ```
69
51
  * @category Sources
70
52
  */
71
- export class Request {
72
- /** Request ID */
53
+ class CrawleeRequest {
54
+ /** Storage-assigned request ID. Only present on requests that went through a {@link RequestQueue}. */
73
55
  id;
74
56
  /** URL of the web page to crawl. */
75
57
  url;
@@ -100,8 +82,13 @@ export class Request {
100
82
  /** Object with HTTP headers. Key is header name, value is the value. */
101
83
  headers;
102
84
  /** Private store for the custom user data assigned to the request. */
103
- _userData = {};
104
- /** Custom user data assigned to the request. */
85
+ #userData = {};
86
+ /**
87
+ * Custom user data assigned to the request.
88
+ *
89
+ * All data stored in `userData` must be JSON-serializable.
90
+ * Storing non-serializable values (e.g. functions, symbols) may result in unexpected results.
91
+ */
105
92
  userData = {};
106
93
  /**
107
94
  * ISO datetime string that indicates the time when the request has been processed.
@@ -110,117 +97,121 @@ export class Request {
110
97
  handledAt;
111
98
  /**
112
99
  * `Request` parameters including the URL, HTTP method and headers, and others.
100
+ *
101
+ * Processing state (`retryCount`, `errorMessages`, `handledAt`, `loadedUrl`, `id`) is not an option;
102
+ * requests coming back from a storage are rebuilt with {@link Request.fromSchema}.
113
103
  */
114
104
  constructor(options) {
115
- ow(options, 'RequestOptions', ow.object);
116
- ow(options.url, 'RequestOptions.url', ow.string);
117
- // 'ow' validation is slow, because it checks all predicates
118
- // even if the validated object has only 1 property.
119
- // This custom validation loop iterates only over existing
120
- // properties and speeds up the validation cca 3-fold.
121
- // See https://github.com/sindresorhus/ow/issues/193
122
- keys(options).forEach((prop) => {
123
- // skip url, because it is validated above
124
- if (prop === 'url') {
125
- return;
126
- }
127
- const predicate = requestOptionalPredicates[prop];
128
- const value = options[prop];
129
- if (predicate) {
130
- ow(value, `RequestOptions.${prop}`, predicate);
131
- }
132
- });
133
- const { id, url, loadedUrl, uniqueKey, payload, noRetry = false, retryCount = 0, sessionRotationCount = 0, maxRetries, errorMessages = [], headers = {}, userData = {}, label, handledAt, keepUrlFragment = false, useExtendedUniqueKey = false, skipNavigation, enqueueStrategy, } = options;
105
+ // A bare URL is a common slip — point at the object form instead of a generic type error.
106
+ if (typeof options === 'string') {
107
+ throw new TypeError(`\`Request\` options must be an object, got the string '${options}'. ` +
108
+ 'Did you mean `new Request({ url })`?');
109
+ }
110
+ parseArgument(options, schemas.anyObject, 'RequestOptions');
111
+ parseArgument(options, requestOptionsSchema, 'RequestOptions');
112
+ const { url, uniqueKey, payload, noRetry = false, sessionId, maxRetries, headers = {}, userData = {}, label, keepUrlFragment = false, useExtendedUniqueKey = false, alwaysEnqueue = false, skipNavigation, enqueueStrategy, crawlDepth, } = options;
134
113
  let { method = 'GET' } = options;
135
114
  method = method.toUpperCase();
136
115
  if (method === 'GET' && payload)
137
116
  throw new Error('Request with GET method cannot have a payload.');
138
- this.id = id;
117
+ if (uniqueKey && alwaysEnqueue) {
118
+ throw new Error('`alwaysEnqueue` cannot be used together with a custom `uniqueKey`.');
119
+ }
139
120
  this.url = url;
140
- this.loadedUrl = loadedUrl;
141
121
  this.uniqueKey =
142
- uniqueKey || Request.computeUniqueKey({ url, method, payload, keepUrlFragment, useExtendedUniqueKey });
122
+ uniqueKey ||
123
+ CrawleeRequest.computeUniqueKey({
124
+ url,
125
+ method,
126
+ payload,
127
+ keepUrlFragment,
128
+ useExtendedUniqueKey,
129
+ alwaysEnqueue,
130
+ });
143
131
  this.method = method;
144
132
  this.payload = payload;
145
133
  this.noRetry = noRetry;
146
- this.retryCount = retryCount;
147
- this.sessionRotationCount = sessionRotationCount;
148
- this.errorMessages = [...errorMessages];
134
+ this.retryCount = 0;
135
+ this.errorMessages = [];
149
136
  this.headers = { ...headers };
150
- this.handledAt = handledAt instanceof Date ? handledAt.toISOString() : handledAt;
151
137
  if (label) {
152
138
  userData.label = label;
153
139
  }
140
+ // Read `__crawlee` explicitly - on a `userData` coming from another Request instance the
141
+ // bag is non-enumerable, so the spread alone would silently drop the internal state
142
+ // (e.g. `skipNavigation`) when a request is re-wrapped after a storage round trip.
143
+ this.#userData = { __crawlee: userData.__crawlee ?? {}, ...userData };
144
+ // `userData` must stay an enumerable own accessor — serialization in the storages relies on it
154
145
  Object.defineProperties(this, {
155
- _userData: {
156
- value: { __crawlee: {}, ...userData },
157
- enumerable: false,
158
- writable: true,
159
- },
160
146
  userData: {
161
- get: () => this._userData,
147
+ get: () => this.#userData,
162
148
  set: (value) => {
163
149
  Object.defineProperties(value, {
164
150
  __crawlee: {
165
- value: this._userData.__crawlee,
151
+ value: this.#userData.__crawlee,
166
152
  enumerable: false,
167
153
  writable: true,
168
154
  },
169
155
  toJSON: {
170
156
  value: () => {
171
- if (Object.keys(this._userData.__crawlee).length > 0) {
157
+ if (Object.keys(this.#userData.__crawlee).length > 0) {
172
158
  return {
173
- ...this._userData,
174
- __crawlee: this._userData.__crawlee,
159
+ ...this.#userData,
160
+ __crawlee: this.#userData.__crawlee,
175
161
  };
176
162
  }
177
- return this._userData;
163
+ return this.#userData;
178
164
  },
179
165
  enumerable: false,
180
166
  writable: true,
181
167
  },
182
168
  });
183
- this._userData = value;
169
+ this.#userData = value;
184
170
  },
185
171
  enumerable: true,
186
172
  },
187
173
  });
188
174
  // reassign userData to ensure internal `__crawlee` object is non-enumerable
189
175
  this.userData = userData;
176
+ const crawlee = (this.userData.__crawlee ??= {});
190
177
  if (skipNavigation != null)
191
- this.skipNavigation = skipNavigation;
178
+ crawlee.skipNavigation = skipNavigation;
192
179
  if (maxRetries != null)
193
- this.maxRetries = maxRetries;
194
- // If it's already set, don't override it (for instance when fetching from storage)
195
- if (enqueueStrategy) {
196
- this.enqueueStrategy ??= enqueueStrategy;
197
- }
198
- }
199
- /** Tells the crawler processing this request to skip the navigation and process the request directly. */
200
- get skipNavigation() {
201
- return this.userData.__crawlee?.skipNavigation ?? false;
180
+ crawlee.maxRetries = maxRetries;
181
+ if (crawlDepth != null)
182
+ crawlee.crawlDepth ??= crawlDepth;
183
+ if (sessionId)
184
+ crawlee.sessionId = sessionId;
185
+ // A stored request keeps the strategy it was enqueued under.
186
+ if (enqueueStrategy)
187
+ crawlee.enqueueStrategy ??= enqueueStrategy;
202
188
  }
203
- /** Tells the crawler processing this request to skip the navigation and process the request directly. */
204
- set skipNavigation(value) {
205
- if (!this.userData.__crawlee) {
206
- this.userData.__crawlee = { skipNavigation: value };
207
- }
208
- else {
209
- this.userData.__crawlee.skipNavigation = value;
210
- }
211
- }
212
- /** Indicates the number of times the crawling of the request has rotated the session due to a session or a proxy error. */
213
- get sessionRotationCount() {
214
- return this.userData.__crawlee?.sessionRotationCount ?? 0;
189
+ /**
190
+ * Rebuilds a request from its stored form, including the processing state a
191
+ * {@link RequestOptions} object cannot carry. Subclasses get instances of themselves.
192
+ */
193
+ static fromSchema(schema) {
194
+ const { id, retryCount = 0, errorMessages = [], handledAt, loadedUrl, ...options } = schema;
195
+ const request = new this(options);
196
+ request.id = id;
197
+ request.retryCount = retryCount;
198
+ request.errorMessages = [...errorMessages];
199
+ request.loadedUrl = loadedUrl;
200
+ // `apify-client` parses `*At` fields into `Date`s, whatever the schema says.
201
+ request.handledAt =
202
+ handledAt instanceof Date ? handledAt.toISOString() : handledAt;
203
+ return request;
215
204
  }
216
- /** Indicates the number of times the crawling of the request has rotated the session due to a session or a proxy error. */
217
- set sessionRotationCount(value) {
218
- if (!this.userData.__crawlee) {
219
- this.userData.__crawlee = { sessionRotationCount: value };
220
- }
221
- else {
222
- this.userData.__crawlee.sessionRotationCount = value;
223
- }
205
+ /**
206
+ * Converts the Crawlee Request object to a `fetch` API Request object.
207
+ * @returns The native `fetch` API Request object.
208
+ */
209
+ intoFetchAPIRequest() {
210
+ return new Request(this.url, {
211
+ method: this.method,
212
+ headers: this.headers,
213
+ body: this.payload,
214
+ });
224
215
  }
225
216
  /** shortcut for getting `request.userData.label` */
226
217
  get label() {
@@ -230,119 +221,33 @@ export class Request {
230
221
  set label(value) {
231
222
  this.userData.label = value;
232
223
  }
233
- /** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
234
- get maxRetries() {
235
- return this.userData.__crawlee?.maxRetries;
236
- }
237
- /** Maximum number of retries for this request. Allows to override the global `maxRequestRetries` option of `BasicCrawler`. */
238
- set maxRetries(value) {
239
- if (!this.userData.__crawlee) {
240
- this.userData.__crawlee = { maxRetries: value };
241
- }
242
- else {
243
- this.userData.__crawlee.maxRetries = value;
244
- }
245
- }
246
- /** Describes the request's current lifecycle state. */
247
- get state() {
248
- return this.userData.__crawlee?.state ?? RequestState.UNPROCESSED;
249
- }
250
- /** Describes the request's current lifecycle state. */
251
- set state(value) {
252
- if (!this.userData.__crawlee) {
253
- this.userData.__crawlee = { state: value };
254
- }
255
- else {
256
- this.userData.__crawlee.state = value;
257
- }
258
- }
259
- get enqueueStrategy() {
260
- return this.userData.__crawlee?.enqueueStrategy;
261
- }
262
- set enqueueStrategy(value) {
263
- if (!this.userData.__crawlee) {
264
- this.userData.__crawlee = { enqueueStrategy: value };
265
- }
266
- else {
267
- this.userData.__crawlee.enqueueStrategy = value;
268
- }
269
- }
270
- /**
271
- * Stores information about an error that occurred during processing of this request.
272
- *
273
- * You should always use Error instances when throwing errors in JavaScript.
274
- *
275
- * Nevertheless, to improve the debugging experience when using third party libraries
276
- * that may not always throw an Error instance, the function performs a type
277
- * inspection of the passed argument and attempts to extract as much information
278
- * as possible, since just throwing a bad type error makes any debugging rather difficult.
279
- *
280
- * @param errorOrMessage Error object or error message to be stored in the request.
281
- * @param [options]
282
- */
283
- pushErrorMessage(errorOrMessage, options = {}) {
284
- const { omitStack } = options;
285
- let message;
286
- const type = typeof errorOrMessage;
287
- if (type === 'object') {
288
- if (!errorOrMessage) {
289
- message = 'null';
290
- }
291
- else if (errorOrMessage instanceof Error) {
292
- message = omitStack
293
- ? errorOrMessage.message
294
- : // .stack includes the message
295
- errorOrMessage.stack;
296
- }
297
- else if (Reflect.has(Object(errorOrMessage), 'message')) {
298
- message = Reflect.get(Object(errorOrMessage), 'message');
299
- }
300
- else if (errorOrMessage.toString() !== '[object Object]') {
301
- message = errorOrMessage.toString();
302
- }
303
- else {
304
- try {
305
- message = util.inspect(errorOrMessage);
306
- }
307
- catch (err) {
308
- message = 'Unable to extract any message from the received object.';
309
- }
310
- }
311
- }
312
- else if (type === 'undefined') {
313
- message = 'undefined';
314
- }
315
- else {
316
- message = errorOrMessage.toString();
317
- }
318
- this.errorMessages.push(message);
319
- }
320
- // TODO: only for better BC, remove in v4
321
- _computeUniqueKey(options) {
322
- return Request.computeUniqueKey(options);
323
- }
324
- // TODO: only for better BC, remove in v4
325
- _hashPayload(payload) {
326
- return Request.hashPayload(payload);
327
- }
328
224
  /** @internal */
329
- static computeUniqueKey({ url, method = 'GET', payload, keepUrlFragment = false, useExtendedUniqueKey = false, }) {
225
+ static computeUniqueKey({ url, method = 'GET', payload, keepUrlFragment = false, useExtendedUniqueKey = false, alwaysEnqueue = false, }) {
330
226
  const normalizedMethod = method.toUpperCase();
331
227
  const normalizedUrl = normalizeUrl(url, keepUrlFragment) || url; // It returns null when url is invalid, causing weird errors.
228
+ let uniqueKey;
332
229
  if (!useExtendedUniqueKey) {
333
230
  if (normalizedMethod !== 'GET' && payload) {
334
- log.warningOnce(`We've encountered a ${normalizedMethod} Request with a payload. ` +
231
+ serviceLocator
232
+ .getLogger()
233
+ .warningOnce(`We've encountered a ${normalizedMethod} Request with a payload. ` +
335
234
  'This is fine. Just letting you know that if your requests point to the same URL ' +
336
235
  'and differ only in method and payload, you should see the "useExtendedUniqueKey" option of Request constructor.');
337
236
  }
338
- return normalizedUrl;
237
+ uniqueKey = normalizedUrl;
238
+ }
239
+ else {
240
+ const payloadHash = payload ? CrawleeRequest.hashPayload(payload) : '';
241
+ uniqueKey = `${normalizedMethod}|${payloadHash}|${normalizedUrl}`;
242
+ }
243
+ if (alwaysEnqueue) {
244
+ uniqueKey = `${cryptoRandomObjectId(17)}|${uniqueKey}`;
339
245
  }
340
- const payloadHash = payload ? Request.hashPayload(payload) : '';
341
- return `${normalizedMethod}(${payloadHash}):${normalizedUrl}`;
246
+ return uniqueKey;
342
247
  }
343
248
  /** @internal */
344
249
  static hashPayload(payload) {
345
250
  return crypto.createHash('sha256').update(payload).digest('base64').replace(/[+/=]/g, '').substring(0, 8);
346
251
  }
347
252
  }
348
- //# sourceMappingURL=request.js.map
253
+ export { CrawleeRequest as Request };
@@ -29,4 +29,3 @@ export declare function deserializeArray<T extends string | Buffer>(compressedDa
29
29
  * @internal
30
30
  */
31
31
  export declare function createDeserialize(compressedData: Buffer | Uint8Array): Readable;
32
- //# sourceMappingURL=serialization.d.ts.map
package/serialization.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { pipeline as streamPipeline, Readable, Writable } from 'node:stream';
2
2
  import util from 'node:util';
3
3
  import zlib from 'node:zlib';
4
- import ow from 'ow';
5
- import StreamArray from 'stream-json/streamers/StreamArray.js';
4
+ import StreamArray from 'stream-json/streamers/stream-array.js';
5
+ import { z } from 'zod';
6
+ import { parseArgument, schemas } from './validators.js';
6
7
  const pipeline = util.promisify(streamPipeline);
8
+ const uint8ArraySchema = z.instanceof(Uint8Array);
7
9
  /**
8
10
  * Transforms an array of items to a JSON in a streaming
9
11
  * fashion to save memory. It operates in batches to speed
@@ -11,32 +13,31 @@ const pipeline = util.promisify(streamPipeline);
11
13
  * @internal
12
14
  */
13
15
  class ArrayToJson extends Readable {
14
- data;
15
- offset = 0;
16
- batchSize;
16
+ #offset = 0;
17
+ #batchSize;
18
+ #data;
17
19
  constructor(data, options = {}) {
18
20
  super({
19
21
  ...options,
20
22
  autoDestroy: true,
21
23
  emitClose: true,
22
24
  });
23
- this.data = data;
24
25
  const { batchSize = 10000 } = options;
25
- this.batchSize = batchSize;
26
- this.data = data;
26
+ this.#batchSize = batchSize;
27
+ this.#data = data;
27
28
  this.push('[');
28
29
  }
29
30
  _read() {
30
31
  try {
31
- const items = this.data.slice(this.offset, this.offset + this.batchSize);
32
+ const items = this.#data.slice(this.#offset, this.#offset + this.#batchSize);
32
33
  if (items.length) {
33
34
  const json = JSON.stringify(items);
34
35
  // Strip brackets to flatten the batch.
35
36
  const itemString = json.substring(1, json.length - 1);
36
- if (this.offset > 0)
37
+ if (this.#offset > 0)
37
38
  this.push(',', 'utf8');
38
39
  this.push(itemString, 'utf8');
39
- this.offset += this.batchSize;
40
+ this.#offset += this.#batchSize;
40
41
  }
41
42
  else {
42
43
  this.push(']');
@@ -58,7 +59,7 @@ class ArrayToJson extends Readable {
58
59
  * @internal
59
60
  */
60
61
  export async function serializeArray(data) {
61
- ow(data, ow.array);
62
+ parseArgument(data, schemas.anyArray);
62
63
  const { chunks, collector } = createChunkCollector();
63
64
  await pipeline(new ArrayToJson(data), zlib.createGzip(), collector);
64
65
  return Buffer.concat(chunks);
@@ -73,9 +74,9 @@ export async function serializeArray(data) {
73
74
  * @internal
74
75
  */
75
76
  export async function deserializeArray(compressedData) {
76
- ow(compressedData, ow.uint8Array);
77
+ parseArgument(compressedData, uint8ArraySchema);
77
78
  const { chunks, collector } = createChunkCollector({ fromValuesStream: true });
78
- await pipeline(Readable.from([compressedData]), zlib.createGunzip(), StreamArray.withParser(), collector);
79
+ await pipeline(Readable.from([compressedData]), zlib.createGunzip(), StreamArray.withParserAsStream(), collector);
79
80
  return chunks;
80
81
  }
81
82
  /**
@@ -88,8 +89,8 @@ export async function deserializeArray(compressedData) {
88
89
  * @internal
89
90
  */
90
91
  export function createDeserialize(compressedData) {
91
- ow(compressedData, ow.uint8Array);
92
- const streamArray = StreamArray.withParser();
92
+ parseArgument(compressedData, uint8ArraySchema);
93
+ const streamArray = StreamArray.withParserAsStream();
93
94
  const destination = pluckValue(streamArray);
94
95
  streamPipeline(Readable.from([compressedData]), zlib.createGunzip(), destination, (err) => destination.emit(err));
95
96
  return destination;
@@ -116,7 +117,6 @@ function createChunkCollector(options = {}) {
116
117
  }
117
118
  function pluckValue(streamArray) {
118
119
  const realPush = streamArray.push.bind(streamArray);
119
- streamArray.push = (obj) => realPush(obj && obj.value);
120
+ streamArray.push = (obj) => realPush(obj?.value ?? null);
120
121
  return streamArray;
121
122
  }
122
- //# sourceMappingURL=serialization.js.map