@crawlee/core 4.0.0-beta.99 → 4.0.0-rc.1

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 (133) hide show
  1. package/README.md +1 -1
  2. package/configuration.d.ts +16 -47
  3. package/configuration.js +13 -25
  4. package/debug.js +4 -4
  5. package/errors.d.ts +28 -38
  6. package/errors.js +33 -47
  7. package/events/event_manager.d.ts +2 -2
  8. package/events/event_manager.js +7 -6
  9. package/events/index.d.ts +1 -0
  10. package/events/local_event_manager.d.ts +1 -8
  11. package/events/local_event_manager.js +13 -13
  12. package/events/system_info.d.ts +38 -0
  13. package/index.d.ts +2 -8
  14. package/index.js +4 -8
  15. package/internal.d.ts +8 -0
  16. package/internal.js +9 -0
  17. package/log.d.ts +10 -11
  18. package/log.js +52 -20
  19. package/memory-storage/memory-storage.d.ts +15 -18
  20. package/memory-storage/memory-storage.js +80 -58
  21. package/memory-storage/resource-clients/dataset.d.ts +1 -6
  22. package/memory-storage/resource-clients/dataset.js +23 -31
  23. package/memory-storage/resource-clients/key-value-store.d.ts +1 -10
  24. package/memory-storage/resource-clients/key-value-store.js +43 -67
  25. package/memory-storage/resource-clients/request-queue.d.ts +1 -42
  26. package/memory-storage/resource-clients/request-queue.js +109 -117
  27. package/owned_or_injected.d.ts +1 -3
  28. package/owned_or_injected.js +17 -17
  29. package/package.json +17 -20
  30. package/proxy_configuration.d.ts +21 -26
  31. package/proxy_configuration.js +35 -25
  32. package/recoverable_state.d.ts +104 -47
  33. package/recoverable_state.js +199 -74
  34. package/request.d.ts +20 -107
  35. package/request.js +78 -244
  36. package/serialization.js +17 -16
  37. package/service_locator.d.ts +22 -10
  38. package/service_locator.js +59 -48
  39. package/storages/batched_adds.d.ts +37 -0
  40. package/storages/batched_adds.js +73 -0
  41. package/storages/dataset.d.ts +13 -8
  42. package/storages/dataset.js +149 -40
  43. package/storages/index.d.ts +4 -4
  44. package/storages/index.js +2 -4
  45. package/storages/key_value_store.d.ts +16 -35
  46. package/storages/key_value_store.js +223 -110
  47. package/storages/key_value_store_codec.js +6 -11
  48. package/storages/request_dedup_cache.d.ts +1 -4
  49. package/storages/request_dedup_cache.js +15 -15
  50. package/storages/request_list.d.ts +9 -104
  51. package/storages/request_list.js +236 -233
  52. package/storages/request_loader.d.ts +49 -18
  53. package/storages/request_loader.js +36 -1
  54. package/storages/request_manager.d.ts +86 -0
  55. package/storages/request_manager_tandem.d.ts +14 -38
  56. package/storages/request_manager_tandem.js +67 -64
  57. package/storages/request_queue.d.ts +23 -50
  58. package/storages/request_queue.js +371 -226
  59. package/storages/storage_instance_manager.d.ts +2 -4
  60. package/storages/storage_instance_manager.js +21 -21
  61. package/storages/storage_stats.d.ts +1 -1
  62. package/storages/storage_stats.js +4 -4
  63. package/storages/transaction.d.ts +270 -0
  64. package/storages/transaction.js +296 -0
  65. package/storages/utils.d.ts +6 -3
  66. package/storages/utils.js +11 -2
  67. package/system-info/runtime.js +7 -7
  68. package/url.d.ts +9 -0
  69. package/url.js +11 -0
  70. package/validators.d.ts +23 -25
  71. package/validators.js +14 -25
  72. package/autoscaling/autoscaled_pool.d.ts +0 -213
  73. package/autoscaling/autoscaled_pool.js +0 -378
  74. package/autoscaling/client_load_signal.d.ts +0 -59
  75. package/autoscaling/client_load_signal.js +0 -73
  76. package/autoscaling/concurrency_system.d.ts +0 -283
  77. package/autoscaling/concurrency_system.js +0 -350
  78. package/autoscaling/cpu_load_signal.d.ts +0 -44
  79. package/autoscaling/cpu_load_signal.js +0 -46
  80. package/autoscaling/event_loop_load_signal.d.ts +0 -54
  81. package/autoscaling/event_loop_load_signal.js +0 -60
  82. package/autoscaling/index.d.ts +0 -9
  83. package/autoscaling/index.js +0 -9
  84. package/autoscaling/load_signal.d.ts +0 -99
  85. package/autoscaling/load_signal.js +0 -103
  86. package/autoscaling/memory_load_signal.d.ts +0 -56
  87. package/autoscaling/memory_load_signal.js +0 -106
  88. package/autoscaling/snapshotter.d.ts +0 -87
  89. package/autoscaling/snapshotter.js +0 -67
  90. package/autoscaling/system_status.d.ts +0 -161
  91. package/autoscaling/system_status.js +0 -139
  92. package/autoscaling/weighted_avg.d.ts +0 -5
  93. package/autoscaling/weighted_avg.js +0 -14
  94. package/cookie_utils.d.ts +0 -44
  95. package/cookie_utils.js +0 -122
  96. package/crawlers/context_pipeline.d.ts +0 -70
  97. package/crawlers/context_pipeline.js +0 -122
  98. package/crawlers/crawler_commons.d.ts +0 -257
  99. package/crawlers/crawler_commons.js +0 -107
  100. package/crawlers/error_snapshotter.d.ts +0 -59
  101. package/crawlers/error_snapshotter.js +0 -117
  102. package/crawlers/error_tracker.d.ts +0 -54
  103. package/crawlers/error_tracker.js +0 -308
  104. package/crawlers/index.d.ts +0 -5
  105. package/crawlers/index.js +0 -5
  106. package/crawlers/internals/types.d.ts +0 -7
  107. package/crawlers/statistics.d.ts +0 -209
  108. package/crawlers/statistics.js +0 -350
  109. package/enqueue_links/enqueue_links.d.ts +0 -264
  110. package/enqueue_links/enqueue_links.js +0 -271
  111. package/enqueue_links/index.d.ts +0 -2
  112. package/enqueue_links/index.js +0 -2
  113. package/enqueue_links/shared.d.ts +0 -83
  114. package/enqueue_links/shared.js +0 -221
  115. package/router.d.ts +0 -309
  116. package/router.js +0 -309
  117. package/session_pool/consts.d.ts +0 -3
  118. package/session_pool/consts.js +0 -3
  119. package/session_pool/errors.d.ts +0 -7
  120. package/session_pool/errors.js +0 -11
  121. package/session_pool/fingerprint.d.ts +0 -9
  122. package/session_pool/fingerprint.js +0 -30
  123. package/session_pool/index.d.ts +0 -4
  124. package/session_pool/index.js +0 -4
  125. package/session_pool/session.d.ts +0 -161
  126. package/session_pool/session.js +0 -218
  127. package/session_pool/session_pool.d.ts +0 -246
  128. package/session_pool/session_pool.js +0 -386
  129. package/storages/access_checking.d.ts +0 -12
  130. package/storages/access_checking.js +0 -17
  131. package/storages/sitemap_request_loader.d.ts +0 -249
  132. package/storages/sitemap_request_loader.js +0 -432
  133. /package/{crawlers/internals/types.js → events/system_info.js} +0 -0
