@crawlee/core 4.0.0-beta.134 → 4.0.0-beta.136
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/enqueue_links/shared.d.ts +0 -1
- package/enqueue_links/shared.js +0 -1
- package/index.d.ts +1 -1
- package/index.js +3 -1
- package/memory-storage/memory-storage.d.ts +11 -5
- package/memory-storage/memory-storage.js +45 -15
- package/memory-storage/resource-clients/dataset.js +3 -5
- package/memory-storage/resource-clients/key-value-store.js +2 -4
- package/memory-storage/resource-clients/request-queue.js +5 -7
- package/package.json +6 -6
|
@@ -2,7 +2,6 @@ import type { Awaitable, Dictionary } from '@crawlee/types';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import type { RequestOptions } from '../request.js';
|
|
4
4
|
import type { EnqueueStrategyOption } from './enqueue_links.js';
|
|
5
|
-
export { tryAbsoluteURL } from '@crawlee/utils/internal';
|
|
6
5
|
export interface UrlPatternObject {
|
|
7
6
|
glob?: string;
|
|
8
7
|
regexp?: RegExp;
|
package/enqueue_links/shared.js
CHANGED
|
@@ -2,7 +2,6 @@ import { URL } from 'node:url';
|
|
|
2
2
|
import { Minimatch } from 'minimatch';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { schemas } from '../validators.js';
|
|
5
|
-
export { tryAbsoluteURL } from '@crawlee/utils/internal';
|
|
6
5
|
const MAX_ENQUEUE_LINKS_CACHE_SIZE = 1000;
|
|
7
6
|
/**
|
|
8
7
|
* To keep high performance when the same patterns are passed on every `enqueueLinks()` call,
|
package/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export * from './serialization.js';
|
|
|
15
15
|
export * from './session_pool/index.js';
|
|
16
16
|
export * from './storages/index.js';
|
|
17
17
|
export * from './memory-storage/index.js';
|
|
18
|
-
export
|
|
18
|
+
export { ArgumentValidationError, validators } from './validators.js';
|
|
19
19
|
export * from './cookie_utils.js';
|
|
20
20
|
export * from './http.js';
|
|
21
21
|
export * from './recoverable_state.js';
|
package/index.js
CHANGED
|
@@ -15,7 +15,9 @@ export * from './serialization.js';
|
|
|
15
15
|
export * from './session_pool/index.js';
|
|
16
16
|
export * from './storages/index.js';
|
|
17
17
|
export * from './memory-storage/index.js';
|
|
18
|
-
export
|
|
18
|
+
// Not `export *`: the rest of the module re-exports `@crawlee/utils/internal` symbols, which carry no
|
|
19
|
+
// semver guarantees and must not reach the public surface. Internal consumers import them directly.
|
|
20
|
+
export { ArgumentValidationError, validators } from './validators.js';
|
|
19
21
|
export * from './cookie_utils.js';
|
|
20
22
|
export * from './http.js';
|
|
21
23
|
export * from './recoverable_state.js';
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type * as storage from '@crawlee/types';
|
|
2
2
|
import type { CrawleeLogger } from '@crawlee/types';
|
|
3
|
-
import { DatasetBackend } from './resource-clients/dataset.js';
|
|
4
|
-
import { KeyValueStoreBackend } from './resource-clients/key-value-store.js';
|
|
5
3
|
import { RequestQueueBackend } from './resource-clients/request-queue.js';
|
|
6
4
|
export interface MemoryStorageOptions {
|
|
7
5
|
/**
|
|
@@ -12,15 +10,23 @@ export interface MemoryStorageOptions {
|
|
|
12
10
|
export declare class MemoryStorageBackend implements storage.StorageBackend {
|
|
13
11
|
#private;
|
|
14
12
|
readonly logger?: CrawleeLogger;
|
|
15
|
-
readonly keyValueStoreBackendCache: KeyValueStoreBackend[];
|
|
16
|
-
readonly datasetBackendCache: DatasetBackend[];
|
|
17
|
-
readonly requestQueueBackendCache: RequestQueueBackend[];
|
|
18
13
|
constructor(options?: MemoryStorageOptions);
|
|
19
14
|
/**
|
|
20
15
|
* Return a per-instance unique cache key so that distinct `MemoryStorageBackend` instances get separate
|
|
21
16
|
* cache partitions in the storage backend cache.
|
|
22
17
|
*/
|
|
23
18
|
getStorageBackendCacheKey(): string;
|
|
19
|
+
/**
|
|
20
|
+
* Evict a cached backend so that a dropped storage is no longer resolved by `createXBackend`,
|
|
21
|
+
* reported by `storageExists` or visited by `purge`. Returns whether the backend was cached, which
|
|
22
|
+
* tells the caller whether it still owns in-memory state worth clearing.
|
|
23
|
+
*
|
|
24
|
+
* The resource clients own their own entry's lifetime but must not reach into the caches directly.
|
|
25
|
+
* Because a client is only ever constructed by `createXBackend`, which caches it immediately, the
|
|
26
|
+
* entry matching `id` is always the caller itself.
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
evictBackend(type: 'Dataset' | 'KeyValueStore' | 'RequestQueue', id: string): boolean;
|
|
24
30
|
createDatasetBackend(options?: storage.StorageIdentifier): Promise<storage.DatasetBackend>;
|
|
25
31
|
createKeyValueStoreBackend(options?: storage.StorageIdentifier): Promise<storage.KeyValueStoreBackend>;
|
|
26
32
|
createRequestQueueBackend(options?: storage.StorageIdentifier): Promise<RequestQueueBackend>;
|
|
@@ -11,9 +11,9 @@ export class MemoryStorageBackend {
|
|
|
11
11
|
* cache by storage directory: two distinct `MemoryStorageBackend` instances must not share cached backends.
|
|
12
12
|
*/
|
|
13
13
|
#instanceCacheKey = `MemoryStorageBackend:${randomUUID()}`;
|
|
14
|
-
keyValueStoreBackendCache = [];
|
|
15
|
-
datasetBackendCache = [];
|
|
16
|
-
requestQueueBackendCache = [];
|
|
14
|
+
#keyValueStoreBackendCache = [];
|
|
15
|
+
#datasetBackendCache = [];
|
|
16
|
+
#requestQueueBackendCache = [];
|
|
17
17
|
constructor(options = {}) {
|
|
18
18
|
this.logger = options.logger;
|
|
19
19
|
}
|
|
@@ -24,6 +24,36 @@ export class MemoryStorageBackend {
|
|
|
24
24
|
getStorageBackendCacheKey() {
|
|
25
25
|
return this.#instanceCacheKey;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Evict a cached backend so that a dropped storage is no longer resolved by `createXBackend`,
|
|
29
|
+
* reported by `storageExists` or visited by `purge`. Returns whether the backend was cached, which
|
|
30
|
+
* tells the caller whether it still owns in-memory state worth clearing.
|
|
31
|
+
*
|
|
32
|
+
* The resource clients own their own entry's lifetime but must not reach into the caches directly.
|
|
33
|
+
* Because a client is only ever constructed by `createXBackend`, which caches it immediately, the
|
|
34
|
+
* entry matching `id` is always the caller itself.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
evictBackend(type, id) {
|
|
38
|
+
let cache;
|
|
39
|
+
switch (type) {
|
|
40
|
+
case 'Dataset':
|
|
41
|
+
cache = this.#datasetBackendCache;
|
|
42
|
+
break;
|
|
43
|
+
case 'KeyValueStore':
|
|
44
|
+
cache = this.#keyValueStoreBackendCache;
|
|
45
|
+
break;
|
|
46
|
+
case 'RequestQueue':
|
|
47
|
+
cache = this.#requestQueueBackendCache;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
const index = cache.findIndex((entry) => entry.id === id);
|
|
51
|
+
if (index === -1) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
cache.splice(index, 1);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
27
57
|
static #resolveStorageKey(options) {
|
|
28
58
|
// No identifier at all means the default storage, which is opened under the reserved alias —
|
|
29
59
|
// same rule as `resolveStorageIdentifier` in the storage frontends, so that a backend used
|
|
@@ -37,7 +67,7 @@ export class MemoryStorageBackend {
|
|
|
37
67
|
}
|
|
38
68
|
async createDatasetBackend(options = {}) {
|
|
39
69
|
const { isAlias, cacheKey } = MemoryStorageBackend.#resolveStorageKey(options);
|
|
40
|
-
const found = this
|
|
70
|
+
const found = this.#datasetBackendCache.find((store) => store.id === cacheKey ||
|
|
41
71
|
store.name?.toLowerCase() === cacheKey.toLowerCase() ||
|
|
42
72
|
store.cacheKey.toLowerCase() === cacheKey.toLowerCase());
|
|
43
73
|
if (found) {
|
|
@@ -48,12 +78,12 @@ export class MemoryStorageBackend {
|
|
|
48
78
|
cacheKey,
|
|
49
79
|
storageBackend: this,
|
|
50
80
|
});
|
|
51
|
-
this
|
|
81
|
+
this.#datasetBackendCache.push(newStore);
|
|
52
82
|
return newStore;
|
|
53
83
|
}
|
|
54
84
|
async createKeyValueStoreBackend(options = {}) {
|
|
55
85
|
const { isAlias, cacheKey } = MemoryStorageBackend.#resolveStorageKey(options);
|
|
56
|
-
const found = this
|
|
86
|
+
const found = this.#keyValueStoreBackendCache.find((store) => store.id === cacheKey ||
|
|
57
87
|
store.name?.toLowerCase() === cacheKey.toLowerCase() ||
|
|
58
88
|
store.cacheKey.toLowerCase() === cacheKey.toLowerCase());
|
|
59
89
|
if (found) {
|
|
@@ -64,12 +94,12 @@ export class MemoryStorageBackend {
|
|
|
64
94
|
cacheKey,
|
|
65
95
|
storageBackend: this,
|
|
66
96
|
});
|
|
67
|
-
this
|
|
97
|
+
this.#keyValueStoreBackendCache.push(newStore);
|
|
68
98
|
return newStore;
|
|
69
99
|
}
|
|
70
100
|
async createRequestQueueBackend(options = {}) {
|
|
71
101
|
const { isAlias, cacheKey } = MemoryStorageBackend.#resolveStorageKey(options);
|
|
72
|
-
const found = this
|
|
102
|
+
const found = this.#requestQueueBackendCache.find((queue) => queue.id === cacheKey ||
|
|
73
103
|
queue.name?.toLowerCase() === cacheKey.toLowerCase() ||
|
|
74
104
|
queue.cacheKey.toLowerCase() === cacheKey.toLowerCase());
|
|
75
105
|
if (found) {
|
|
@@ -80,20 +110,20 @@ export class MemoryStorageBackend {
|
|
|
80
110
|
cacheKey,
|
|
81
111
|
storageBackend: this,
|
|
82
112
|
});
|
|
83
|
-
this
|
|
113
|
+
this.#requestQueueBackendCache.push(newStore);
|
|
84
114
|
return newStore;
|
|
85
115
|
}
|
|
86
116
|
async storageExists(id, type) {
|
|
87
117
|
let backends;
|
|
88
118
|
switch (type) {
|
|
89
119
|
case 'Dataset':
|
|
90
|
-
backends = this
|
|
120
|
+
backends = this.#datasetBackendCache;
|
|
91
121
|
break;
|
|
92
122
|
case 'KeyValueStore':
|
|
93
|
-
backends = this
|
|
123
|
+
backends = this.#keyValueStoreBackendCache;
|
|
94
124
|
break;
|
|
95
125
|
case 'RequestQueue':
|
|
96
|
-
backends = this
|
|
126
|
+
backends = this.#requestQueueBackendCache;
|
|
97
127
|
break;
|
|
98
128
|
default:
|
|
99
129
|
return false;
|
|
@@ -115,9 +145,9 @@ export class MemoryStorageBackend {
|
|
|
115
145
|
};
|
|
116
146
|
await Promise.all([
|
|
117
147
|
// Only the default store holds the run input, so it is the only one that keeps `INPUT`.
|
|
118
|
-
purgeRunScoped(this
|
|
119
|
-
purgeRunScoped(this
|
|
120
|
-
purgeRunScoped(this
|
|
148
|
+
purgeRunScoped(this.#keyValueStoreBackendCache, async (store) => isDefault(store) ? store.purgeExceptInput() : store.purge()),
|
|
149
|
+
purgeRunScoped(this.#datasetBackendCache, async (store) => store.purge()),
|
|
150
|
+
purgeRunScoped(this.#requestQueueBackendCache, async (store) => store.purge()),
|
|
121
151
|
]);
|
|
122
152
|
}
|
|
123
153
|
/**
|
|
@@ -36,11 +36,9 @@ export class DatasetBackend extends BaseClient {
|
|
|
36
36
|
return this.toDatasetInfo();
|
|
37
37
|
}
|
|
38
38
|
async drop() {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
oldBackend.itemCount = 0;
|
|
43
|
-
oldBackend.#datasetEntries.clear();
|
|
39
|
+
if (this.storageBackend.evictBackend('Dataset', this.id)) {
|
|
40
|
+
this.itemCount = 0;
|
|
41
|
+
this.#datasetEntries.clear();
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
async purge() {
|
|
@@ -49,10 +49,8 @@ export class KeyValueStoreBackend extends BaseClient {
|
|
|
49
49
|
return this.toKeyValueStoreInfo();
|
|
50
50
|
}
|
|
51
51
|
async drop() {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const [oldBackend] = this.storageBackend.keyValueStoreBackendCache.splice(storeIndex, 1);
|
|
55
|
-
oldBackend.#keyValueEntries.clear();
|
|
52
|
+
if (this.storageBackend.evictBackend('KeyValueStore', this.id)) {
|
|
53
|
+
this.#keyValueEntries.clear();
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
56
|
async purge() {
|
|
@@ -55,16 +55,14 @@ export class RequestQueueBackend extends BaseClient {
|
|
|
55
55
|
// removed, which `listPendingHead` would then dereference as `undefined`.
|
|
56
56
|
await this.#queueStateMutex.wait();
|
|
57
57
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const [oldBackend] = this.storageBackend.requestQueueBackendCache.splice(storeIndex, 1);
|
|
61
|
-
oldBackend.pendingRequestCount = 0;
|
|
58
|
+
if (this.storageBackend.evictBackend('RequestQueue', this.id)) {
|
|
59
|
+
this.pendingRequestCount = 0;
|
|
62
60
|
// Clear all in-memory state, consistent with `purge`. Clearing `requests` alone would
|
|
63
61
|
// leave dangling ids in `forefrontRequestIds`/`inProgressRequestIds`, which a later head
|
|
64
62
|
// scan would resolve to a missing request and dereference.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
this.#requests.clear();
|
|
64
|
+
this.#forefrontRequestIds = [];
|
|
65
|
+
this.#inProgressRequestIds.clear();
|
|
68
66
|
}
|
|
69
67
|
}
|
|
70
68
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/core",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.136",
|
|
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"
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"@apify/log": "^2.5.18",
|
|
53
53
|
"@apify/timeout": "^0.4.4",
|
|
54
54
|
"@apify/utilities": "^2.15.5",
|
|
55
|
-
"@crawlee/fs-storage": "4.0.0-beta.
|
|
56
|
-
"@crawlee/http-client": "4.0.0-beta.
|
|
57
|
-
"@crawlee/types": "4.0.0-beta.
|
|
58
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
55
|
+
"@crawlee/fs-storage": "4.0.0-beta.136",
|
|
56
|
+
"@crawlee/http-client": "4.0.0-beta.136",
|
|
57
|
+
"@crawlee/types": "4.0.0-beta.136",
|
|
58
|
+
"@crawlee/utils": "4.0.0-beta.136",
|
|
59
59
|
"@sapphire/async-queue": "^1.5.5",
|
|
60
60
|
"@standard-schema/spec": "^1.0.0",
|
|
61
61
|
"@vladfrangu/async_event_emitter": "^2.4.6",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "d425bb9841885b4b71b84ae6fb0bafbdc4a7e978"
|
|
82
82
|
}
|