scrapely 1.2.12 → 1.2.13
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.js
CHANGED
|
@@ -18,6 +18,7 @@ var ACTOR_PERMISSION_LEVEL;
|
|
|
18
18
|
})(ACTOR_PERMISSION_LEVEL || (exports.ACTOR_PERMISSION_LEVEL = ACTOR_PERMISSION_LEVEL = {}));
|
|
19
19
|
const core_1 = require("@crawlee/core");
|
|
20
20
|
const configuration_js_1 = require("./configuration.js");
|
|
21
|
+
const proxy_input_aliases_js_1 = require("./proxy-input-aliases.js");
|
|
21
22
|
const patch_storage_js_1 = require("./storage/patch-storage.js");
|
|
22
23
|
const api_storage_client_js_1 = require("./storage/api-storage-client.js");
|
|
23
24
|
const events_client_js_1 = require("./events/events-client.js");
|
|
@@ -417,7 +418,7 @@ class Actor {
|
|
|
417
418
|
// Same treatment as the regular input: decrypt secret fields
|
|
418
419
|
// and let the synchronous Actor.input getter return it.
|
|
419
420
|
if (typeof metamorphInput === 'object' && !Array.isArray(metamorphInput)) {
|
|
420
|
-
const decrypted = (0, secrets_js_1.decryptSecretInputs)(metamorphInput);
|
|
421
|
+
const decrypted = (0, proxy_input_aliases_js_1.withProxyKeyAliases)((0, secrets_js_1.decryptSecretInputs)(metamorphInput));
|
|
421
422
|
cachedInput = decrypted;
|
|
422
423
|
return decrypted;
|
|
423
424
|
}
|
|
@@ -448,7 +449,8 @@ class Actor {
|
|
|
448
449
|
// Decrypt secret inputs after loading (must happen here, not in init(),
|
|
449
450
|
// because the input needs to be available first)
|
|
450
451
|
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
|
451
|
-
|
|
452
|
+
// Proxy objects carry both namings (scrapelyProxy* and apifyProxy*).
|
|
453
|
+
const decrypted = (0, proxy_input_aliases_js_1.withProxyKeyAliases)((0, secrets_js_1.decryptSecretInputs)(input));
|
|
452
454
|
// Large inputs are only in the INPUT record (no ACTOR_INPUT env):
|
|
453
455
|
// let the synchronous Actor.input getter return what was loaded here.
|
|
454
456
|
cachedInput = decrypted;
|
|
@@ -494,7 +496,7 @@ class Actor {
|
|
|
494
496
|
}
|
|
495
497
|
// Decrypt secret inputs after loading
|
|
496
498
|
if (cachedInput && typeof cachedInput === 'object' && !Array.isArray(cachedInput)) {
|
|
497
|
-
cachedInput = (0, secrets_js_1.decryptSecretInputs)(cachedInput);
|
|
499
|
+
cachedInput = (0, proxy_input_aliases_js_1.withProxyKeyAliases)((0, secrets_js_1.decryptSecretInputs)(cachedInput));
|
|
498
500
|
}
|
|
499
501
|
return cachedInput ?? undefined;
|
|
500
502
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy input objects with both namings. An input-schema proxy field may be
|
|
3
|
+
* written with the Scrapely names (`useScrapelyProxy`, `scrapelyProxyGroups`,
|
|
4
|
+
* `scrapelyProxyCountry`, `scrapelyProxySubdivision`) or the Apify names
|
|
5
|
+
* (`useApifyProxy`, `apifyProxy*`). Actor.getInput() and Actor.input return
|
|
6
|
+
* every set option under both names, so actor code that reads either naming
|
|
7
|
+
* sees the user's choice.
|
|
8
|
+
*
|
|
9
|
+
* Precedence matches createProxyConfiguration(): a boolean `useScrapelyProxy`
|
|
10
|
+
* wins over `useApifyProxy`, and a set `scrapelyProxy*` value wins over its
|
|
11
|
+
* `apifyProxy*` counterpart. Nothing is removed: keys with unset or
|
|
12
|
+
* non-boolean values stay as sent.
|
|
13
|
+
*/
|
|
14
|
+
/** `input` with every top-level proxy object under both namings; `input` itself when nothing changes. Never mutates. */
|
|
15
|
+
export declare function withProxyKeyAliases<T>(input: T): T;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Proxy input objects with both namings. An input-schema proxy field may be
|
|
4
|
+
* written with the Scrapely names (`useScrapelyProxy`, `scrapelyProxyGroups`,
|
|
5
|
+
* `scrapelyProxyCountry`, `scrapelyProxySubdivision`) or the Apify names
|
|
6
|
+
* (`useApifyProxy`, `apifyProxy*`). Actor.getInput() and Actor.input return
|
|
7
|
+
* every set option under both names, so actor code that reads either naming
|
|
8
|
+
* sees the user's choice.
|
|
9
|
+
*
|
|
10
|
+
* Precedence matches createProxyConfiguration(): a boolean `useScrapelyProxy`
|
|
11
|
+
* wins over `useApifyProxy`, and a set `scrapelyProxy*` value wins over its
|
|
12
|
+
* `apifyProxy*` counterpart. Nothing is removed: keys with unset or
|
|
13
|
+
* non-boolean values stay as sent.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.withProxyKeyAliases = withProxyKeyAliases;
|
|
17
|
+
const OPTION_NAMES = ['Groups', 'Country', 'Subdivision'];
|
|
18
|
+
const SWITCH_KEYS = ['useScrapelyProxy', 'useApifyProxy'];
|
|
19
|
+
function isPlainObject(value) {
|
|
20
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
function isSet(value) {
|
|
23
|
+
return value !== undefined && value !== null;
|
|
24
|
+
}
|
|
25
|
+
function isProxyObject(value) {
|
|
26
|
+
return SWITCH_KEYS.some((k) => Object.hasOwn(value, k))
|
|
27
|
+
|| OPTION_NAMES.some((n) => Object.hasOwn(value, `scrapelyProxy${n}`) || Object.hasOwn(value, `apifyProxy${n}`));
|
|
28
|
+
}
|
|
29
|
+
function aliasProxyObject(value) {
|
|
30
|
+
const result = { ...value };
|
|
31
|
+
const use = SWITCH_KEYS.map((k) => value[k]).find((v) => typeof v === 'boolean');
|
|
32
|
+
if (use !== undefined) {
|
|
33
|
+
for (const k of SWITCH_KEYS)
|
|
34
|
+
result[k] = use;
|
|
35
|
+
}
|
|
36
|
+
for (const name of OPTION_NAMES) {
|
|
37
|
+
const scrapely = `scrapelyProxy${name}`;
|
|
38
|
+
const apify = `apifyProxy${name}`;
|
|
39
|
+
const option = isSet(value[scrapely]) ? value[scrapely] : value[apify];
|
|
40
|
+
if (isSet(option)) {
|
|
41
|
+
result[scrapely] = option;
|
|
42
|
+
result[apify] = option;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function sameObject(a, b) {
|
|
48
|
+
const keys = Object.keys(a);
|
|
49
|
+
return keys.length === Object.keys(b).length && keys.every((k) => Object.hasOwn(b, k) && a[k] === b[k]);
|
|
50
|
+
}
|
|
51
|
+
/** `input` with every top-level proxy object under both namings; `input` itself when nothing changes. Never mutates. */
|
|
52
|
+
function withProxyKeyAliases(input) {
|
|
53
|
+
if (!isPlainObject(input))
|
|
54
|
+
return input;
|
|
55
|
+
let result;
|
|
56
|
+
for (const [key, value] of Object.entries(input)) {
|
|
57
|
+
if (!isPlainObject(value) || !isProxyObject(value))
|
|
58
|
+
continue;
|
|
59
|
+
const aliased = aliasProxyObject(value);
|
|
60
|
+
if (sameObject(aliased, value))
|
|
61
|
+
continue;
|
|
62
|
+
result ??= { ...input };
|
|
63
|
+
result[key] = aliased;
|
|
64
|
+
}
|
|
65
|
+
return (result ?? input);
|
|
66
|
+
}
|
|
@@ -313,7 +313,10 @@ class BaseApiClient {
|
|
|
313
313
|
const response = await fetch(url, {
|
|
314
314
|
method,
|
|
315
315
|
headers,
|
|
316
|
-
|
|
316
|
+
// Binary bodies (KV records) go out as the raw bytes, never JSON/base64.
|
|
317
|
+
body: body === undefined ? undefined
|
|
318
|
+
: typeof body === 'string' || body instanceof Uint8Array ? body
|
|
319
|
+
: JSON.stringify(body),
|
|
317
320
|
signal: controller.signal,
|
|
318
321
|
});
|
|
319
322
|
// Update rate limiter state from response headers
|
|
@@ -429,14 +432,15 @@ class BaseApiClient {
|
|
|
429
432
|
return { data: undefined, contentType: '' };
|
|
430
433
|
}
|
|
431
434
|
const responseContentType = response.headers.get('content-type') || 'application/json';
|
|
432
|
-
|
|
433
|
-
|
|
435
|
+
const mime = responseContentType.split(';')[0].trim().toLowerCase() || 'application/json';
|
|
436
|
+
if (mime === 'application/json' || mime.endsWith('+json')) {
|
|
437
|
+
return { data: await response.json(), contentType: mime };
|
|
434
438
|
}
|
|
435
|
-
if (
|
|
436
|
-
return { data:
|
|
439
|
+
if (mime.startsWith('text/')) {
|
|
440
|
+
return { data: await response.text(), contentType: mime };
|
|
437
441
|
}
|
|
438
|
-
|
|
439
|
-
return { data:
|
|
442
|
+
// Any other content type (video/mp4, image/png, application/zip, ...) is binary.
|
|
443
|
+
return { data: Buffer.from(await response.arrayBuffer()), contentType: mime };
|
|
440
444
|
});
|
|
441
445
|
}
|
|
442
446
|
}
|
|
@@ -632,12 +636,16 @@ class ApiKeyValueStoreClient extends BaseApiClient {
|
|
|
632
636
|
for await (const chunk of value) {
|
|
633
637
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
634
638
|
}
|
|
635
|
-
|
|
636
|
-
|
|
639
|
+
body = Buffer.concat(chunks);
|
|
640
|
+
ct = 'application/octet-stream';
|
|
641
|
+
}
|
|
642
|
+
else if (value instanceof Uint8Array) {
|
|
643
|
+
// Raw bytes, as Apify stores them; the API returns them unchanged on GET.
|
|
644
|
+
body = value;
|
|
637
645
|
ct = 'application/octet-stream';
|
|
638
646
|
}
|
|
639
|
-
else if (
|
|
640
|
-
body =
|
|
647
|
+
else if (value instanceof ArrayBuffer) {
|
|
648
|
+
body = Buffer.from(value);
|
|
641
649
|
ct = 'application/octet-stream';
|
|
642
650
|
}
|
|
643
651
|
else if (typeof value === 'string') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scrapely",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.13",
|
|
4
4
|
"description": "Scrapely SDK for building JavaScript and TypeScript Actors",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
"test:run-control": "npx tsx --test tests/run-control.test.ts",
|
|
39
39
|
"test:rq-list": "npx tsx --test tests/rq-list-requests.test.ts",
|
|
40
40
|
"test:terminal-status": "npx tsx --test tests/terminal-status-message.test.ts",
|
|
41
|
-
"test:cdp-retry": "npx tsx --test tests/cdp-retry.test.ts"
|
|
41
|
+
"test:cdp-retry": "npx tsx --test tests/cdp-retry.test.ts",
|
|
42
|
+
"test:proxy-input-aliases": "npx tsx --test tests/proxy-input-aliases.test.ts",
|
|
43
|
+
"test:charge-retries": "npx tsx --test tests/charge-retries.test.ts"
|
|
42
44
|
},
|
|
43
45
|
"author": "SandeepDev1",
|
|
44
46
|
"license": "ISC",
|