@@ -0,0 +1,296 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { addTimeoutToPromise, storage as timeoutStorage, tryCancel } from '@apify/timeout';
3
+ import { serviceLocator } from '../service_locator.js';
4
+ import { AfterCommitError, NonRetryableError } from '../errors.js';
5
+ const DEFAULT_STORAGE_WRITE_POLICY = { requestQueue: 'writeThrough' };
6
+ const DEFAULT_COMMIT_TIMEOUT_MILLIS = 300_000;
7
+ const transactionStorage = new AsyncLocalStorage();
8
+ const COMMIT_ORDER = ['keyValueStore', 'requestQueue', 'dataset'];
9
+ /**
10
+ * A storage transaction scoped to a request's lifecycle. Writes made through the storage frontends
11
+ * ({@link Dataset}, {@link KeyValueStore}, {@link RequestQueue}) while the transaction is active
12
+ * are recorded rather than applied; on {@link StorageTransaction.commit|`commit()`} they are replayed
13
+ * into real storage, on {@link StorageTransaction.rollback|`rollback()`} they are dropped. Reads consult
14
+ * the recorded writes first, so a handler sees its own writes.
15
+ *
16
+ * Create one with {@link createStorageTransaction} (explicit commit/rollback) or
17
+ * {@link withStorageTransaction} (scoped sugar). Crawlers open one automatically around every request
18
+ * handler unless `transactionalStorage: false` is set.
19
+ */
20
+ export class StorageTransaction {
21
+ /**
22
+ * The ordered, append-only journal — the source of truth for commit, introspection and reads.
23
+ * @internal
24
+ */
25
+ journal = [];
26
+ #policy;
27
+ #commitTimeoutMillis;
28
+ #state = 'open';
29
+ #disposed = false;
30
+ /** Callbacks registered via `afterCommit()`, run in registration order once a commit has been attempted. */
31
+ #afterCommitCallbacks = [];
32
+ /** @internal */
33
+ constructor(options = {}) {
34
+ this.#policy = { ...DEFAULT_STORAGE_WRITE_POLICY, ...options.policy };
35
+ this.#commitTimeoutMillis = options.commitTimeoutMillis ?? DEFAULT_COMMIT_TIMEOUT_MILLIS;
36
+ }
37
+ /**
38
+ * Per-storage-type write policy.
39
+ * @internal
40
+ */
41
+ get policy() {
42
+ return this.#policy;
43
+ }
44
+ get state() {
45
+ return this.#state;
46
+ }
47
+ /**
48
+ * `true` only while `state === 'open'`. This is the single predicate every storage operation
49
+ * consults — operations performed after the transaction is closed pass through to the real backend.
50
+ */
51
+ get isActive() {
52
+ return this.#state === 'open';
53
+ }
54
+ /** Runs `callback` with this transaction installed in the async context. */
55
+ async run(callback) {
56
+ return transactionStorage.run(this, async () => callback());
57
+ }
58
+ /**
59
+ * Records a write operation in the journal.
60
+ * @internal
61
+ */
62
+ recordJournalEntry(entry) {
63
+ if (!this.isActive) {
64
+ throw new Error(`Cannot record a journal entry on a transaction in the '${this.#state}' state`);
65
+ }
66
+ this.journal.push(entry);
67
+ }
68
+ /**
69
+ * Registers a callback to run once a commit has been attempted, whether it succeeded (`error` is
70
+ * `undefined`) or failed - the only point at which a deferred write can be reacted to where it was
71
+ * made. Callbacks run in registration order; the first to throw propagates, the rest do not run, and
72
+ * on a failed commit its error replaces the commit error. Not run on rollback: nothing was written.
73
+ */
74
+ afterCommit(callback) {
75
+ if (!this.isActive) {
76
+ throw new Error(`Cannot register a commit callback on a transaction in the '${this.#state}' state`);
77
+ }
78
+ this.#afterCommitCallbacks.push(callback);
79
+ }
80
+ /**
81
+ * Replays the journaled writes into real storage. A no-op unless the transaction is `open`.
82
+ *
83
+ * The transaction transitions to `committing` *before* anything is flushed, so a commit that throws
84
+ * partway lands in `failed` (never back in `open`) and subsequent storage operations pass through
85
+ * rather than recording into a dead transaction. Delivery is at-least-once — a commit that fails
86
+ * partway may have applied some of the writes already.
87
+ */
88
+ async commit() {
89
+ if (this.#state !== 'open') {
90
+ return;
91
+ }
92
+ this.#state = 'committing';
93
+ try {
94
+ // The replay re-drives the frontend write path, which checks for cancellation (`tryCancel`)
95
+ // on every operation - and `@apify/timeout` shares one `AbortController` across nested
96
+ // frames, so a request-handler timeout that already fired would abort the commit of a
97
+ // handler that succeeded. Hence a fresh timeout context, which also provides the time bound.
98
+ await timeoutStorage.exit(async () => addTimeoutToPromise(async () => this.#flush(), this.#commitTimeoutMillis, `Committing the storage transaction timed out after ${this.#commitTimeoutMillis / 1000} seconds.`));
99
+ }
100
+ catch (error) {
101
+ this.#state = 'failed';
102
+ // Callbacks are handed an `Error`, so a backend that rejects with anything else gets wrapped.
103
+ const commitError = error instanceof Error ? error : new Error(String(error), { cause: error });
104
+ // A callback that throws here propagates instead: it was handed the failure and decided what
105
+ // the request should fail with.
106
+ await this.#runAfterCommitCallbacks(commitError);
107
+ throw commitError;
108
+ }
109
+ this.#state = 'committed';
110
+ try {
111
+ await this.#runAfterCommitCallbacks();
112
+ }
113
+ catch (error) {
114
+ throw error instanceof NonRetryableError ? error : new AfterCommitError(error);
115
+ }
116
+ }
117
+ /** Fresh timeout context for the same reason as the flush. */
118
+ async #runAfterCommitCallbacks(error) {
119
+ await timeoutStorage.exit(async () => {
120
+ for (const callback of this.#afterCommitCallbacks) {
121
+ await callback(error);
122
+ }
123
+ });
124
+ }
125
+ async #flush() {
126
+ // Each participating frontend replays all of its buffered entries in one call. Frontends are
127
+ // ordered by storage type: key-value stores and request queues first (idempotent under retry),
128
+ // datasets last (not idempotent), minimizing the blast radius of a partial commit failure.
129
+ const groups = new Map();
130
+ for (const entry of this.journal) {
131
+ if (entry.type === 'requestQueue' && entry.writeThrough)
132
+ continue;
133
+ const group = groups.get(entry.participant);
134
+ if (group)
135
+ group.push(entry);
136
+ else
137
+ groups.set(entry.participant, [entry]);
138
+ }
139
+ // A participant only records entries of its own storage type, so the first entry determines
140
+ // the group's place in the commit order.
141
+ const orderedGroups = [...groups.values()].sort((a, b) => COMMIT_ORDER.indexOf(a[0].type) - COMMIT_ORDER.indexOf(b[0].type));
142
+ for (const entries of orderedGroups) {
143
+ await entries[0].participant.commitJournalEntries(entries);
144
+ }
145
+ }
146
+ /**
147
+ * Discards the journaled writes. A no-op unless the transaction is `open` — in particular, calling it
148
+ * after a successful `commit()` (which the crawler's error handling can legitimately do) does nothing
149
+ * and never throws.
150
+ */
151
+ rollback() {
152
+ if (this.#state !== 'open') {
153
+ return;
154
+ }
155
+ this.#state = 'rolledBack';
156
+ }
157
+ /**
158
+ * Releases the journal, the write-time snapshots it holds and any registered commit callbacks. Must
159
+ * be called for *every* terminal state, `failed` included. Idempotent, never throws, and does not
160
+ * change `state`. Any {@link StorageTransactionView} of this transaction is only valid until this
161
+ * is called.
162
+ */
163
+ dispose() {
164
+ if (this.#disposed) {
165
+ return;
166
+ }
167
+ if (this.#state === 'open') {
168
+ // Disposing an open transaction is an internal invariant violation - roll back first.
169
+ try {
170
+ serviceLocator
171
+ .getLogger()
172
+ .warning('Internal error: a storage transaction was disposed while still open; rolling it back.');
173
+ }
174
+ catch {
175
+ // Never throw from dispose.
176
+ }
177
+ this.rollback();
178
+ }
179
+ this.#disposed = true;
180
+ this.journal.length = 0;
181
+ this.#afterCommitCallbacks.length = 0;
182
+ }
183
+ get datasetItems() {
184
+ return this.journal.flatMap((entry) => entry.type === 'dataset' ? entry.items.map((item) => ({ item, datasetId: entry.storageId })) : []);
185
+ }
186
+ get enqueuedUrls() {
187
+ return this.journal.flatMap((entry) => entry.type === 'requestQueue' ? entry.requests.map(({ url, label }) => ({ url, label })) : []);
188
+ }
189
+ get keyValueStoreChanges() {
190
+ const result = {};
191
+ for (const entry of this.journal) {
192
+ if (entry.type !== 'keyValueStore')
193
+ continue;
194
+ result[entry.storageId] ??= {};
195
+ result[entry.storageId][entry.key] = { changedValue: entry.value, options: entry.options };
196
+ }
197
+ return result;
198
+ }
199
+ }
200
+ /**
201
+ * Opens a {@link StorageTransaction} without running anything yet. The caller owns the outcome:
202
+ * `run()`, then `commit()` or `rollback()`, and always `dispose()` when done. For the common
203
+ * open-run-commit flow, prefer {@link withStorageTransaction}.
204
+ */
205
+ export function createStorageTransaction(options = {}) {
206
+ return new StorageTransaction(options);
207
+ }
208
+ /**
209
+ * Runs `callback` inside a new {@link StorageTransaction}: storage writes made in the callback are
210
+ * committed when it returns and rolled back when it throws. If a transaction is already active in the
211
+ * current async context, it is reused and its outcome is left to its owner (and `options` are ignored)
212
+ * — there are no nested transaction semantics.
213
+ */
214
+ export async function withStorageTransaction(callback, options = {}) {
215
+ const existing = transactionStorage.getStore();
216
+ if (existing?.isActive) {
217
+ return callback(existing);
218
+ }
219
+ const transaction = createStorageTransaction(options);
220
+ try {
221
+ const result = await transaction.run(async () => callback(transaction));
222
+ await transaction.commit();
223
+ return result;
224
+ }
225
+ catch (error) {
226
+ transaction.rollback();
227
+ throw error;
228
+ }
229
+ finally {
230
+ transaction.dispose();
231
+ }
232
+ }
233
+ /**
234
+ * Runs `callback` outside of any storage transaction — the per-call-site escape hatch. Storage operations
235
+ * made inside it hit the real backend directly, are not rolled back, and operations that a transaction
236
+ * rejects (`drop`, stream-valued `setValue`, request queue internals, ...) are permitted.
237
+ */
238
+ export async function withDirectStorageAccess(callback) {
239
+ return transactionStorage.exit(async () => callback());
240
+ }
241
+ /**
242
+ * The per-operation hook consulted by every storage frontend method: performs the cancellation check
243
+ * that aborts storage operations when the request handler times out, and returns the active storage
244
+ * transaction. Returns `undefined` when there is no transaction in the async context *or* when it is no
245
+ * longer open — operations on a closed transaction deliberately pass through to the real backend.
246
+ * @internal
247
+ */
248
+ export function activeStorageTransaction() {
249
+ tryCancel();
250
+ const transaction = transactionStorage.getStore();
251
+ return transaction?.isActive ? transaction : undefined;
252
+ }
253
+ /**
254
+ * Returns the transaction installed in the current async context, regardless of its state. Used by the
255
+ * crawler to drive the outcome of the transaction it opened.
256
+ * @internal
257
+ */
258
+ export function currentStorageTransaction() {
259
+ return transactionStorage.getStore();
260
+ }
261
+ /**
262
+ * Captures a value at write time, so that later mutations of the caller's object affect neither the
263
+ * read-your-own-writes reads nor the commit replay. `structuredClone` for fidelity (`Date`, `Map`, `Set`,
264
+ * typed arrays, `undefined`); values it cannot handle fall back to the JSON round-trip the storage
265
+ * backends perform anyway.
266
+ * @internal
267
+ */
268
+ export function snapshotValue(value) {
269
+ try {
270
+ return structuredClone(value);
271
+ }
272
+ catch {
273
+ return JSON.parse(JSON.stringify(value));
274
+ }
275
+ }
276
+ /**
277
+ * The guard for operations that cannot be performed inside a storage transaction: throws when one is
278
+ * active, and performs the per-operation cancellation check either way.
279
+ * @internal
280
+ */
281
+ export function rejectOperationInTransaction(operation, reason = 'it cannot be rolled back.') {
282
+ if (activeStorageTransaction() === undefined) {
283
+ return;
284
+ }
285
+ throw operationRejectedInTransaction(operation, reason);
286
+ }
287
+ /**
288
+ * Builds the "operation not allowed in a transaction" error, for a call site that has already
289
+ * established a transaction is active and so wants to `throw` unconditionally.
290
+ * @internal
291
+ */
292
+ export function operationRejectedInTransaction(operation, reason = 'it cannot be rolled back.') {
293
+ return new Error(`${operation} cannot be used inside a storage transaction: ${reason} ` +
294
+ 'If you really need it, wrap the call in withDirectStorageAccess(() => ...) - operations ' +
295
+ 'performed there are applied immediately and are not rolled back.');
296
+ }
@@ -1,4 +1,5 @@
1
- import type { BaseHttpClient, Dictionary, StorageBackend } from '@crawlee/types';
1
+ import type { BaseHttpClient } from '@crawlee/http-client';
2
+ import type { Dictionary, StorageBackend } from '@crawlee/types';
2
3
  import { Configuration } from '../configuration.js';
