@crawlee/core 4.0.0-rc.0 → 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.
- package/README.md +1 -1
- package/configuration.d.ts +15 -46
- package/configuration.js +8 -20
- package/errors.d.ts +12 -53
- package/errors.js +13 -66
- package/events/index.d.ts +1 -0
- package/events/local_event_manager.d.ts +0 -7
- package/events/local_event_manager.js +8 -8
- package/events/system_info.d.ts +38 -0
- package/index.d.ts +2 -8
- package/index.js +4 -8
- package/internal.d.ts +8 -0
- package/internal.js +9 -0
- package/log.d.ts +10 -11
- package/log.js +53 -25
- package/memory-storage/memory-storage.d.ts +12 -7
- package/memory-storage/memory-storage.js +45 -17
- package/memory-storage/resource-clients/dataset.d.ts +0 -5
- package/memory-storage/resource-clients/dataset.js +17 -20
- package/memory-storage/resource-clients/key-value-store.d.ts +0 -9
- package/memory-storage/resource-clients/key-value-store.js +11 -33
- package/memory-storage/resource-clients/request-queue.d.ts +0 -22
- package/memory-storage/resource-clients/request-queue.js +57 -53
- package/package.json +16 -18
- package/proxy_configuration.d.ts +20 -23
- package/proxy_configuration.js +18 -12
- package/recoverable_state.d.ts +28 -6
- package/recoverable_state.js +51 -14
- package/request.d.ts +18 -104
- package/request.js +41 -220
- package/serialization.js +3 -3
- package/service_locator.d.ts +3 -0
- package/service_locator.js +2 -0
- package/storages/dataset.d.ts +9 -15
- package/storages/dataset.js +22 -14
- package/storages/index.d.ts +3 -4
- package/storages/index.js +1 -4
- package/storages/key_value_store.d.ts +12 -46
- package/storages/key_value_store.js +31 -47
- package/storages/key_value_store_codec.js +6 -11
- package/storages/request_dedup_cache.d.ts +0 -2
- package/storages/request_dedup_cache.js +8 -8
- package/storages/request_list.d.ts +6 -82
- package/storages/request_list.js +175 -179
- package/storages/request_loader.d.ts +48 -22
- package/storages/request_loader.js +36 -1
- package/storages/request_manager.d.ts +86 -0
- package/storages/request_manager_tandem.d.ts +13 -28
- package/storages/request_manager_tandem.js +46 -43
- package/storages/request_queue.d.ts +19 -49
- package/storages/request_queue.js +92 -88
- package/storages/storage_instance_manager.d.ts +1 -2
- package/storages/storage_instance_manager.js +4 -4
- package/storages/transaction.d.ts +27 -9
- package/storages/transaction.js +56 -11
- package/storages/utils.d.ts +2 -2
- package/validators.d.ts +3 -2
- package/validators.js +3 -2
- package/autoscaling/autoscaled_pool.d.ts +0 -195
- package/autoscaling/autoscaled_pool.js +0 -386
- package/autoscaling/concurrency_system.d.ts +0 -268
- package/autoscaling/concurrency_system.js +0 -362
- package/autoscaling/cpu_load_signal.d.ts +0 -43
- package/autoscaling/cpu_load_signal.js +0 -47
- package/autoscaling/event_loop_load_signal.d.ts +0 -51
- package/autoscaling/event_loop_load_signal.js +0 -60
- package/autoscaling/index.d.ts +0 -9
- package/autoscaling/index.js +0 -9
- package/autoscaling/load_signal.d.ts +0 -100
- package/autoscaling/load_signal.js +0 -105
- package/autoscaling/memory_load_signal.d.ts +0 -47
- package/autoscaling/memory_load_signal.js +0 -106
- package/autoscaling/snapshotter.d.ts +0 -84
- package/autoscaling/snapshotter.js +0 -67
- package/autoscaling/storage_backend_load_signal.d.ts +0 -56
- package/autoscaling/storage_backend_load_signal.js +0 -73
- package/autoscaling/system_status.d.ts +0 -159
- package/autoscaling/system_status.js +0 -139
- package/autoscaling/weighted_avg.d.ts +0 -5
- package/autoscaling/weighted_avg.js +0 -14
- package/cookie_utils.d.ts +0 -44
- package/cookie_utils.js +0 -122
- package/crawlers/context_pipeline.d.ts +0 -70
- package/crawlers/context_pipeline.js +0 -122
- package/crawlers/crawler_commons.d.ts +0 -159
- package/crawlers/error_snapshotter.d.ts +0 -57
- package/crawlers/error_snapshotter.js +0 -117
- package/crawlers/error_tracker.d.ts +0 -54
- package/crawlers/error_tracker.js +0 -308
- package/crawlers/index.d.ts +0 -5
- package/crawlers/index.js +0 -4
- package/crawlers/internals/types.d.ts +0 -7
- package/crawlers/internals/types.js +0 -1
- package/crawlers/statistics.d.ts +0 -328
- package/crawlers/statistics.js +0 -536
- package/enqueue_links/enqueue_links.d.ts +0 -156
- package/enqueue_links/enqueue_links.js +0 -78
- package/enqueue_links/index.d.ts +0 -2
- package/enqueue_links/index.js +0 -2
- package/enqueue_links/shared.d.ts +0 -93
- package/enqueue_links/shared.js +0 -239
- package/http.d.ts +0 -9
- package/http.js +0 -28
- package/router.d.ts +0 -306
- package/router.js +0 -309
- package/session_pool/consts.d.ts +0 -3
- package/session_pool/consts.js +0 -3
- package/session_pool/errors.d.ts +0 -7
- package/session_pool/errors.js +0 -11
- package/session_pool/fingerprint.d.ts +0 -9
- package/session_pool/fingerprint.js +0 -30
- package/session_pool/index.d.ts +0 -4
- package/session_pool/index.js +0 -4
- package/session_pool/session.d.ts +0 -150
- package/session_pool/session.js +0 -220
- package/session_pool/session_pool.d.ts +0 -240
- package/session_pool/session_pool.js +0 -394
- package/storages/sitemap_request_loader.d.ts +0 -201
- package/storages/sitemap_request_loader.js +0 -438
- package/storages/throttling_request_manager.d.ts +0 -239
- package/storages/throttling_request_manager.js +0 -646
- /package/{crawlers/crawler_commons.js → events/system_info.js} +0 -0
package/serialization.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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 StreamArray from 'stream-json/streamers/
|
|
4
|
+
import StreamArray from 'stream-json/streamers/stream-array.js';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { parseArgument, schemas } from './validators.js';
|
|
7
7
|
const pipeline = util.promisify(streamPipeline);
|
|
@@ -76,7 +76,7 @@ export async function serializeArray(data) {
|
|
|
76
76
|
export async function deserializeArray(compressedData) {
|
|
77
77
|
parseArgument(compressedData, uint8ArraySchema);
|
|
78
78
|
const { chunks, collector } = createChunkCollector({ fromValuesStream: true });
|
|
79
|
-
await pipeline(Readable.from([compressedData]), zlib.createGunzip(), StreamArray.
|
|
79
|
+
await pipeline(Readable.from([compressedData]), zlib.createGunzip(), StreamArray.withParserAsStream(), collector);
|
|
80
80
|
return chunks;
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
@@ -90,7 +90,7 @@ export async function deserializeArray(compressedData) {
|
|
|
90
90
|
*/
|
|
91
91
|
export function createDeserialize(compressedData) {
|
|
92
92
|
parseArgument(compressedData, uint8ArraySchema);
|
|
93
|
-
const streamArray = StreamArray.
|
|
93
|
+
const streamArray = StreamArray.withParserAsStream();
|
|
94
94
|
const destination = pluckValue(streamArray);
|
|
95
95
|
streamPipeline(Readable.from([compressedData]), zlib.createGunzip(), destination, (err) => destination.emit(err));
|
|
96
96
|
return destination;
|
package/service_locator.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ interface ServiceLocatorInterface {
|
|
|
60
60
|
getChildLog(prefix: string): CrawleeLogger;
|
|
61
61
|
/**
|
|
62
62
|
* Get the storage instance manager (shared across all storage types).
|
|
63
|
+
* @internal
|
|
63
64
|
*/
|
|
64
65
|
getStorageInstanceManager(): StorageInstanceManager;
|
|
65
66
|
/**
|
|
@@ -138,7 +139,9 @@ export declare class ServiceLocator implements ServiceLocatorInterface {
|
|
|
138
139
|
getLogger(): CrawleeLogger;
|
|
139
140
|
setLogger(logger: CrawleeLogger): void;
|
|
140
141
|
getChildLog(prefix: string): CrawleeLogger;
|
|
142
|
+
/** @internal */
|
|
141
143
|
getStorageInstanceManager(): StorageInstanceManager;
|
|
144
|
+
/** @internal */
|
|
142
145
|
reset(): void;
|
|
143
146
|
}
|
|
144
147
|
/**
|
package/service_locator.js
CHANGED
|
@@ -161,12 +161,14 @@ export class ServiceLocator {
|
|
|
161
161
|
getChildLog(prefix) {
|
|
162
162
|
return this.getLogger().child({ prefix });
|
|
163
163
|
}
|
|
164
|
+
/** @internal */
|
|
164
165
|
getStorageInstanceManager() {
|
|
165
166
|
if (!ServiceLocator.#storageInstanceManager) {
|
|
166
167
|
ServiceLocator.#storageInstanceManager = new StorageInstanceManager();
|
|
167
168
|
}
|
|
168
169
|
return ServiceLocator.#storageInstanceManager;
|
|
169
170
|
}
|
|
171
|
+
/** @internal */
|
|
170
172
|
reset() {
|
|
171
173
|
this.#configuration = undefined;
|
|
172
174
|
this.#eventManager = undefined;
|
package/storages/dataset.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Awaitable, DatasetBackend, DatasetInfo, Dictionary } from '@crawlee/types';
|
|
2
2
|
import { Configuration } from '../configuration.js';
|
|
3
|
-
import type { CrawleeLogger } from '../log.js';
|
|
4
3
|
import type { JournalEntry } from './transaction.js';
|
|
5
4
|
import type { DatasetStats } from './storage_stats.js';
|
|
6
5
|
import type { StorageOpenOptions } from './utils.js';
|
|
@@ -142,10 +141,9 @@ export interface DatasetExportToOptions extends DatasetExportOptions {
|
|
|
142
141
|
export declare class Dataset<Data extends Dictionary = Dictionary> {
|
|
143
142
|
#private;
|
|
144
143
|
readonly configuration: Configuration;
|
|
145
|
-
id: string;
|
|
146
|
-
name?: string;
|
|
147
|
-
backend
|
|
148
|
-
log: CrawleeLogger;
|
|
144
|
+
readonly id: string;
|
|
145
|
+
readonly name?: string;
|
|
146
|
+
private readonly backend;
|
|
149
147
|
/**
|
|
150
148
|
* @internal
|
|
151
149
|
*/
|
|
@@ -171,14 +169,6 @@ export declare class Dataset<Data extends Dictionary = Dictionary> {
|
|
|
171
169
|
* Returns {@link DatasetContent} object holding the items in the dataset based on the provided parameters.
|
|
172
170
|
*/
|
|
173
171
|
getData(options?: DatasetDataOptions): Promise<DatasetContent<Data>>;
|
|
174
|
-
/**
|
|
175
|
-
* The single transaction-aware page read all dataset read paths go through — both `getData()` and
|
|
176
|
-
* the private `fetchPages()`. Returns the real page concatenated with the current transaction's
|
|
177
|
-
* buffered items, with `offset` / `limit` / `desc` windowing applied across the concatenation.
|
|
178
|
-
*/
|
|
179
|
-
private readPage;
|
|
180
|
-
/** The active transaction's buffered writes to this dataset, derived from its journal. */
|
|
181
|
-
private bufferedJournalEntries;
|
|
182
172
|
/** @internal */
|
|
183
173
|
commitJournalEntries(entries: JournalEntry[]): Promise<void>;
|
|
184
174
|
/**
|
|
@@ -323,8 +313,6 @@ export declare class Dataset<Data extends Dictionary = Dictionary> {
|
|
|
323
313
|
* @param [options] An object containing extra options for `reduce()`
|
|
324
314
|
*/
|
|
325
315
|
reduce<T>(iteratee: DatasetReducer<T, Data>, memo: T, options?: DatasetIteratorOptions): Promise<T>;
|
|
326
|
-
private fetchEntryPages;
|
|
327
|
-
private fetchPages;
|
|
328
316
|
/**
|
|
329
317
|
* Returns dataset items.
|
|
330
318
|
*
|
|
@@ -391,6 +379,11 @@ export declare class Dataset<Data extends Dictionary = Dictionary> {
|
|
|
391
379
|
* depending on the mode of operation.
|
|
392
380
|
*/
|
|
393
381
|
drop(): Promise<void>;
|
|
382
|
+
/**
|
|
383
|
+
* Removes all items from the dataset but keeps the dataset itself, along with its
|
|
384
|
+
* {@link Dataset.id|`id`} and {@link Dataset.name|`name`}.
|
|
385
|
+
*/
|
|
386
|
+
purge(): Promise<void>;
|
|
394
387
|
/**
|
|
395
388
|
* Opens a dataset and returns a promise resolving to an instance of the {@link Dataset} class.
|
|
396
389
|
*
|
|
@@ -469,6 +462,7 @@ export interface DatasetReducer<T, Data> {
|
|
|
469
462
|
*/
|
|
470
463
|
(memo: T, item: Data, index: number): Awaitable<T>;
|
|
471
464
|
}
|
|
465
|
+
/** @internal */
|
|
472
466
|
export interface DatasetOptions {
|
|
473
467
|
/** Resolved metadata for the dataset, as returned by the backend's `getMetadata()`. */
|
|
474
468
|
metadata: DatasetInfo;
|
package/storages/dataset.js
CHANGED
|
@@ -91,8 +91,8 @@ export class Dataset {
|
|
|
91
91
|
configuration;
|
|
92
92
|
id;
|
|
93
93
|
name;
|
|
94
|
+
// oxlint-disable-next-line crawlee/prefer-private-fields -- tests spy on the backend directly
|
|
94
95
|
backend;
|
|
95
|
-
log;
|
|
96
96
|
#statsTracker = new StorageStatsTracker({
|
|
97
97
|
readCount: 0,
|
|
98
98
|
writeCount: 0,
|
|
@@ -105,7 +105,6 @@ export class Dataset {
|
|
|
105
105
|
this.id = options.metadata.id;
|
|
106
106
|
this.name = options.metadata.name;
|
|
107
107
|
this.backend = options.backend;
|
|
108
|
-
this.log = serviceLocator.getLogger().child({ prefix: 'Dataset' });
|
|
109
108
|
}
|
|
110
109
|
/**
|
|
111
110
|
* Backend-independent usage counters tracked for this dataset (read / write operations issued to
|
|
@@ -126,6 +125,7 @@ export class Dataset {
|
|
|
126
125
|
* The objects must be serializable to JSON.
|
|
127
126
|
*/
|
|
128
127
|
async pushData(data) {
|
|
128
|
+
tryCancel();
|
|
129
129
|
const transaction = activeStorageTransaction();
|
|
130
130
|
parseArgument(data, schemas.anyObject);
|
|
131
131
|
// Normalize to array and validate each item
|
|
@@ -152,7 +152,7 @@ export class Dataset {
|
|
|
152
152
|
*/
|
|
153
153
|
async getData(options = {}) {
|
|
154
154
|
try {
|
|
155
|
-
return await this
|
|
155
|
+
return await this.#readPage(options);
|
|
156
156
|
}
|
|
157
157
|
catch (e) {
|
|
158
158
|
const error = e;
|
|
@@ -167,8 +167,8 @@ export class Dataset {
|
|
|
167
167
|
* the private `fetchPages()`. Returns the real page concatenated with the current transaction's
|
|
168
168
|
* buffered items, with `offset` / `limit` / `desc` windowing applied across the concatenation.
|
|
169
169
|
*/
|
|
170
|
-
async readPage(options) {
|
|
171
|
-
const buffered = this
|
|
170
|
+
async #readPage(options) {
|
|
171
|
+
const buffered = this.#bufferedJournalEntries()?.flatMap((entry) => entry.items);
|
|
172
172
|
// Every branch below hits the backend exactly once.
|
|
173
173
|
this.#statsTracker.add('readCount');
|
|
174
174
|
if (!buffered?.length) {
|
|
@@ -224,7 +224,7 @@ export class Dataset {
|
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
226
|
/** The active transaction's buffered writes to this dataset, derived from its journal. */
|
|
227
|
-
bufferedJournalEntries() {
|
|
227
|
+
#bufferedJournalEntries() {
|
|
228
228
|
const transaction = activeStorageTransaction();
|
|
229
229
|
return transaction?.journal.filter((entry) => entry.type === 'dataset' && entry.participant === this);
|
|
230
230
|
}
|
|
@@ -250,7 +250,7 @@ export class Dataset {
|
|
|
250
250
|
async export(options = {}) {
|
|
251
251
|
tryCancel();
|
|
252
252
|
const items = [];
|
|
253
|
-
for await (const page of this
|
|
253
|
+
for await (const page of this.#fetchPages(options)) {
|
|
254
254
|
items.push(...page.items);
|
|
255
255
|
}
|
|
256
256
|
return items;
|
|
@@ -348,7 +348,7 @@ export class Dataset {
|
|
|
348
348
|
* @throws If the underlying storage no longer exists (e.g. it was deleted externally).
|
|
349
349
|
*/
|
|
350
350
|
async getInfo() {
|
|
351
|
-
const buffered = this
|
|
351
|
+
const buffered = this.#bufferedJournalEntries();
|
|
352
352
|
const metadata = await this.backend.getMetadata();
|
|
353
353
|
if (buffered?.length) {
|
|
354
354
|
const lastWriteAt = buffered[buffered.length - 1].recordedAt;
|
|
@@ -432,16 +432,16 @@ export class Dataset {
|
|
|
432
432
|
await this.forEach(wrappedFunc, options);
|
|
433
433
|
return currentMemo;
|
|
434
434
|
}
|
|
435
|
-
async
|
|
435
|
+
async *#fetchEntryPages(options) {
|
|
436
436
|
let index = options.offset ?? 0;
|
|
437
|
-
for await (const page of this
|
|
437
|
+
for await (const page of this.#fetchPages(options)) {
|
|
438
438
|
yield {
|
|
439
439
|
...page,
|
|
440
440
|
items: page.items.map((item) => [index++, item]),
|
|
441
441
|
};
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
|
-
async
|
|
444
|
+
async *#fetchPages(options, pageSize = DATASET_ITERATORS_DEFAULT_LIMIT) {
|
|
445
445
|
let offset = options.offset ?? 0;
|
|
446
446
|
const totalLimit = options.limit;
|
|
447
447
|
let yielded = 0;
|
|
@@ -449,7 +449,7 @@ export class Dataset {
|
|
|
449
449
|
const fetchLimit = totalLimit !== undefined ? Math.min(pageSize, totalLimit - yielded) : pageSize;
|
|
450
450
|
if (fetchLimit <= 0)
|
|
451
451
|
break;
|
|
452
|
-
const page = await this
|
|
452
|
+
const page = await this.#readPage({ ...options, offset, limit: fetchLimit });
|
|
453
453
|
yield page;
|
|
454
454
|
yielded += page.items.length;
|
|
455
455
|
if (page.items.length < fetchLimit || offset + page.items.length >= page.total)
|
|
@@ -483,7 +483,7 @@ export class Dataset {
|
|
|
483
483
|
values(options = {}) {
|
|
484
484
|
tryCancel();
|
|
485
485
|
return createDualIterable({
|
|
486
|
-
createPages: () => this
|
|
486
|
+
createPages: () => this.#fetchPages(options),
|
|
487
487
|
extractItems: (page) => page.items,
|
|
488
488
|
});
|
|
489
489
|
}
|
|
@@ -513,7 +513,7 @@ export class Dataset {
|
|
|
513
513
|
entries(options = {}) {
|
|
514
514
|
tryCancel();
|
|
515
515
|
return createDualIterable({
|
|
516
|
-
createPages: () => this
|
|
516
|
+
createPages: () => this.#fetchEntryPages(options),
|
|
517
517
|
extractItems: (page) => page.items,
|
|
518
518
|
});
|
|
519
519
|
}
|
|
@@ -541,6 +541,14 @@ export class Dataset {
|
|
|
541
541
|
await this.backend.drop();
|
|
542
542
|
serviceLocator.getStorageInstanceManager().removeFromCache(this);
|
|
543
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* Removes all items from the dataset but keeps the dataset itself, along with its
|
|
546
|
+
* {@link Dataset.id|`id`} and {@link Dataset.name|`name`}.
|
|
547
|
+
*/
|
|
548
|
+
async purge() {
|
|
549
|
+
rejectOperationInTransaction('Dataset.purge()');
|
|
550
|
+
await this.backend.purge();
|
|
551
|
+
}
|
|
544
552
|
/**
|
|
545
553
|
* Opens a dataset and returns a promise resolving to an instance of the {@link Dataset} class.
|
|
546
554
|
*
|
package/storages/index.d.ts
CHANGED
|
@@ -5,10 +5,9 @@ export * from './request_list.js';
|
|
|
5
5
|
export type * from './request_loader.js';
|
|
6
6
|
export type * from './request_manager.js';
|
|
7
7
|
export * from './request_queue.js';
|
|
8
|
-
export
|
|
9
|
-
export
|
|
8
|
+
export type { DefaultStorageIdentifier, ExplicitStorageIdentifier, IStorage, StorageIdentifier, } from './storage_instance_manager.js';
|
|
9
|
+
export { StorageInstanceManager } from './storage_instance_manager.js';
|
|
10
|
+
export type { DatasetStats, KeyValueStoreStats, RequestQueueStats } from './storage_stats.js';
|
|
10
11
|
export * from './utils.js';
|
|
11
12
|
export * from './transaction.js';
|
|
12
|
-
export * from './sitemap_request_loader.js';
|
|
13
13
|
export * from './request_manager_tandem.js';
|
|
14
|
-
export * from './throttling_request_manager.js';
|
package/storages/index.js
CHANGED
|
@@ -3,10 +3,7 @@ export * from './key_value_store.js';
|
|
|
3
3
|
export * from './key_value_store_codec.js';
|
|
4
4
|
export * from './request_list.js';
|
|
5
5
|
export * from './request_queue.js';
|
|
6
|
-
export
|
|
7
|
-
export * from './storage_stats.js';
|
|
6
|
+
export { StorageInstanceManager } from './storage_instance_manager.js';
|
|
8
7
|
export * from './utils.js';
|
|
9
8
|
export * from './transaction.js';
|
|
10
|
-
export * from './sitemap_request_loader.js';
|
|
11
9
|
export * from './request_manager_tandem.js';
|
|
12
|
-
export * from './throttling_request_manager.js';
|
|
@@ -20,8 +20,6 @@ import type { StorageIdentifier } from './storage_instance_manager.js';
|
|
|
20
20
|
* To access the default key-value store directly, you can use the
|
|
21
21
|
* {@link KeyValueStore.getValue} and {@link KeyValueStore.setValue} convenience functions.
|
|
22
22
|
*
|
|
23
|
-
* To access the input, you can also use the {@link KeyValueStore.getInput} convenience function.
|
|
24
|
-
*
|
|
25
23
|
* `KeyValueStore` stores its data on a local disk.
|
|
26
24
|
*
|
|
27
25
|
* If the `CRAWLEE_STORAGE_DIR` environment variable is set, the data is stored in
|
|
@@ -29,15 +27,14 @@ import type { StorageIdentifier } from './storage_instance_manager.js';
|
|
|
29
27
|
* ```
|
|
30
28
|
* {CRAWLEE_STORAGE_DIR}/key_value_stores/{STORE_ID}/{INDEX}.{EXT}
|
|
31
29
|
* ```
|
|
32
|
-
* Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default
|
|
33
|
-
* unless you override it by setting the `CRAWLEE_DEFAULT_KEY_VALUE_STORE_ID` environment variable.
|
|
30
|
+
* Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default`.
|
|
34
31
|
* The `{KEY}` is the key of the record and `{EXT}` corresponds to the MIME content type of the data value.
|
|
35
32
|
*
|
|
36
33
|
* **Example usage:**
|
|
37
34
|
*
|
|
38
35
|
* ```javascript
|
|
39
36
|
* // Get crawler input from the default key-value store.
|
|
40
|
-
* const input = await KeyValueStore.
|
|
37
|
+
* const input = await KeyValueStore.getValue('INPUT');
|
|
41
38
|
* // Get some value from the default key-value store.
|
|
42
39
|
* const otherValue = await KeyValueStore.getValue('my-key');
|
|
43
40
|
*
|
|
@@ -65,7 +62,7 @@ export declare class KeyValueStore {
|
|
|
65
62
|
readonly configuration: Configuration;
|
|
66
63
|
readonly id: string;
|
|
67
64
|
readonly name?: string;
|
|
68
|
-
|
|
65
|
+
readonly backend: KeyValueStoreBackend;
|
|
69
66
|
/**
|
|
70
67
|
* @internal
|
|
71
68
|
*/
|
|
@@ -139,21 +136,6 @@ export declare class KeyValueStore {
|
|
|
139
136
|
* on the MIME content type of the record, or the default value if the key is missing from the store.
|
|
140
137
|
*/
|
|
141
138
|
getValue<T = unknown>(key: string, defaultValue: T): Promise<T>;
|
|
142
|
-
/**
|
|
143
|
-
* The active transaction's last buffered write per key for this store, derived from its journal.
|
|
144
|
-
* An entry with a `null` value is a tombstone (an in-transaction deletion).
|
|
145
|
-
*/
|
|
146
|
-
private bufferedJournalEntries;
|
|
147
|
-
/**
|
|
148
|
-
* The single transaction-aware record read shared by `getValue`, `getRecord`, `recordExists` and the
|
|
149
|
-
* listing paths: buffered key → serialized through the standard codec (same fidelity as a real
|
|
150
|
-
* round-trip); tombstoned key → `null`; otherwise the backend.
|
|
151
|
-
*
|
|
152
|
-
* The per-key buffered lookup requires the whole journal to be reduced to a last-write-per-key map,
|
|
153
|
-
* which is O(journal). Single-record callers let it default (rebuilt per call); the listing paths,
|
|
154
|
-
* which read many keys, pass a map built once so the read stays O(1) per key instead of O(journal).
|
|
155
|
-
*/
|
|
156
|
-
private readRecord;
|
|
157
139
|
/**
|
|
158
140
|
* Reads a record from the key-value store without parsing the value.
|
|
159
141
|
*
|
|
@@ -189,9 +171,6 @@ export declare class KeyValueStore {
|
|
|
189
171
|
*/
|
|
190
172
|
recordExists(key: string): Promise<boolean>;
|
|
191
173
|
getAutoSavedValue<T extends Dictionary = Dictionary>(key: string, defaultValue?: T): Promise<T>;
|
|
192
|
-
private ensurePersistStateEvent;
|
|
193
|
-
private fetchKeyValuePages;
|
|
194
|
-
private fetchKeyPages;
|
|
195
174
|
/**
|
|
196
175
|
* Saves or deletes a record in the key-value store.
|
|
197
176
|
* The function returns a promise that resolves once the record has been saved or deleted.
|
|
@@ -243,6 +222,11 @@ export declare class KeyValueStore {
|
|
|
243
222
|
* depending on the mode of operation.
|
|
244
223
|
*/
|
|
245
224
|
drop(): Promise<void>;
|
|
225
|
+
/**
|
|
226
|
+
* Removes all records from the store but keeps the store itself, along with its
|
|
227
|
+
* {@link KeyValueStore.id|`id`} and {@link KeyValueStore.name|`name`}.
|
|
228
|
+
*/
|
|
229
|
+
purge(): Promise<void>;
|
|
246
230
|
/** @internal */
|
|
247
231
|
clearCache(): void;
|
|
248
232
|
/**
|
|
@@ -355,7 +339,9 @@ export declare class KeyValueStore {
|
|
|
355
339
|
/**
|
|
356
340
|
* Returns a file URL for the given key.
|
|
357
341
|
*
|
|
358
|
-
*
|
|
342
|
+
* The URL is derived from the key, so it is also returned for a record that does not exist (yet) —
|
|
343
|
+
* including one written earlier in an uncommitted storage transaction. Returns `undefined` only when
|
|
344
|
+
* the storage has no file URLs at all (e.g. the in-memory storage).
|
|
359
345
|
*
|
|
360
346
|
* @param key The key of the record to generate the public URL for.
|
|
361
347
|
*/
|
|
@@ -486,27 +472,6 @@ export declare class KeyValueStore {
|
|
|
486
472
|
* @ignore
|
|
487
473
|
*/
|
|
488
474
|
static setValue<T>(key: string, value: T | null, options?: RecordOptions): Promise<void>;
|
|
489
|
-
/**
|
|
490
|
-
* Gets the crawler input value from the default {@link KeyValueStore} associated with the current crawler run.
|
|
491
|
-
*
|
|
492
|
-
* The input is read from the default {@link KeyValueStore} under the configured input key
|
|
493
|
-
* (`CRAWLEE_INPUT_KEY`, default `INPUT`).
|
|
494
|
-
*
|
|
495
|
-
* Note that the `getInput()` function does not cache the value read from the key-value store.
|
|
496
|
-
* If you need to use the input multiple times in your crawler,
|
|
497
|
-
* it is far more efficient to read it once and store it locally.
|
|
498
|
-
*
|
|
499
|
-
* For more information, see {@link KeyValueStore.open}
|
|
500
|
-
* and {@link KeyValueStore.getValue}.
|
|
501
|
-
*
|
|
502
|
-
* @returns
|
|
503
|
-
* Returns a promise that resolves to an object, string
|
|
504
|
-
* or [`Buffer`](https://nodejs.org/api/buffer.html), depending
|
|
505
|
-
* on the MIME content type of the record, or `null`
|
|
506
|
-
* if the record is missing.
|
|
507
|
-
* @ignore
|
|
508
|
-
*/
|
|
509
|
-
static getInput<T = Dictionary | string | Buffer>(): Promise<T | null>;
|
|
510
475
|
}
|
|
511
476
|
/**
|
|
512
477
|
* User-function used in the {@link KeyValueStore.forEachKey} method.
|
|
@@ -522,6 +487,7 @@ export interface KeyConsumer {
|
|
|
522
487
|
size: number;
|
|
523
488
|
}): Awaitable<void>;
|
|
524
489
|
}
|
|
490
|
+
/** @internal */
|
|
525
491
|
export interface KeyValueStoreOptions {
|
|
526
492
|
/** Resolved metadata for the key-value store, as returned by the backend's `getMetadata()`. */
|
|
527
493
|
metadata: KeyValueStoreInfo;
|
|
@@ -42,8 +42,6 @@ const openOptionsSchema = z.strictObject({
|
|
|
42
42
|
* To access the default key-value store directly, you can use the
|
|
43
43
|
* {@link KeyValueStore.getValue} and {@link KeyValueStore.setValue} convenience functions.
|
|
44
44
|
*
|
|
45
|
-
* To access the input, you can also use the {@link KeyValueStore.getInput} convenience function.
|
|
46
|
-
*
|
|
47
45
|
* `KeyValueStore` stores its data on a local disk.
|
|
48
46
|
*
|
|
49
47
|
* If the `CRAWLEE_STORAGE_DIR` environment variable is set, the data is stored in
|
|
@@ -51,15 +49,14 @@ const openOptionsSchema = z.strictObject({
|
|
|
51
49
|
* ```
|
|
52
50
|
* {CRAWLEE_STORAGE_DIR}/key_value_stores/{STORE_ID}/{INDEX}.{EXT}
|
|
53
51
|
* ```
|
|
54
|
-
* Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default
|
|
55
|
-
* unless you override it by setting the `CRAWLEE_DEFAULT_KEY_VALUE_STORE_ID` environment variable.
|
|
52
|
+
* Note that `{STORE_ID}` is the name or ID of the key-value store. The default key-value store has ID: `default`.
|
|
56
53
|
* The `{KEY}` is the key of the record and `{EXT}` corresponds to the MIME content type of the data value.
|
|
57
54
|
*
|
|
58
55
|
* **Example usage:**
|
|
59
56
|
*
|
|
60
57
|
* ```javascript
|
|
61
58
|
* // Get crawler input from the default key-value store.
|
|
62
|
-
* const input = await KeyValueStore.
|
|
59
|
+
* const input = await KeyValueStore.getValue('INPUT');
|
|
63
60
|
* // Get some value from the default key-value store.
|
|
64
61
|
* const otherValue = await KeyValueStore.getValue('my-key');
|
|
65
62
|
*
|
|
@@ -86,7 +83,6 @@ export class KeyValueStore {
|
|
|
86
83
|
configuration;
|
|
87
84
|
id;
|
|
88
85
|
name;
|
|
89
|
-
// kept as TS-private: key_value_store tests spy on the backend directly
|
|
90
86
|
backend;
|
|
91
87
|
#persistStateEventStarted = false;
|
|
92
88
|
/** Cache for persistent (auto-saved) values. When we try to set such value, the cache will be updated automatically. */
|
|
@@ -148,7 +144,7 @@ export class KeyValueStore {
|
|
|
148
144
|
async getValue(key, defaultValue) {
|
|
149
145
|
tryCancel();
|
|
150
146
|
parseArgument(key, keySchema);
|
|
151
|
-
const record = await this
|
|
147
|
+
const record = await this.#readRecord(key);
|
|
152
148
|
// A missing record falls back to the default; a record that parses to a falsy value (including
|
|
153
149
|
// a stored literal `null`) is returned verbatim, so callers can tell "stored null" from "absent".
|
|
154
150
|
if (!record) {
|
|
@@ -161,7 +157,7 @@ export class KeyValueStore {
|
|
|
161
157
|
* The active transaction's last buffered write per key for this store, derived from its journal.
|
|
162
158
|
* An entry with a `null` value is a tombstone (an in-transaction deletion).
|
|
163
159
|
*/
|
|
164
|
-
bufferedJournalEntries() {
|
|
160
|
+
#bufferedJournalEntries() {
|
|
165
161
|
const transaction = activeStorageTransaction();
|
|
166
162
|
if (!transaction)
|
|
167
163
|
return undefined;
|
|
@@ -182,7 +178,7 @@ export class KeyValueStore {
|
|
|
182
178
|
* which is O(journal). Single-record callers let it default (rebuilt per call); the listing paths,
|
|
183
179
|
* which read many keys, pass a map built once so the read stays O(1) per key instead of O(journal).
|
|
184
180
|
*/
|
|
185
|
-
async readRecord(key, buffered = this
|
|
181
|
+
async #readRecord(key, buffered = this.#bufferedJournalEntries()) {
|
|
186
182
|
const entry = buffered?.get(key);
|
|
187
183
|
if (entry) {
|
|
188
184
|
if (entry.value === null) {
|
|
@@ -232,7 +228,7 @@ export class KeyValueStore {
|
|
|
232
228
|
async getRecord(key) {
|
|
233
229
|
tryCancel();
|
|
234
230
|
parseArgument(key, keySchema);
|
|
235
|
-
return this
|
|
231
|
+
return this.#readRecord(key);
|
|
236
232
|
}
|
|
237
233
|
/**
|
|
238
234
|
* Tests whether a record with the given key exists in the key-value store without retrieving its value.
|
|
@@ -243,7 +239,7 @@ export class KeyValueStore {
|
|
|
243
239
|
async recordExists(key) {
|
|
244
240
|
tryCancel();
|
|
245
241
|
parseArgument(key, keySchema);
|
|
246
|
-
const entry = this
|
|
242
|
+
const entry = this.#bufferedJournalEntries()?.get(key);
|
|
247
243
|
if (entry) {
|
|
248
244
|
return entry.value !== null;
|
|
249
245
|
}
|
|
@@ -265,10 +261,10 @@ export class KeyValueStore {
|
|
|
265
261
|
return this.#cache.get(key);
|
|
266
262
|
}
|
|
267
263
|
this.#cache.set(key, value);
|
|
268
|
-
this
|
|
264
|
+
this.#ensurePersistStateEvent();
|
|
269
265
|
return value;
|
|
270
266
|
}
|
|
271
|
-
ensurePersistStateEvent() {
|
|
267
|
+
#ensurePersistStateEvent() {
|
|
272
268
|
if (this.#persistStateEventStarted) {
|
|
273
269
|
return;
|
|
274
270
|
}
|
|
@@ -281,15 +277,15 @@ export class KeyValueStore {
|
|
|
281
277
|
});
|
|
282
278
|
this.#persistStateEventStarted = true;
|
|
283
279
|
}
|
|
284
|
-
async
|
|
280
|
+
async *#fetchKeyValuePages(options, mapRecord) {
|
|
285
281
|
// Reduce the journal once for the whole iteration, not once per key inside `readRecord`.
|
|
286
|
-
const buffered = this
|
|
287
|
-
for await (const page of this
|
|
282
|
+
const buffered = this.#bufferedJournalEntries();
|
|
283
|
+
for await (const page of this.#fetchKeyPages(options, buffered)) {
|
|
288
284
|
const results = [];
|
|
289
285
|
for (const item of page) {
|
|
290
286
|
// The shared transaction-aware read, so a key that exists only in the transaction resolves
|
|
291
287
|
// here instead of being dropped (`values()` would disagree with `keys()` on length).
|
|
292
|
-
const record = await this
|
|
288
|
+
const record = await this.#readRecord(item.key, buffered);
|
|
293
289
|
if (record) {
|
|
294
290
|
const parsed = parseValue(record.value, record.contentType ?? null);
|
|
295
291
|
results.push(mapRecord(item.key, parsed));
|
|
@@ -298,7 +294,7 @@ export class KeyValueStore {
|
|
|
298
294
|
yield results;
|
|
299
295
|
}
|
|
300
296
|
}
|
|
301
|
-
async
|
|
297
|
+
async *#fetchKeyPages(options, buffered = this.#bufferedJournalEntries(), limit = KVS_KEYS_DEFAULT_LIMIT) {
|
|
302
298
|
// Buffered keys are emitted first, then the real pages with any buffered (or tombstoned) key
|
|
303
299
|
// skipped - a merge-join is not an option, since `listKeys` promises no sort order.
|
|
304
300
|
const shadowedKeys = new Set();
|
|
@@ -460,6 +456,16 @@ export class KeyValueStore {
|
|
|
460
456
|
await this.backend.drop();
|
|
461
457
|
serviceLocator.getStorageInstanceManager().removeFromCache(this);
|
|
462
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Removes all records from the store but keeps the store itself, along with its
|
|
461
|
+
* {@link KeyValueStore.id|`id`} and {@link KeyValueStore.name|`name`}.
|
|
462
|
+
*/
|
|
463
|
+
async purge() {
|
|
464
|
+
rejectOperationInTransaction('KeyValueStore.purge()');
|
|
465
|
+
await this.backend.purge();
|
|
466
|
+
// The auto-saved values this cache holds are no longer in the store.
|
|
467
|
+
this.#cache.clear();
|
|
468
|
+
}
|
|
463
469
|
/** @internal */
|
|
464
470
|
clearCache() {
|
|
465
471
|
rejectOperationInTransaction('KeyValueStore.clearCache()');
|
|
@@ -491,7 +497,7 @@ export class KeyValueStore {
|
|
|
491
497
|
parseArgument(iteratee, schemas.anyFunction);
|
|
492
498
|
const parsedOptions = parseArgument(options, iteratorOptionsSchema);
|
|
493
499
|
let index = 0;
|
|
494
|
-
for await (const page of this
|
|
500
|
+
for await (const page of this.#fetchKeyPages(parsedOptions)) {
|
|
495
501
|
for (const item of page) {
|
|
496
502
|
await iteratee(item.key, index++, { size: item.size });
|
|
497
503
|
}
|
|
@@ -523,7 +529,7 @@ export class KeyValueStore {
|
|
|
523
529
|
keys(options = {}) {
|
|
524
530
|
tryCancel();
|
|
525
531
|
return createDualIterable({
|
|
526
|
-
createPages: () => this
|
|
532
|
+
createPages: () => this.#fetchKeyPages(options),
|
|
527
533
|
extractItems: (page) => page.map((item) => item.key),
|
|
528
534
|
});
|
|
529
535
|
}
|
|
@@ -553,7 +559,7 @@ export class KeyValueStore {
|
|
|
553
559
|
values(options = {}) {
|
|
554
560
|
tryCancel();
|
|
555
561
|
return createDualIterable({
|
|
556
|
-
createPages: () => this
|
|
562
|
+
createPages: () => this.#fetchKeyValuePages(options, (_key, value) => value),
|
|
557
563
|
extractItems: (page) => page,
|
|
558
564
|
});
|
|
559
565
|
}
|
|
@@ -583,7 +589,7 @@ export class KeyValueStore {
|
|
|
583
589
|
entries(options = {}) {
|
|
584
590
|
tryCancel();
|
|
585
591
|
return createDualIterable({
|
|
586
|
-
createPages: () => this
|
|
592
|
+
createPages: () => this.#fetchKeyValuePages(options, (key, value) => [key, value]),
|
|
587
593
|
extractItems: (page) => page,
|
|
588
594
|
});
|
|
589
595
|
}
|
|
@@ -605,7 +611,9 @@ export class KeyValueStore {
|
|
|
605
611
|
/**
|
|
606
612
|
* Returns a file URL for the given key.
|
|
607
613
|
*
|
|
608
|
-
*
|
|
614
|
+
* The URL is derived from the key, so it is also returned for a record that does not exist (yet) —
|
|
615
|
+
* including one written earlier in an uncommitted storage transaction. Returns `undefined` only when
|
|
616
|
+
* the storage has no file URLs at all (e.g. the in-memory storage).
|
|
609
617
|
*
|
|
610
618
|
* @param key The key of the record to generate the public URL for.
|
|
611
619
|
*/
|
|
@@ -737,30 +745,6 @@ export class KeyValueStore {
|
|
|
737
745
|
const store = await this.open();
|
|
738
746
|
return store.setValue(key, value, options);
|
|
739
747
|
}
|
|
740
|
-
/**
|
|
741
|
-
* Gets the crawler input value from the default {@link KeyValueStore} associated with the current crawler run.
|
|
742
|
-
*
|
|
743
|
-
* The input is read from the default {@link KeyValueStore} under the configured input key
|
|
744
|
-
* (`CRAWLEE_INPUT_KEY`, default `INPUT`).
|
|
745
|
-
*
|
|
746
|
-
* Note that the `getInput()` function does not cache the value read from the key-value store.
|
|
747
|
-
* If you need to use the input multiple times in your crawler,
|
|
748
|
-
* it is far more efficient to read it once and store it locally.
|
|
749
|
-
*
|
|
750
|
-
* For more information, see {@link KeyValueStore.open}
|
|
751
|
-
* and {@link KeyValueStore.getValue}.
|
|
752
|
-
*
|
|
753
|
-
* @returns
|
|
754
|
-
* Returns a promise that resolves to an object, string
|
|
755
|
-
* or [`Buffer`](https://nodejs.org/api/buffer.html), depending
|
|
756
|
-
* on the MIME content type of the record, or `null`
|
|
757
|
-
* if the record is missing.
|
|
758
|
-
* @ignore
|
|
759
|
-
*/
|
|
760
|
-
static async getInput() {
|
|
761
|
-
const store = await this.open();
|
|
762
|
-
return store.getValue(store.configuration.inputKey);
|
|
763
|
-
}
|
|
764
748
|
}
|
|
765
749
|
/** Normalizes a codec-serialized value into the `Buffer | ArrayBuffer` shape raw record reads promise. */
|
|
766
750
|
function normalizeSerializedValue(value) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isTypeValid, parse as parseContentType } from 'content-type';
|
|
2
2
|
import JSON5 from 'json5';
|
|
3
3
|
import { jsonStringifyExtended } from '@apify/utilities';
|
|
4
4
|
import { isBuffer, isStream } from '../byte_utils.js';
|
|
@@ -67,17 +67,12 @@ export function parseValue(body, contentTypeHeader) {
|
|
|
67
67
|
// No content type at all → we have no basis for interpretation; hand back the raw value.
|
|
68
68
|
if (contentTypeHeader === null)
|
|
69
69
|
return body;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const result = contentTypeParser.parse(contentTypeHeader);
|
|
74
|
-
contentType = result.type;
|
|
75
|
-
charset = result.parameters.charset;
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// Unparseable header → keep the original value rather than a mangled string.
|
|
70
|
+
const result = parseContentType(contentTypeHeader);
|
|
71
|
+
// Unparseable header → keep the original value rather than a mangled string.
|
|
72
|
+
if (!isTypeValid(result.type))
|
|
79
73
|
return body;
|
|
80
|
-
|
|
74
|
+
const contentType = result.type;
|
|
75
|
+
const charset = result.parameters.charset;
|
|
81
76
|
// If we can't successfully interpret it, we return the original value rather than mangling it.
|
|
82
77
|
if (!areDataStringifiable(contentType, charset))
|
|
83
78
|
return body;
|
|
@@ -13,10 +13,8 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export declare class RequestDeduplicationCache {
|
|
15
15
|
#private;
|
|
16
|
-
private readonly size;
|
|
17
16
|
constructor(size?: number);
|
|
18
17
|
get(cacheKey: string): string | null;
|
|
19
18
|
add(cacheKey: string, requestId: string): void;
|
|
20
19
|
clear(): void;
|
|
21
|
-
private indexOf;
|
|
22
20
|
}
|