@crawlee/utils
Version:
A set of shared utilities that can be used by crawlers
174 lines • 5.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isIterable = isIterable;
exports.isAsyncIterable = isAsyncIterable;
exports.asyncifyIterable = asyncifyIterable;
exports.chunkedAsyncIterable = chunkedAsyncIterable;
exports.peekableAsyncIterable = peekableAsyncIterable;
const node_util_1 = require("node:util");
/**
* Type guard that checks if a value is iterable (has Symbol.iterator).
* @internal
*
* **Example usage:**
* ```ts
* if (isIterable(someValue)) {
* for (const item of someValue) {
* console.log(item);
* }
* }
* ```
*/
function isIterable(value) {
if (value == null || typeof value === 'string' || ArrayBuffer.isView(value)) {
return false;
}
if (Array.isArray(value)) {
return true;
}
return typeof Object(value)[Symbol.iterator] === 'function';
}
/**
* Type guard that checks if a value is async iterable (has Symbol.asyncIterator).
* @internal
*
* **Example usage:**
* ```ts
* if (isAsyncIterable(someValue)) {
* for await (const item of someValue) {
* console.log(item);
* }
* }
* ```
*/
function isAsyncIterable(value) {
if (value == null || typeof value === 'string' || ArrayBuffer.isView(value)) {
return false;
}
return typeof Object(value)[Symbol.asyncIterator] === 'function';
}
/**
* Converts any iterable or async iterable to an async iterable.
* @internal
*
* @yields Each item from the input iterable
*
* **Example usage:**
* ```ts
* const syncArray = [1, 2, 3];
* for await (const item of asyncifyIterable(syncArray)) {
* console.log(item); // 1, 2, 3
* }
* ```
*/
async function* asyncifyIterable(iterable) {
yield* iterable;
}
/**
* Lazily splits the input async iterable into chunks of specified size.
* The last chunk may contain fewer items if the total number of items
* is not evenly divisible by the chunk size.
* @internal
*
* @yields Arrays of items, each containing up to chunkSize items
*
* **Example usage:**
* ```ts
* const numbers = async function* () {
* for (let i = 1; i <= 10; i++) yield i;
* };
*
* for await (const chunk of chunkedAsyncIterable(numbers(), 3)) {
* console.log(chunk); // [1, 2, 3], [4, 5, 6], [7, 8, 9], [10]
* }
* ```
*/
async function* chunkedAsyncIterable(iterable, chunkSize) {
if (typeof chunkSize !== 'number' || chunkSize < 1) {
throw new Error(`Chunk size must be a positive number (${(0, node_util_1.inspect)(chunkSize)}) received`);
}
let chunk = [];
for await (const item of iterable) {
chunk.push(item);
if (chunk.length >= chunkSize) {
yield chunk;
chunk = [];
}
}
if (chunk.length) {
yield chunk;
}
}
/**
* Wraps an async iterable to provide peek functionality, allowing you to look at
* the next value without consuming it from the iterator.
* @internal
*
* @param iterable - The async iterable to make peekable
*
* **Example usage:**
* ```ts
* const numbers = async function* () {
* yield 1; yield 2; yield 3;
* };
*
* const peekable = peekableAsyncIterable(numbers());
* const iterator = peekable[Symbol.asyncIterator]();
*
* console.log(await iterator.peek()); // 1 (doesn't consume)
* console.log(await iterator.peek()); // 1 (still doesn't consume)
* console.log(await iterator.next()); // { value: 1, done: false } (now consumed)
* console.log(await iterator.peek()); // 2 (next value)
* ```
*/
function peekableAsyncIterable(iterable) {
const iterator = asyncifyIterable(iterable)[Symbol.asyncIterator]();
let peekedValue;
let isExhausted = false;
const peekableIterator = {
async next() {
// If we have peeked a value, return it and clear the peek
if (peekedValue !== undefined) {
const result = peekedValue;
peekedValue = undefined;
if (result.done) {
isExhausted = true;
return { done: true, value: undefined };
}
return { done: false, value: result.value };
}
if (isExhausted) {
return { done: true, value: undefined };
}
const result = await iterator.next();
if (result.done) {
isExhausted = true;
}
return result;
},
async peek() {
if (peekedValue !== undefined) {
return peekedValue.done ? undefined : peekedValue.value;
}
if (isExhausted) {
return undefined;
}
const result = await iterator.next();
peekedValue = { done: result.done ?? false, value: result.value };
if (result.done) {
isExhausted = true;
return undefined;
}
return result.value;
},
[Symbol.asyncIterator]() {
return this;
},
};
return {
[Symbol.asyncIterator]() {
return peekableIterator;
},
};
}
//# sourceMappingURL=iterables.js.map