@crawlee/core 4.0.0-beta.93 → 4.0.0-beta.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cookie_utils.js CHANGED
@@ -103,8 +103,10 @@ export function mergeCookies(url, sourceCookies) {
103
103
  if (!cookieString)
104
104
  continue;
105
105
  const cookie = Cookie.parse(cookieString);
106
- if (!cookie)
107
- throw new CookieParseError(cookieString);
106
+ if (!cookie) {
107
+ serviceLocator.getLogger().warning(`Skipping malformed cookie fragment: '${cookieString}'`);
108
+ continue;
109
+ }
108
110
  const similarKeyCookie = jar.getCookiesSync(url).find((c) => {
109
111
  return cookie.key !== c.key && cookie.key.toLowerCase() === c.key.toLowerCase();
110
112
  });
package/iterables.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Converts any iterable or async iterable to an async iterable.
3
+ * @internal
4
+ *
5
+ * @yields Each item from the input iterable
6
+ *
7
+ * **Example usage:**
8
+ * ```ts
9
+ * const syncArray = [1, 2, 3];
10
+ * for await (const item of asyncifyIterable(syncArray)) {
11
+ * console.log(item); // 1, 2, 3
12
+ * }
13
+ * ```
14
+ */
15
+ export declare function asyncifyIterable<T>(iterable: Iterable<T> | AsyncIterable<T>): AsyncIterable<T>;
16
+ /**
17
+ * Lazily splits the input async iterable into chunks of specified size.
18
+ * The last chunk may contain fewer items if the total number of items
19
+ * is not evenly divisible by the chunk size.
20
+ * @internal
21
+ *
22
+ * @yields Arrays of items, each containing up to chunkSize items
23
+ *
24
+ * **Example usage:**
25
+ * ```ts
26
+ * const numbers = async function* () {
27
+ * for (let i = 1; i <= 10; i++) yield i;
28
+ * };
29
+ *
30
+ * for await (const chunk of chunkedAsyncIterable(numbers(), 3)) {
31
+ * console.log(chunk); // [1, 2, 3], [4, 5, 6], [7, 8, 9], [10]
32
+ * }
33
+ * ```
34
+ */
35
+ export declare function chunkedAsyncIterable<T>(iterable: AsyncIterable<T> | Iterable<T>, chunkSize: number | (() => number)): AsyncIterable<T[]>;
36
+ /**
37
+ * An async iterator that also supports peeking at the next value without consuming it.
38
+ * Extends both AsyncIterator and AsyncIterable interfaces.
39
+ * @internal
40
+ */
41
+ export interface PeekableAsyncIterator<T> extends AsyncIterator<T>, AsyncIterable<T> {
42
+ /**
43
+ * Peeks at the next value without consuming it from the iterator.
44
+ * Subsequent calls to peek() will return the same value until next() is called.
45
+ *
46
+ * @returns Promise that resolves to the next value, or undefined if the iterator is exhausted
47
+ */
48
+ peek(): Promise<T | undefined>;
49
+ }
50
+ /**
51
+ * An async iterable that yields peekable async iterators.
52
+ * @internal
53
+ */
54
+ export interface PeekableAsyncIterable<T> extends AsyncIterable<T> {
55
+ [Symbol.asyncIterator](): PeekableAsyncIterator<T>;
56
+ }
57
+ /**
58
+ * Wraps an async iterable to provide peek functionality, allowing you to look at
59
+ * the next value without consuming it from the iterator.
60
+ * @internal
61
+ *
62
+ * @param iterable - The async iterable to make peekable
63
+ *
64
+ * **Example usage:**
65
+ * ```ts
66
+ * const numbers = async function* () {
67
+ * yield 1; yield 2; yield 3;
68
+ * };
69
+ *
70
+ * const peekable = peekableAsyncIterable(numbers());
71
+ * const iterator = peekable[Symbol.asyncIterator]();
72
+ *
73
+ * console.log(await iterator.peek()); // 1 (doesn't consume)
74
+ * console.log(await iterator.peek()); // 1 (still doesn't consume)
75
+ * console.log(await iterator.next()); // { value: 1, done: false } (now consumed)
76
+ * console.log(await iterator.peek()); // 2 (next value)
77
+ * ```
78
+ */
79
+ export declare function peekableAsyncIterable<T>(iterable: AsyncIterable<T> | Iterable<T>): PeekableAsyncIterable<T>;
package/iterables.js ADDED
@@ -0,0 +1,134 @@
1
+ import { inspect } from 'node:util';
2
+ /**
3
+ * Converts any iterable or async iterable to an async iterable.
4
+ * @internal
5
+ *
6
+ * @yields Each item from the input iterable
7
+ *
8
+ * **Example usage:**
9
+ * ```ts
10
+ * const syncArray = [1, 2, 3];
11
+ * for await (const item of asyncifyIterable(syncArray)) {
12
+ * console.log(item); // 1, 2, 3
13
+ * }
14
+ * ```
15
+ */
16
+ export async function* asyncifyIterable(iterable) {
17
+ yield* iterable;
18
+ }
19
+ /**
20
+ * Lazily splits the input async iterable into chunks of specified size.
21
+ * The last chunk may contain fewer items if the total number of items
22
+ * is not evenly divisible by the chunk size.
23
+ * @internal
24
+ *
25
+ * @yields Arrays of items, each containing up to chunkSize items
26
+ *
27
+ * **Example usage:**
28
+ * ```ts
29
+ * const numbers = async function* () {
30
+ * for (let i = 1; i <= 10; i++) yield i;
31
+ * };
32
+ *
33
+ * for await (const chunk of chunkedAsyncIterable(numbers(), 3)) {
34
+ * console.log(chunk); // [1, 2, 3], [4, 5, 6], [7, 8, 9], [10]
35
+ * }
36
+ * ```
37
+ */
38
+ export async function* chunkedAsyncIterable(iterable, chunkSize) {
39
+ const getChunkSize = typeof chunkSize === 'function' ? chunkSize : () => chunkSize;
40
+ if (typeof chunkSize === 'number' && chunkSize < 1) {
41
+ throw new Error(`Chunk size must be a positive number (${inspect(chunkSize)}) received`);
42
+ }
43
+ const iterator = Symbol.asyncIterator in iterable
44
+ ? iterable[Symbol.asyncIterator]()
45
+ : iterable[Symbol.iterator]();
46
+ while (true) {
47
+ const currentSize = getChunkSize();
48
+ if (currentSize < 1)
49
+ break;
50
+ const chunk = [];
51
+ for (let i = 0; i < currentSize; i++) {
52
+ const next = await iterator.next();
53
+ if (next.done) {
54
+ break;
55
+ }
56
+ chunk.push(next.value);
57
+ }
58
+ if (chunk.length === 0)
59
+ break;
60
+ yield chunk;
61
+ }
62
+ }
63
+ /**
64
+ * Wraps an async iterable to provide peek functionality, allowing you to look at
65
+ * the next value without consuming it from the iterator.
66
+ * @internal
67
+ *
68
+ * @param iterable - The async iterable to make peekable
69
+ *
70
+ * **Example usage:**
71
+ * ```ts
72
+ * const numbers = async function* () {
73
+ * yield 1; yield 2; yield 3;
74
+ * };
75
+ *
76
+ * const peekable = peekableAsyncIterable(numbers());
77
+ * const iterator = peekable[Symbol.asyncIterator]();
78
+ *
79
+ * console.log(await iterator.peek()); // 1 (doesn't consume)
80
+ * console.log(await iterator.peek()); // 1 (still doesn't consume)
81
+ * console.log(await iterator.next()); // { value: 1, done: false } (now consumed)
82
+ * console.log(await iterator.peek()); // 2 (next value)
83
+ * ```
84
+ */
85
+ export function peekableAsyncIterable(iterable) {
86
+ const iterator = asyncifyIterable(iterable)[Symbol.asyncIterator]();
87
+ let peekedValue;
88
+ let isExhausted = false;
89
+ const peekableIterator = {
90
+ async next() {
91
+ // If we have peeked a value, return it and clear the peek
92
+ if (peekedValue !== undefined) {
93
+ const result = peekedValue;
94
+ peekedValue = undefined;
95
+ if (result.done) {
96
+ isExhausted = true;
97
+ return { done: true, value: undefined };
98
+ }
99
+ return { done: false, value: result.value };
100
+ }
101
+ if (isExhausted) {
102
+ return { done: true, value: undefined };
103
+ }
104
+ const result = await iterator.next();
105
+ if (result.done) {
106
+ isExhausted = true;
107
+ }
108
+ return result;
109
+ },
110
+ async peek() {
111
+ if (peekedValue !== undefined) {
112
+ return peekedValue.done ? undefined : peekedValue.value;
113
+ }
114
+ if (isExhausted) {
115
+ return undefined;
116
+ }
117
+ const result = await iterator.next();
118
+ peekedValue = { done: result.done ?? false, value: result.value };
119
+ if (result.done) {
120
+ isExhausted = true;
121
+ return undefined;
122
+ }
123
+ return result.value;
124
+ },
125
+ [Symbol.asyncIterator]() {
126
+ return this;
127
+ },
128
+ };
129
+ return {
130
+ [Symbol.asyncIterator]() {
131
+ return peekableIterator;
132
+ },
133
+ };
134
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crawlee/core",
3
- "version": "4.0.0-beta.93",
3
+ "version": "4.0.0-beta.95",
4
4
  "description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -53,9 +53,9 @@
53
53
  "@apify/pseudo_url": "^2.0.59",
54
54
  "@apify/timeout": "^0.3.2",
55
55
  "@apify/utilities": "^2.15.5",
56
- "@crawlee/fs-storage": "4.0.0-beta.93",
57
- "@crawlee/types": "4.0.0-beta.93",
58
- "@crawlee/utils": "4.0.0-beta.93",
56
+ "@crawlee/fs-storage": "4.0.0-beta.95",
57
+ "@crawlee/types": "4.0.0-beta.95",
58
+ "@crawlee/utils": "4.0.0-beta.95",
59
59
  "@sapphire/async-queue": "^1.5.5",
60
60
  "@sapphire/shapeshift": "^4.0.0",
61
61
  "@vladfrangu/async_event_emitter": "^2.4.6",
@@ -79,5 +79,5 @@
79
79
  }
80
80
  }