3
4
  import type { IProxyConfiguration } from '../proxy_configuration.js';
4
5
  /**
@@ -14,7 +15,8 @@ interface PurgeDefaultStorageOptions {
14
15
  }
15
16
  /**
16
17
  * Cleans up the local storage folder (defaults to `./storage`) created when running code locally.
17
- * Purging will remove all the files in all storages except for INPUT.json in the default KV store.
18
+ * Purging empties the storages that belong to a single run — the default one and every alias-keyed one —
19
+ * Named storages persist across runs and are not touched.
18
20
  *
19
21
  * Purging of storages is happening automatically when we run our crawler (or when we open some storage
20
22
  * explicitly, e.g. via `RequestList.open()`). We can disable that via `purgeOnStart` {@link Configuration}
@@ -28,7 +30,8 @@ interface PurgeDefaultStorageOptions {
28
30
  export declare function purgeDefaultStorages(options?: PurgeDefaultStorageOptions): Promise<void>;
29
31
  /**
30
32
  * Cleans up the local storage folder (defaults to `./storage`) created when running code locally.
31
- * Purging will remove all the files in all storages except for INPUT.json in the default KV store.
33
+ * Purging empties the storages that belong to a single run — the default one and every alias-keyed one —
34
+ * Named storages persist across runs and are not touched.
32
35
  *
33
36
  * Purging of storages is happening automatically when we run our crawler (or when we open some storage
34
37
  * explicitly, e.g. via `RequestList.open()`). We can disable that via `purgeOnStart` {@link Configuration}
package/storages/utils.js CHANGED
@@ -12,11 +12,20 @@ export async function purgeDefaultStorages(configurationOrOptions, storageBacken
12
12
  const { configuration = serviceLocator.getConfiguration(), onlyPurgeOnce = false } = options;
13
13
  ({ storageBackend = serviceLocator.getStorageBackend() } = options);
14
14
  const casted = storageBackend;
15
+ const runPurge = async () => {
16
+ try {
17
+ await casted.purge?.();
18
+ }
19
+ catch (e) {
20
+ casted.__purged = undefined;
21
+ throw e;
22
+ }
23
+ };
15
24
  // if `onlyPurgeOnce` is true, will purge anytime this function is called, otherwise - only on start
16
25
  if (!onlyPurgeOnce || (configuration.purgeOnStart && !casted.__purged)) {
17
- casted.__purged = true;
18
- await casted.purge?.();
26
+ casted.__purged = runPurge();
19
27
  }
28
+ await casted.__purged;
20
29
  }
21
30
  /**
22
31
  * Easily create and manage state values. All state values are automatically persisted.
@@ -50,31 +50,31 @@ export async function isContainerized() {
50
50
  export function isLambda() {
51
51
  return !!process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE;
52
52
  }
53
- let _cgroupsVersion;
53
+ let cgroupsVersion;
54
54
  /**
55
55
  * gets the cgroup version by checking for a file at /sys/fs/cgroup/memory
56
56
  * @returns "V1" or "V2" for the version of cgroup or null if cgroup is not found.
57
57
  */
