apify 4.0.0-beta.30 → 4.0.0-beta.32
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/dist/actor.d.ts +21 -43
- package/dist/actor.js +74 -74
- package/dist/apify_dataset_backend.d.ts +1 -1
- package/dist/apify_dataset_backend.js +6 -6
- package/dist/apify_key_value_store_backend.d.ts +1 -1
- package/dist/apify_key_value_store_backend.js +10 -10
- package/dist/apify_request_queue_backend.d.ts +1 -1
- package/dist/apify_request_queue_backend.js +3 -3
- package/dist/apify_request_queue_shared_backend.d.ts +1 -14
- package/dist/apify_request_queue_shared_backend.js +38 -38
- package/dist/apify_request_queue_single_backend.d.ts +1 -10
- package/dist/apify_request_queue_single_backend.js +44 -44
- package/dist/apify_storage_backend.d.ts +23 -12
- package/dist/apify_storage_backend.js +104 -51
- package/dist/charging.d.ts +1 -14
- package/dist/charging.js +84 -81
- package/dist/index.d.ts +1 -1
- package/dist/platform_event_manager.d.ts +1 -2
- package/dist/platform_event_manager.js +7 -7
- package/dist/proxy_configuration.d.ts +7 -18
- package/dist/proxy_configuration.js +46 -57
- package/dist/storage.d.ts +6 -26
- package/dist/storage.js +1 -79
- package/package.json +1 -1
|
@@ -39,9 +39,8 @@ import { Configuration } from './configuration.js';
|
|
|
39
39
|
* you can achieve the same effect using `setInterval()` and listening for the `migrating` event.
|
|
40
40
|
*/
|
|
41
41
|
export declare class PlatformEventManager extends EventManager {
|
|
42
|
+
#private;
|
|
42
43
|
readonly configuration: Configuration;
|
|
43
|
-
/** Websocket connection to Actor events. */
|
|
44
|
-
private eventsWs?;
|
|
45
44
|
constructor(configuration?: Configuration);
|
|
46
45
|
/**
|
|
47
46
|
* Initializes `Actor.events` event emitter by creating a connection to a websocket that provides them.
|
|
@@ -44,7 +44,7 @@ import { Configuration } from './configuration.js';
|
|
|
44
44
|
export class PlatformEventManager extends EventManager {
|
|
45
45
|
configuration;
|
|
46
46
|
/** Websocket connection to Actor events. */
|
|
47
|
-
eventsWs;
|
|
47
|
+
#eventsWs;
|
|
48
48
|
constructor(configuration = Configuration.getGlobalConfiguration()) {
|
|
49
49
|
super({
|
|
50
50
|
persistStateIntervalMillis: configuration.persistStateIntervalMillis,
|
|
@@ -69,8 +69,8 @@ export class PlatformEventManager extends EventManager {
|
|
|
69
69
|
this.createWebSocketConnection(eventsWsUrl);
|
|
70
70
|
}
|
|
71
71
|
createWebSocketConnection(eventsWsUrl) {
|
|
72
|
-
this
|
|
73
|
-
this
|
|
72
|
+
this.#eventsWs = new WebSocket(eventsWsUrl);
|
|
73
|
+
this.#eventsWs.on('message', (message) => {
|
|
74
74
|
if (!message)
|
|
75
75
|
return;
|
|
76
76
|
try {
|
|
@@ -87,15 +87,15 @@ export class PlatformEventManager extends EventManager {
|
|
|
87
87
|
this.log.exception(err, 'Cannot parse Actor event');
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
|
-
this
|
|
90
|
+
this.#eventsWs.on('error', (err) => {
|
|
91
91
|
// Don't print this error as this happens in the case of very short Actor.main().
|
|
92
92
|
if (err.message === 'WebSocket was closed before the connection was established')
|
|
93
93
|
return;
|
|
94
94
|
this.log.exception(err, 'web socket connection failed');
|
|
95
95
|
});
|
|
96
|
-
this
|
|
96
|
+
this.#eventsWs.on('close', () => {
|
|
97
97
|
this.log.debug('web socket has been closed');
|
|
98
|
-
this
|
|
98
|
+
this.#eventsWs = undefined;
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
@@ -108,6 +108,6 @@ export class PlatformEventManager extends EventManager {
|
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
110
|
await super.close();
|
|
111
|
-
this
|
|
111
|
+
this.#eventsWs?.close();
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -151,14 +151,8 @@ export interface ProxyInfo extends CoreProxyInfo {
|
|
|
151
151
|
* @category Scaling
|
|
152
152
|
*/
|
|
153
153
|
export declare class ProxyConfiguration extends CoreProxyConfiguration {
|
|
154
|
+
#private;
|
|
154
155
|
readonly configuration: Configuration;
|
|
155
|
-
private groups;
|
|
156
|
-
private countryCode?;
|
|
157
|
-
private subdivisionCode?;
|
|
158
|
-
private password?;
|
|
159
|
-
private hostname;
|
|
160
|
-
private port?;
|
|
161
|
-
private usesApifyProxy?;
|
|
162
156
|
protected readonly log: import("@apify/log").Log;
|
|
163
157
|
/**
|
|
164
158
|
* @internal
|
|
@@ -190,22 +184,22 @@ export declare class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
190
184
|
/**
|
|
191
185
|
* Returns proxy username.
|
|
192
186
|
*/
|
|
193
|
-
protected
|
|
187
|
+
protected getUsername(sessionId: string): string;
|
|
194
188
|
protected composeDefaultUrl(sessionId: string): string;
|
|
195
189
|
/**
|
|
196
190
|
* Fetch & set the proxy password from Apify API if an Apify token is provided.
|
|
197
191
|
*/
|
|
198
|
-
|
|
192
|
+
private setPasswordIfToken;
|
|
199
193
|
/**
|
|
200
194
|
* Checks whether the user has access to the proxies specified in the provided ProxyConfigurationOptions.
|
|
201
195
|
* If the check can not be made, it only prints a warning and allows the program to continue. This is to
|
|
202
196
|
* prevent program crashes caused by short downtimes of Proxy.
|
|
203
197
|
*/
|
|
204
|
-
protected
|
|
198
|
+
protected checkAccess(): Promise<boolean>;
|
|
205
199
|
/**
|
|
206
200
|
* Apify Proxy can be down for a second or a minute, but this should not crash processes.
|
|
207
201
|
*/
|
|
208
|
-
protected
|
|
202
|
+
protected fetchStatus(): Promise<ProxyStatus | undefined>;
|
|
209
203
|
/**
|
|
210
204
|
* Fetches the Apify Proxy status endpoint once, *through* the proxy, so the
|
|
211
205
|
* response reports on this exact connection (auth + man-in-the-middle).
|
|
@@ -214,16 +208,11 @@ export declare class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
214
208
|
* plus a `Proxy-Authorization` header — so no proxy-agent dependency is
|
|
215
209
|
* needed. The status endpoint (`http://proxy.apify.com`) is plain HTTP.
|
|
216
210
|
*/
|
|
217
|
-
protected
|
|
211
|
+
protected requestStatus(statusUrl: string, proxyUrl: string): Promise<ProxyStatus>;
|
|
218
212
|
/**
|
|
219
213
|
* Throws cannot combine custom proxies with Apify Proxy
|
|
220
214
|
* @internal
|
|
221
215
|
*/
|
|
222
|
-
protected
|
|
223
|
-
/**
|
|
224
|
-
* Throws cannot combine custom proxies with custom generating function
|
|
225
|
-
* @internal
|
|
226
|
-
*/
|
|
227
|
-
protected _throwCannotCombineCustomMethods(): void;
|
|
216
|
+
protected throwCannotCombineCustomWithApify(): void;
|
|
228
217
|
}
|
|
229
218
|
export {};
|
|
@@ -53,13 +53,13 @@ const SESSION_ID_LENGTH = 12;
|
|
|
53
53
|
*/
|
|
54
54
|
export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
55
55
|
configuration;
|
|
56
|
-
groups;
|
|
57
|
-
countryCode;
|
|
58
|
-
subdivisionCode;
|
|
59
|
-
password;
|
|
60
|
-
hostname;
|
|
61
|
-
port;
|
|
62
|
-
usesApifyProxy;
|
|
56
|
+
#groups;
|
|
57
|
+
#countryCode;
|
|
58
|
+
#subdivisionCode;
|
|
59
|
+
#password;
|
|
60
|
+
#hostname;
|
|
61
|
+
#port;
|
|
62
|
+
#usesApifyProxy;
|
|
63
63
|
log = defaultLog.child({ prefix: 'ProxyConfiguration' });
|
|
64
64
|
/**
|
|
65
65
|
* @internal
|
|
@@ -96,17 +96,15 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
96
96
|
}
|
|
97
97
|
// Validation
|
|
98
98
|
if ((proxyUrls || newUrlFunction) && (groupsToUse.length || countryCodeToUse || subdivisionCodeToUse)) {
|
|
99
|
-
this.
|
|
99
|
+
this.throwCannotCombineCustomWithApify();
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this
|
|
104
|
-
this
|
|
105
|
-
this
|
|
106
|
-
this
|
|
107
|
-
this
|
|
108
|
-
this.port = port;
|
|
109
|
-
this.usesApifyProxy = !proxyUrls && !newUrlFunction;
|
|
101
|
+
this.#groups = groupsToUse;
|
|
102
|
+
this.#countryCode = countryCodeToUse;
|
|
103
|
+
this.#subdivisionCode = subdivisionCodeToUse;
|
|
104
|
+
this.#password = password;
|
|
105
|
+
this.#hostname = hostname;
|
|
106
|
+
this.#port = port;
|
|
107
|
+
this.#usesApifyProxy = !proxyUrls && !newUrlFunction;
|
|
110
108
|
if (proxyUrls && proxyUrls.some((url) => url?.includes('apify.com'))) {
|
|
111
109
|
this.log.warning('Some Apify proxy features may work incorrectly. Please consider setting up Apify properties instead of `proxyUrls`.\n' +
|
|
112
110
|
'See https://docs.apify.com/sdk/js/docs/concepts/proxy-management#apify-proxy-configuration');
|
|
@@ -121,11 +119,11 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
121
119
|
* `ProxyConfiguration` instance instead of calling this manually.
|
|
122
120
|
*/
|
|
123
121
|
async initialize(options) {
|
|
124
|
-
if (this
|
|
125
|
-
if (!this
|
|
126
|
-
await this.
|
|
122
|
+
if (this.#usesApifyProxy) {
|
|
123
|
+
if (!this.#password) {
|
|
124
|
+
await this.setPasswordIfToken();
|
|
127
125
|
}
|
|
128
|
-
if (!this
|
|
126
|
+
if (!this.#password) {
|
|
129
127
|
if (Actor.isAtHome()) {
|
|
130
128
|
throw new Error(`Apify Proxy password must be provided using options.password or the "${APIFY_ENV_VARS.PROXY_PASSWORD}" environment variable. ` +
|
|
131
129
|
`You can also provide your Apify token via the "${APIFY_ENV_VARS.TOKEN}" environment variable, ` +
|
|
@@ -139,7 +137,7 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
139
|
if (options?.checkAccess !== false) {
|
|
142
|
-
return this.
|
|
140
|
+
return this.checkAccess();
|
|
143
141
|
}
|
|
144
142
|
}
|
|
145
143
|
return true;
|
|
@@ -161,12 +159,12 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
161
159
|
hostname: parsed.hostname,
|
|
162
160
|
port: parsed.port,
|
|
163
161
|
};
|
|
164
|
-
if (this
|
|
165
|
-
result.groups = this
|
|
166
|
-
if (this
|
|
167
|
-
result.countryCode = this
|
|
168
|
-
if (this
|
|
169
|
-
result.subdivisionCode = this
|
|
162
|
+
if (this.#usesApifyProxy) {
|
|
163
|
+
result.groups = this.#groups;
|
|
164
|
+
if (this.#countryCode !== undefined)
|
|
165
|
+
result.countryCode = this.#countryCode;
|
|
166
|
+
if (this.#subdivisionCode !== undefined)
|
|
167
|
+
result.subdivisionCode = this.#subdivisionCode;
|
|
170
168
|
}
|
|
171
169
|
return result;
|
|
172
170
|
}
|
|
@@ -176,7 +174,7 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
176
174
|
* `proxyUrls`, the URLs are rotated round-robin.
|
|
177
175
|
*/
|
|
178
176
|
async newUrl(options) {
|
|
179
|
-
if (!this
|
|
177
|
+
if (!this.#usesApifyProxy) {
|
|
180
178
|
return super.newUrl(options);
|
|
181
179
|
}
|
|
182
180
|
return this.composeDefaultUrl(cryptoRandomObjectId(SESSION_ID_LENGTH));
|
|
@@ -184,40 +182,38 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
184
182
|
/**
|
|
185
183
|
* Returns proxy username.
|
|
186
184
|
*/
|
|
187
|
-
|
|
188
|
-
const { groups, countryCode, subdivisionCode } = this;
|
|
185
|
+
getUsername(sessionId) {
|
|
189
186
|
const parts = [];
|
|
190
|
-
if (groups && groups.length) {
|
|
191
|
-
parts.push(`groups-${groups.join('+')}`);
|
|
187
|
+
if (this.#groups && this.#groups.length) {
|
|
188
|
+
parts.push(`groups-${this.#groups.join('+')}`);
|
|
192
189
|
}
|
|
193
190
|
parts.push(`session-${sessionId}`);
|
|
194
|
-
if (subdivisionCode) {
|
|
195
|
-
parts.push(`country-${countryCode}_${subdivisionCode}`);
|
|
191
|
+
if (this.#subdivisionCode) {
|
|
192
|
+
parts.push(`country-${this.#countryCode}_${this.#subdivisionCode}`);
|
|
196
193
|
}
|
|
197
|
-
else if (countryCode) {
|
|
198
|
-
parts.push(`country-${countryCode}`);
|
|
194
|
+
else if (this.#countryCode) {
|
|
195
|
+
parts.push(`country-${this.#countryCode}`);
|
|
199
196
|
}
|
|
200
197
|
return parts.join(',');
|
|
201
198
|
}
|
|
202
199
|
composeDefaultUrl(sessionId) {
|
|
203
|
-
const username = this.
|
|
204
|
-
const url = new URL(`http://${this
|
|
200
|
+
const username = this.getUsername(sessionId);
|
|
201
|
+
const url = new URL(`http://${this.#hostname}:${this.#port}`);
|
|
205
202
|
url.username = `${username}`;
|
|
206
|
-
url.password = `${this
|
|
203
|
+
url.password = `${this.#password}`;
|
|
207
204
|
const urlString = url.toString();
|
|
208
205
|
return urlString.substring(0, urlString.length - 1);
|
|
209
206
|
}
|
|
210
207
|
/**
|
|
211
208
|
* Fetch & set the proxy password from Apify API if an Apify token is provided.
|
|
212
209
|
*/
|
|
213
|
-
|
|
214
|
-
async _setPasswordIfToken() {
|
|
210
|
+
async setPasswordIfToken() {
|
|
215
211
|
const { token } = this.configuration;
|
|
216
212
|
if (!token)
|
|
217
213
|
return;
|
|
218
214
|
try {
|
|
219
215
|
const user = await Actor.apifyClient.user().get();
|
|
220
|
-
this
|
|
216
|
+
this.#password = user.proxy?.password;
|
|
221
217
|
}
|
|
222
218
|
catch (error) {
|
|
223
219
|
if (Actor.isAtHome()) {
|
|
@@ -235,8 +231,8 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
235
231
|
* If the check can not be made, it only prints a warning and allows the program to continue. This is to
|
|
236
232
|
* prevent program crashes caused by short downtimes of Proxy.
|
|
237
233
|
*/
|
|
238
|
-
async
|
|
239
|
-
const status = await this.
|
|
234
|
+
async checkAccess() {
|
|
235
|
+
const status = await this.fetchStatus();
|
|
240
236
|
if (!status) {
|
|
241
237
|
this.log.warning('Apify Proxy access check timed out. Watch out for errors with status code 407. ' +
|
|
242
238
|
"If you see some, it most likely means you don't have access to either all or some of the proxies you're trying to use.");
|
|
@@ -261,7 +257,7 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
261
257
|
/**
|
|
262
258
|
* Apify Proxy can be down for a second or a minute, but this should not crash processes.
|
|
263
259
|
*/
|
|
264
|
-
async
|
|
260
|
+
async fetchStatus() {
|
|
265
261
|
const { proxyStatusUrl } = this.configuration;
|
|
266
262
|
const statusUrl = `${proxyStatusUrl}/?format=json`;
|
|
267
263
|
const proxyUrl = await this.newUrl();
|
|
@@ -270,7 +266,7 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
270
266
|
return undefined;
|
|
271
267
|
for (let attempt = 1; attempt <= CHECK_ACCESS_MAX_ATTEMPTS; attempt++) {
|
|
272
268
|
try {
|
|
273
|
-
return await this.
|
|
269
|
+
return await this.requestStatus(statusUrl, proxyUrl);
|
|
274
270
|
}
|
|
275
271
|
catch {
|
|
276
272
|
// retry connection errors
|
|
@@ -286,7 +282,7 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
286
282
|
* plus a `Proxy-Authorization` header — so no proxy-agent dependency is
|
|
287
283
|
* needed. The status endpoint (`http://proxy.apify.com`) is plain HTTP.
|
|
288
284
|
*/
|
|
289
|
-
async
|
|
285
|
+
async requestStatus(statusUrl, proxyUrl) {
|
|
290
286
|
const target = new URL(statusUrl);
|
|
291
287
|
const proxy = new URL(proxyUrl);
|
|
292
288
|
const headers = { host: target.host };
|
|
@@ -304,7 +300,7 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
304
300
|
});
|
|
305
301
|
request.end();
|
|
306
302
|
// `once` rejects if the request emits `error` first (connection refused,
|
|
307
|
-
// timeout/abort), so failures propagate to the retry loop in `
|
|
303
|
+
// timeout/abort), so failures propagate to the retry loop in `fetchStatus`.
|
|
308
304
|
const [response] = (await once(request, 'response'));
|
|
309
305
|
const statusCode = response.statusCode ?? 0;
|
|
310
306
|
if (statusCode < 200 || statusCode >= 300) {
|
|
@@ -317,17 +313,10 @@ export class ProxyConfiguration extends CoreProxyConfiguration {
|
|
|
317
313
|
* Throws cannot combine custom proxies with Apify Proxy
|
|
318
314
|
* @internal
|
|
319
315
|
*/
|
|
320
|
-
|
|
316
|
+
throwCannotCombineCustomWithApify() {
|
|
321
317
|
throw new Error('Cannot combine custom proxies with Apify Proxy! ' +
|
|
322
318
|
'It is not allowed to set "options.proxyUrls" or "options.newUrlFunction" combined with ' +
|
|
323
319
|
'"options.groups", "options.apifyProxyGroups", "options.countryCode", "options.apifyProxyCountry", ' +
|
|
324
320
|
'"options.subdivisionCode" or "options.apifyProxySubdivision".');
|
|
325
321
|
}
|
|
326
|
-
/**
|
|
327
|
-
* Throws cannot combine custom proxies with custom generating function
|
|
328
|
-
* @internal
|
|
329
|
-
*/
|
|
330
|
-
_throwCannotCombineCustomMethods() {
|
|
331
|
-
throw new Error('Cannot combine custom proxies "options.proxyUrls" with custom generating function "options.newUrlFunction".');
|
|
332
|
-
}
|
|
333
322
|
}
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import type { IStorage, StorageOpenOptions } from '@crawlee/core';
|
|
2
|
-
import type { Constructor, StorageBackend } from '@crawlee/types';
|
|
3
|
-
import type { Configuration } from './configuration.js';
|
|
4
1
|
export interface OpenStorageOptions {
|
|
5
2
|
/**
|
|
6
3
|
* If set to `true` then the cloud storage is used even if the `CRAWLEE_STORAGE_DIR`
|
|
@@ -10,8 +7,11 @@ export interface OpenStorageOptions {
|
|
|
10
7
|
forceCloud?: boolean;
|
|
11
8
|
}
|
|
12
9
|
/**
|
|
13
|
-
* Identifies a storage by its alias
|
|
14
|
-
*
|
|
10
|
+
* Identifies a run-scoped storage by its alias.
|
|
11
|
+
*
|
|
12
|
+
* An alias declared in the Actor's schema storages (the `ACTOR_STORAGES_JSON` environment variable)
|
|
13
|
+
* resolves to the storage the platform created for it; any other alias gets an unnamed storage of
|
|
14
|
+
* its own, for this run only.
|
|
15
15
|
*/
|
|
16
16
|
export interface StorageAlias {
|
|
17
17
|
alias: string;
|
|
@@ -31,28 +31,8 @@ export interface StorageName {
|
|
|
31
31
|
/**
|
|
32
32
|
* Identifies a storage to open. Can be:
|
|
33
33
|
* - A plain `string` for backward compatibility (treated as ID or name)
|
|
34
|
-
* - `{ alias: string }` to
|
|
34
|
+
* - `{ alias: string }` to open a run-scoped storage — see {@link StorageAlias}
|
|
35
35
|
* - `{ id: string }` to open by explicit platform ID
|
|
36
36
|
* - `{ name: string }` to open by explicit name
|
|
37
37
|
*/
|
|
38
38
|
export type StorageIdentifier = string | StorageAlias | StorageId | StorageName;
|
|
39
|
-
/**
|
|
40
|
-
* Identifies a storage to open, without alias support.
|
|
41
|
-
* Used for key-value stores and request queues, which do not support aliases.
|
|
42
|
-
* Can be:
|
|
43
|
-
* - A plain `string` for backward compatibility (treated as ID or name)
|
|
44
|
-
* - `{ id: string }` to open by explicit platform ID
|
|
45
|
-
* - `{ name: string }` to open by explicit name
|
|
46
|
-
*/
|
|
47
|
-
export type StorageIdentifierWithoutAlias = string | StorageId | StorageName;
|
|
48
|
-
export interface OpenStorageContext {
|
|
49
|
-
config: Configuration;
|
|
50
|
-
backend?: StorageBackend;
|
|
51
|
-
purgedStorageAliases: Set<string>;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Opens a storage by its identifier, handling Apify alias resolution and local purging.
|
|
55
|
-
*/
|
|
56
|
-
export declare function openStorage<T extends IStorage>(storageClass: Constructor<T> & {
|
|
57
|
-
open(id?: string | null, options?: StorageOpenOptions): Promise<T>;
|
|
58
|
-
}, identifier: StorageIdentifier | null | undefined, context: OpenStorageContext): Promise<T>;
|
package/dist/storage.js
CHANGED
|
@@ -1,79 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const STORAGE_TYPE_KEYS = {
|
|
3
|
-
Dataset: 'datasets',
|
|
4
|
-
KeyValueStore: 'keyValueStores',
|
|
5
|
-
RequestQueue: 'requestQueues',
|
|
6
|
-
};
|
|
7
|
-
const parsedStoragesJson = new Map();
|
|
8
|
-
/**
|
|
9
|
-
* Resolves a {@link StorageIdentifier} to a plain string ID or name
|
|
10
|
-
* that can be passed to crawlee v4's `<Storage>.open()`.
|
|
11
|
-
*/
|
|
12
|
-
function resolveStorageIdentifier(storageType, identifier, config) {
|
|
13
|
-
if (identifier === null || identifier === undefined) {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
if (typeof identifier === 'string') {
|
|
17
|
-
return identifier;
|
|
18
|
-
}
|
|
19
|
-
if ('id' in identifier) {
|
|
20
|
-
return identifier.id;
|
|
21
|
-
}
|
|
22
|
-
if ('name' in identifier) {
|
|
23
|
-
return identifier.name;
|
|
24
|
-
}
|
|
25
|
-
// { alias: string }
|
|
26
|
-
const storagesJson = config.actorStoragesJson;
|
|
27
|
-
if (config.isAtHome && storagesJson) {
|
|
28
|
-
let storages;
|
|
29
|
-
try {
|
|
30
|
-
if (!parsedStoragesJson.has(storagesJson)) {
|
|
31
|
-
parsedStoragesJson.set(storagesJson, JSON.parse(storagesJson));
|
|
32
|
-
}
|
|
33
|
-
storages = parsedStoragesJson.get(storagesJson);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
throw new Error(`Failed to parse ACTOR_STORAGES_JSON environment variable: ${storagesJson}`);
|
|
37
|
-
}
|
|
38
|
-
const typeKey = STORAGE_TYPE_KEYS[storageType];
|
|
39
|
-
const resolvedId = storages[typeKey]?.[identifier.alias];
|
|
40
|
-
if (resolvedId) {
|
|
41
|
-
return resolvedId;
|
|
42
|
-
}
|
|
43
|
-
throw new Error(`Storage alias "${identifier.alias}" not found in ACTOR_STORAGES_JSON for storage type "${storageType}". ` +
|
|
44
|
-
`Available aliases: ${Object.keys(storages[typeKey] ?? {}).join(', ') || '(none)'}`);
|
|
45
|
-
}
|
|
46
|
-
// When using local storage, just use the alias as a name.
|
|
47
|
-
// When using platform storage, we can't just make up a name — the alias must be
|
|
48
|
-
// in ACTOR_STORAGES_JSON.
|
|
49
|
-
if (config.isAtHome) {
|
|
50
|
-
throw new Error(`Storage alias "${identifier.alias}" cannot be resolved because ACTOR_STORAGES_JSON is not set. ` +
|
|
51
|
-
`Aliases are only available for storages declared in the Actor's schema.`);
|
|
52
|
-
}
|
|
53
|
-
return identifier.alias;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Opens a storage by its identifier, handling Apify alias resolution and local purging.
|
|
57
|
-
*/
|
|
58
|
-
export async function openStorage(storageClass, identifier, context) {
|
|
59
|
-
const isAlias = identifier !== null && identifier !== undefined && typeof identifier === 'object' && 'alias' in identifier;
|
|
60
|
-
if (isAlias && !context.config.isAtHome && context.backend instanceof ApifyStorageBackend) {
|
|
61
|
-
throw new Error('The `alias` option is not allowed for Apify-based storages running outside of Apify');
|
|
62
|
-
}
|
|
63
|
-
const resolvedIdOrName = resolveStorageIdentifier(storageClass.name, identifier, context.config);
|
|
64
|
-
// When running locally, purge aliased storages on first open
|
|
65
|
-
// (similar to how crawlee purges default storages on start).
|
|
66
|
-
if (isAlias &&
|
|
67
|
-
!context.config.isAtHome &&
|
|
68
|
-
context.config.purgeOnStart &&
|
|
69
|
-
!context.purgedStorageAliases.has(identifier.alias)) {
|
|
70
|
-
context.purgedStorageAliases.add(identifier.alias);
|
|
71
|
-
const existingStorage = await storageClass.open(resolvedIdOrName ?? null, {
|
|
72
|
-
storageBackend: context.backend,
|
|
73
|
-
});
|
|
74
|
-
await existingStorage.drop();
|
|
75
|
-
}
|
|
76
|
-
return storageClass.open(resolvedIdOrName ?? null, {
|
|
77
|
-
storageBackend: context.backend,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apify",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.32",
|
|
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"
|