81
81
  },
82
- "gitHead": "b9d21e80d94d01e21d4e1c19191610d0157dd172"
82
+ "gitHead": "ba8602d011706fb5c9930232402156eb5b657eb3"
83
83
  }
@@ -189,9 +189,15 @@ const serviceLocatorStorage = new AsyncLocalStorage();
189
189
  */
190
190
  export function bindMethodsToServiceLocator(serviceLocator, target) {
191
191
  let proto = Object.getPrototypeOf(target);
192
+ const seenKeys = new Set();
192
193
  while (proto !== null && proto !== Object.prototype) {
193
194
  const propertyKeys = [...Object.getOwnPropertyNames(proto), ...Object.getOwnPropertySymbols(proto)];
194
195
  for (const propertyKey of propertyKeys) {
196
+ // The chain is walked derived-first, so the first occurrence of a key is the one dynamic
197
+ // dispatch would pick — a subclass override must not be clobbered by its base version.
198
+ if (seenKeys.has(propertyKey))
199
+ continue;
200
+ seenKeys.add(propertyKey);
195
201
  const descriptor = Object.getOwnPropertyDescriptor(proto, propertyKey);
196
202
  // We use property descriptors rather than accessing target[propertyKey] directly,
197
203
  // because that would trigger getters and cause unwanted side effects.
@@ -1,9 +1,10 @@
1
1
  import { inspect } from 'node:util';
2
- import { chunkedAsyncIterable, downloadListOfUrls, isAsyncIterable, isIterable, peekableAsyncIterable, sleep, } from '@crawlee/utils';
2
+ import { downloadListOfUrls, isAsyncIterable, isIterable, sleep } from '@crawlee/utils';
3
3
  import ow from 'ow';
4
4
  import { LruCache } from '@apify/datastructures';
5
5
  import { Configuration } from '../configuration.js';
6
6
  import { getObjectType } from '../debug.js';
7
+ import { chunkedAsyncIterable, peekableAsyncIterable } from '../iterables.js';
7
8
  import { Request } from '../request.js';
8
9
  import { serviceLocator } from '../service_locator.js';
9
10
  import { checkStorageAccess } from './access_checking.js';