58
58
  export async function getCgroupsVersion(forceReset) {
59
59
  // Parameter forceReset is just internal for unit tests.
60
- if (_cgroupsVersion !== undefined && !forceReset) {
61
- return _cgroupsVersion;
60
+ if (cgroupsVersion !== undefined && !forceReset) {
61
+ return cgroupsVersion;
62
62
  }
63
63
  try {
64
64
  // If this directory does not exists, cgroups are not available
65
65
  await fs.access('/sys/fs/cgroup/');
66
66
  }
67
67
  catch (e) {
68
- _cgroupsVersion = null;
68
+ cgroupsVersion = null;
69
69
  return null;
70
70
  }
71
- _cgroupsVersion = 'V1';
71
+ cgroupsVersion = 'V1';
72
72
  try {
73
73
  // If this directory does not exists, assume the container is using cgroups V2
74
74
  await fs.access('/sys/fs/cgroup/memory/');
75
75
  }
76
76
  catch (e) {
77
- _cgroupsVersion = 'V2';
77
+ cgroupsVersion = 'V2';
78
78
  }
79
- return _cgroupsVersion;
79
+ return cgroupsVersion;
80
80
  }
package/url.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The canonical form of a hostname: lower-case, punycode, and without the optional root dot.
3
+ *
4
+ * Pass both sides of a hostname comparison through this, so that a domain written as `háčky.cz` still matches the
5
+ * `xn--hky-ela4t.cz` that `URL` reports.
6
+ *
7
+ * @internal
8
+ */
9
+ export declare function normalizeHostname(hostname: string): string;
package/url.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The canonical form of a hostname: lower-case, punycode, and without the optional root dot.
3
+ *
4
+ * Pass both sides of a hostname comparison through this, so that a domain written as `háčky.cz` still matches the
5
+ * `xn--hky-ela4t.cz` that `URL` reports.
6
+ *
7
+ * @internal
8
+ */
9
+ export function normalizeHostname(hostname) {
10
+ return hostname.toLowerCase().replace(/\.$/, '');
11
+ }
package/validators.d.ts CHANGED
@@ -1,28 +1,26 @@
1
- import type { Dictionary } from '@crawlee/types';
1
+ export { ArgumentValidationError } from '@crawlee/utils';
2
+ export { parseArgument } from '@crawlee/utils/internal';
3
+ export { schemas } from '@crawlee/utils/internal';
2
4
  /** @internal */
3
5
  export declare const validators: {
4
- browserPage: (value: Dictionary) => {
5
- validator: boolean;
6
- message: (label: string) => string;
7
- };
8
- proxyConfiguration: (value: Dictionary) => {
9
- validator: boolean;
10
- message: (label: string) => string;
11
- };
12
- requestList: (value: Dictionary) => {
13
- validator: boolean;
14
- message: (label: string) => string;
15
- };
16
- requestQueue: (value: Dictionary) => {
17
- validator: boolean;
18
- message: (label: string) => string;
19
- };
20
- browserPool: (value: Dictionary) => {
21
- validator: boolean;
22
- message: (label: string) => string;
23
- };
24
- sessionPool: (value: Dictionary) => {
25
- validator: boolean;
26
- message: (label: string) => string;
27
- };
6
+ // @ts-ignore optional peer dependency or compatibility with es2022
7
+ browserPage: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
8
+ // @ts-ignore optional peer dependency or compatibility with es2022
9
+ proxyConfiguration: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
10
+ // @ts-ignore optional peer dependency or compatibility with es2022
11
+ requestList: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
12
+ // @ts-ignore optional peer dependency or compatibility with es2022
13
+ requestQueue: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
14
+ // @ts-ignore optional peer dependency or compatibility with es2022
15
+ browserPool: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
16
+ // @ts-ignore optional peer dependency or compatibility with es2022
17
+ sessionPool: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
18
+ // @ts-ignore optional peer dependency or compatibility with es2022
19
+ requestManager: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
20
+ // @ts-ignore optional peer dependency or compatibility with es2022
21
+ storageBackend: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
22
+ // @ts-ignore optional peer dependency or compatibility with es2022
23
+ logger: import("zod").ZodType<import("@crawlee/types").Dictionary, unknown, import("zod/v4/core").$ZodTypeInternals<import("@crawlee/types").Dictionary, unknown>>;
24
+ // @ts-ignore optional peer dependency or compatibility with es2022
25
+ httpClient: import("zod").ZodInstanceOf<import("@crawlee/http-client").BaseHttpClient>;
28
26
  };
package/validators.js CHANGED
@@ -1,29 +1,18 @@
1
- import ow from 'ow';
1
+ import { schemas } from '@crawlee/utils/internal';
2
+ export { ArgumentValidationError } from '@crawlee/utils';
3
+ export { parseArgument } from '@crawlee/utils/internal';
4
+ export { schemas } from '@crawlee/utils/internal';
2
5
  /** @internal */
3
6
  export const validators = {
4
7
  // Naming it browser page for future proofing with Playwright
5
- browserPage: (value) => ({
6
- validator: ow.isValid(value, ow.object.hasKeys('goto', 'evaluate', '$', 'on')),
7
- message: (label) => `Expected argument '${label}' to be a Puppeteer Page, got something else.`,
8
- }),
9
- proxyConfiguration: (value) => ({
10
- validator: ow.isValid(value, ow.object.hasKeys('newProxyInfo')),
11
- message: (label) => `Expected argument '${label}' to implement the IProxyConfiguration interface (missing 'newProxyInfo'), got something else.`,
12
- }),
13
- requestList: (value) => ({
14
- validator: ow.isValid(value, ow.object.hasKeys('fetchNextRequest', 'persistState')),
15
- message: (label) => `Expected argument '${label}' to be a RequestList, got something else.`,
16
- }),
17
- requestQueue: (value) => ({
18
- validator: ow.isValid(value, ow.object.hasKeys('fetchNextRequest', 'addRequest')),
19
- message: (label) => `Expected argument '${label}' to be a RequestQueue, got something else.`,
20
- }),
21
- browserPool: (value) => ({
22
- validator: ow.isValid(value, ow.object.hasKeys('newPage', 'closePage', 'extractPageState', 'injectPageState')),
23
- message: (label) => `Expected argument '${label}' to implement the IBrowserPool interface (missing one of 'newPage', 'closePage', 'extractPageState', 'injectPageState'), got something else.`,
24
- }),
25
- sessionPool: (value) => ({
26
- validator: ow.isValid(value, ow.object.hasKeys('getSession')),
27
- message: (label) => `Expected argument '${label}' to implement the ISessionPool interface (missing 'getSession'), got something else.`,
28
- }),
8
+ browserPage: schemas.objectWithKeys(['goto', 'evaluate', '$', 'on'], 'Expected a Puppeteer Page, got something else.'),
9
+ proxyConfiguration: schemas.objectWithKeys(['newProxyInfo'], "Expected an object implementing the IProxyConfiguration interface (missing 'newProxyInfo'), got something else."),
10
+ requestList: schemas.objectWithKeys(['fetchNextRequest', 'checkReadiness'], 'Expected a RequestList, got something else.'),
11
+ requestQueue: schemas.objectWithKeys(['fetchNextRequest', 'addRequest'], 'Expected a RequestQueue, got something else.'),
12
+ browserPool: schemas.objectWithKeys(['newPage', 'closePage', 'extractPageState', 'injectPageState'], "Expected an object implementing the IBrowserPool interface (missing one of 'newPage', 'closePage', 'extractPageState', 'injectPageState'), got something else."),
13
+ sessionPool: schemas.objectWithKeys(['getSession'], "Expected an object implementing the ISessionPool interface (missing 'getSession'), got something else."),
14
+ requestManager: schemas.objectWithKeys(['fetchNextRequest', 'addRequest', 'addRequestsBatched'], "Expected an object implementing the IRequestManager interface (missing one of 'fetchNextRequest', 'addRequest', 'addRequestsBatched'), got something else."),
15
+ storageBackend: schemas.objectWithKeys(['createDatasetBackend', 'createKeyValueStoreBackend', 'createRequestQueueBackend'], "Expected an object implementing the StorageBackend interface (missing one of 'createDatasetBackend', 'createKeyValueStoreBackend', 'createRequestQueueBackend'), got something else."),
16
+ logger: schemas.logger,
17
+ httpClient: schemas.httpClient,
29
18
  };