scrapely 1.2.3 → 1.2.5
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 +119 -1
- package/dist/actor.d.ts +703 -0
- package/dist/actor.js +1441 -0
- package/dist/browser-configuration.d.ts +58 -0
- package/dist/browser-configuration.js +128 -0
- package/dist/browser-locations.d.ts +5 -0
- package/dist/browser-locations.js +5469 -0
- package/dist/charging.d.ts +153 -0
- package/dist/charging.js +782 -0
- package/dist/charging_client.d.ts +18 -0
- package/dist/charging_client.js +49 -0
- package/dist/configuration.d.ts +121 -0
- package/dist/configuration.js +128 -0
- package/dist/crypto/secrets.d.ts +44 -0
- package/dist/crypto/secrets.js +164 -0
- package/dist/events/events-client.d.ts +59 -0
- package/dist/events/events-client.js +120 -0
- package/dist/events/scrapely-event-manager.d.ts +13 -0
- package/dist/events/scrapely-event-manager.js +61 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +95 -0
- package/dist/log-env.d.ts +1 -0
- package/dist/log-env.js +12 -0
- package/dist/manifest.d.ts +30 -0
- package/dist/manifest.js +47 -0
- package/dist/patch-browser-pool.d.ts +66 -0
- package/dist/patch-browser-pool.js +827 -0
- package/dist/proxy-configuration.d.ts +118 -0
- package/dist/proxy-configuration.js +88 -0
- package/dist/request-list.d.ts +57 -0
- package/dist/request-list.js +79 -0
- package/dist/runner/docker-manager.d.ts +88 -0
- package/dist/runner/docker-manager.js +283 -0
- package/dist/runner/index.d.ts +2 -0
- package/dist/runner/index.js +7 -0
- package/dist/runner/timeout-manager.d.ts +43 -0
- package/dist/runner/timeout-manager.js +90 -0
- package/dist/scrapely-proxy-configuration.d.ts +99 -0
- package/dist/scrapely-proxy-configuration.js +559 -0
- package/dist/storage/api-storage-client.d.ts +199 -0
- package/dist/storage/api-storage-client.js +910 -0
- package/dist/storage/billing-tracker.d.ts +163 -0
- package/dist/storage/billing-tracker.js +260 -0
- package/dist/storage/dataset-client.d.ts +43 -0
- package/dist/storage/dataset-client.js +232 -0
- package/dist/storage/dataset-collection-client.d.ts +8 -0
- package/dist/storage/dataset-collection-client.js +75 -0
- package/dist/storage/dataset-push-script.d.ts +18 -0
- package/dist/storage/dataset-push-script.js +38 -0
- package/dist/storage/dragonfly-client.d.ts +94 -0
- package/dist/storage/dragonfly-client.js +167 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.js +25 -0
- package/dist/storage/key-value-store-client.d.ts +61 -0
- package/dist/storage/key-value-store-client.js +306 -0
- package/dist/storage/kv-store-collection-client.d.ts +8 -0
- package/dist/storage/kv-store-collection-client.js +76 -0
- package/dist/storage/kvs-iteration.d.ts +17 -0
- package/dist/storage/kvs-iteration.js +126 -0
- package/dist/storage/log-manager.d.ts +114 -0
- package/dist/storage/log-manager.js +244 -0
- package/dist/storage/patch-storage.d.ts +11 -0
- package/dist/storage/patch-storage.js +177 -0
- package/dist/storage/request-queue-client.d.ts +93 -0
- package/dist/storage/request-queue-client.js +522 -0
- package/dist/storage/rq-collection-client.d.ts +8 -0
- package/dist/storage/rq-collection-client.js +89 -0
- package/dist/storage/rq-list-requests.d.ts +59 -0
- package/dist/storage/rq-list-requests.js +114 -0
- package/dist/storage/rq-lock-script.d.ts +56 -0
- package/dist/storage/rq-lock-script.js +127 -0
- package/dist/storage/run-metadata.d.ts +98 -0
- package/dist/storage/run-metadata.js +256 -0
- package/dist/storage/s3-client.d.ts +103 -0
- package/dist/storage/s3-client.js +302 -0
- package/dist/storage/ttl-manager.d.ts +70 -0
- package/dist/storage/ttl-manager.js +115 -0
- package/dist/storage/utils.d.ts +3 -0
- package/dist/storage/utils.js +19 -0
- package/dist/storage-identifiers.d.ts +20 -0
- package/dist/storage-identifiers.js +6 -0
- package/dist/utils.d.ts +54 -0
- package/dist/utils.js +63 -0
- package/dist/validator.d.ts +24 -0
- package/dist/validator.js +158 -0
- package/package.json +90 -9
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ApifyClientOptions, PpeAwarePushDataContext } from 'scrapely-client';
|
|
2
|
+
import { ApifyClient, USES_PUSH_DATA_INTERCEPTION, pushDataChargingContext } from 'scrapely-client';
|
|
3
|
+
import { type ChargingManager } from './charging.js';
|
|
4
|
+
import type { Configuration } from './configuration.js';
|
|
5
|
+
export { USES_PUSH_DATA_INTERCEPTION, pushDataChargingContext };
|
|
6
|
+
export type { PpeAwarePushDataContext };
|
|
7
|
+
/**
|
|
8
|
+
* Returns a `scrapely-client` `ApifyClient` configured with the SDK's charging
|
|
9
|
+
* hooks, so pushes to the run's default dataset are intercepted and charged
|
|
10
|
+
* pay-per-event by the client itself (no subclassing).
|
|
11
|
+
*
|
|
12
|
+
* `defaultDatasetId` is captured from the configuration at construction time;
|
|
13
|
+
* the pricing check happens lazily on every `dataset()` call.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createChargingClient(options: ApifyClientOptions, actor: {
|
|
16
|
+
config: Configuration;
|
|
17
|
+
getChargingManager: () => ChargingManager;
|
|
18
|
+
}): ApifyClient;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pushDataChargingContext = exports.USES_PUSH_DATA_INTERCEPTION = void 0;
|
|
4
|
+
exports.createChargingClient = createChargingClient;
|
|
5
|
+
const scrapely_client_1 = require("scrapely-client");
|
|
6
|
+
Object.defineProperty(exports, "USES_PUSH_DATA_INTERCEPTION", { enumerable: true, get: function () { return scrapely_client_1.USES_PUSH_DATA_INTERCEPTION; } });
|
|
7
|
+
Object.defineProperty(exports, "pushDataChargingContext", { enumerable: true, get: function () { return scrapely_client_1.pushDataChargingContext; } });
|
|
8
|
+
const charging_js_1 = require("./charging.js");
|
|
9
|
+
/**
|
|
10
|
+
* Adapts the SDK's {@link ChargingManager} to `scrapely-client`'s
|
|
11
|
+
* {@link ChargingHooks} protocol. The client only routes `pushItems()`
|
|
12
|
+
* through these hooks for the run's default dataset; all pricing/budget/ledger
|
|
13
|
+
* decisions stay in the SDK.
|
|
14
|
+
*/
|
|
15
|
+
function createChargingHooks(actor) {
|
|
16
|
+
return {
|
|
17
|
+
// Called by the client at `dataset()` time, so pricing is consulted
|
|
18
|
+
// lazily — same timing as the former subclass override.
|
|
19
|
+
shouldChargeDatasetPushes() {
|
|
20
|
+
return (0, charging_js_1.hasDefaultDatasetItemEventInPricing)(actor.getChargingManager().getPricingInfo().perEventPrices);
|
|
21
|
+
},
|
|
22
|
+
async pushAndCharge({ items, eventName, pushFn }) {
|
|
23
|
+
return (0, charging_js_1.pushDataAndCharge)({
|
|
24
|
+
chargingManager: actor.getChargingManager(),
|
|
25
|
+
items,
|
|
26
|
+
eventName,
|
|
27
|
+
// The client only intercepts the default dataset.
|
|
28
|
+
isDefaultDataset: true,
|
|
29
|
+
pushFn: async (limitedItems) => pushFn(limitedItems),
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
mergeChargeResults: charging_js_1.mergeChargeResults,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns a `scrapely-client` `ApifyClient` configured with the SDK's charging
|
|
37
|
+
* hooks, so pushes to the run's default dataset are intercepted and charged
|
|
38
|
+
* pay-per-event by the client itself (no subclassing).
|
|
39
|
+
*
|
|
40
|
+
* `defaultDatasetId` is captured from the configuration at construction time;
|
|
41
|
+
* the pricing check happens lazily on every `dataset()` call.
|
|
42
|
+
*/
|
|
43
|
+
function createChargingClient(options, actor) {
|
|
44
|
+
return new scrapely_client_1.ApifyClient({
|
|
45
|
+
...options,
|
|
46
|
+
defaultDatasetId: actor.config.get('defaultDatasetId'),
|
|
47
|
+
charging: createChargingHooks(actor),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ConfigurationOptions as CoreConfigurationOptions } from '@crawlee/core';
|
|
2
|
+
import { Configuration as CoreConfiguration } from '@crawlee/core';
|
|
3
|
+
export interface ConfigurationOptions extends CoreConfigurationOptions {
|
|
4
|
+
metamorphAfterSleepMillis?: number;
|
|
5
|
+
actorEventsWsUrl?: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
actorId?: string;
|
|
8
|
+
actorRunId?: string;
|
|
9
|
+
actorTaskId?: string;
|
|
10
|
+
apiBaseUrl?: string;
|
|
11
|
+
apiPublicBaseUrl?: string;
|
|
12
|
+
containerPort?: number;
|
|
13
|
+
containerUrl?: string;
|
|
14
|
+
proxyHostname?: string;
|
|
15
|
+
proxyPassword?: string;
|
|
16
|
+
proxyPort?: number;
|
|
17
|
+
proxyStatusUrl?: string;
|
|
18
|
+
/** @deprecated use `containerPort` instead */
|
|
19
|
+
standbyPort?: number;
|
|
20
|
+
standbyUrl?: string;
|
|
21
|
+
isAtHome?: boolean;
|
|
22
|
+
userId?: string;
|
|
23
|
+
inputSecretsPrivateKeyPassphrase?: string;
|
|
24
|
+
inputSecretsPrivateKeyFile?: string;
|
|
25
|
+
maxTotalChargeUsd?: number;
|
|
26
|
+
metaOrigin?: string;
|
|
27
|
+
testPayPerEvent?: boolean;
|
|
28
|
+
useChargingLogDataset?: boolean;
|
|
29
|
+
actorPricingInfo?: string;
|
|
30
|
+
chargedEventCounts?: string;
|
|
31
|
+
actorStoragesJson?: string;
|
|
32
|
+
actorPermissionLevel?: string;
|
|
33
|
+
userIsPaying?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare class Configuration extends CoreConfiguration {
|
|
36
|
+
static globalConfig?: Configuration;
|
|
37
|
+
protected static ENV_MAP: {
|
|
38
|
+
APIFY_AVAILABLE_MEMORY_RATIO: string;
|
|
39
|
+
APIFY_PURGE_ON_START: string;
|
|
40
|
+
APIFY_MEMORY_MBYTES: string;
|
|
41
|
+
APIFY_DEFAULT_DATASET_ID: string;
|
|
42
|
+
APIFY_DEFAULT_KEY_VALUE_STORE_ID: string;
|
|
43
|
+
APIFY_DEFAULT_REQUEST_QUEUE_ID: string;
|
|
44
|
+
APIFY_INPUT_KEY: string;
|
|
45
|
+
APIFY_PERSIST_STATE_INTERVAL_MILLIS: string;
|
|
46
|
+
APIFY_HEADLESS: string;
|
|
47
|
+
APIFY_XVFB: string;
|
|
48
|
+
APIFY_CHROME_EXECUTABLE_PATH: string;
|
|
49
|
+
APIFY_DEFAULT_BROWSER_PATH: string;
|
|
50
|
+
APIFY_DISABLE_BROWSER_SANDBOX: string;
|
|
51
|
+
APIFY_TOKEN: string;
|
|
52
|
+
APIFY_METAMORPH_AFTER_SLEEP_MILLIS: string;
|
|
53
|
+
APIFY_TEST_PERSIST_INTERVAL_MILLIS: string;
|
|
54
|
+
APIFY_ACTOR_EVENTS_WS_URL: string;
|
|
55
|
+
APIFY_ACTOR_ID: string;
|
|
56
|
+
APIFY_API_BASE_URL: string;
|
|
57
|
+
APIFY_API_PUBLIC_BASE_URL: string;
|
|
58
|
+
APIFY_IS_AT_HOME: string;
|
|
59
|
+
APIFY_ACTOR_RUN_ID: string;
|
|
60
|
+
APIFY_ACTOR_TASK_ID: string;
|
|
61
|
+
APIFY_CONTAINER_PORT: string;
|
|
62
|
+
APIFY_CONTAINER_URL: string;
|
|
63
|
+
APIFY_USER_ID: string;
|
|
64
|
+
APIFY_USER_IS_PAYING: string;
|
|
65
|
+
APIFY_PROXY_HOSTNAME: string;
|
|
66
|
+
APIFY_PROXY_PASSWORD: string;
|
|
67
|
+
APIFY_PROXY_STATUS_URL: string;
|
|
68
|
+
APIFY_PROXY_PORT: string;
|
|
69
|
+
APIFY_INPUT_SECRETS_PRIVATE_KEY_FILE: string;
|
|
70
|
+
APIFY_INPUT_SECRETS_PRIVATE_KEY_PASSPHRASE: string;
|
|
71
|
+
APIFY_META_ORIGIN: string;
|
|
72
|
+
SCRAPELY_TOKEN: string;
|
|
73
|
+
SCRAPELY_API_URL: string;
|
|
74
|
+
SCRAPELY_IS_AT_HOME: string;
|
|
75
|
+
ACTOR_DEFAULT_DATASET_ID: string;
|
|
76
|
+
ACTOR_DEFAULT_KEY_VALUE_STORE_ID: string;
|
|
77
|
+
ACTOR_DEFAULT_REQUEST_QUEUE_ID: string;
|
|
78
|
+
ACTOR_EVENTS_WEBSOCKET_URL: string;
|
|
79
|
+
ACTOR_ID: string;
|
|
80
|
+
ACTOR_INPUT_KEY: string;
|
|
81
|
+
ACTOR_MEMORY_MBYTES: string;
|
|
82
|
+
ACTOR_PERMISSION_LEVEL: string;
|
|
83
|
+
ACTOR_RUN_ID: string;
|
|
84
|
+
ACTOR_STANDBY_PORT: string;
|
|
85
|
+
ACTOR_STANDBY_URL: string;
|
|
86
|
+
ACTOR_TASK_ID: string;
|
|
87
|
+
ACTOR_WEB_SERVER_PORT: string;
|
|
88
|
+
ACTOR_WEB_SERVER_URL: string;
|
|
89
|
+
ACTOR_MAX_TOTAL_CHARGE_USD: string;
|
|
90
|
+
ACTOR_TEST_PAY_PER_EVENT: string;
|
|
91
|
+
ACTOR_USE_CHARGING_LOG_DATASET: string;
|
|
92
|
+
APIFY_ACTOR_PRICING_INFO: string;
|
|
93
|
+
APIFY_CHARGED_ACTOR_EVENT_COUNTS: string;
|
|
94
|
+
ACTOR_STORAGES_JSON: string;
|
|
95
|
+
ACTOR_META_ORIGIN: string;
|
|
96
|
+
};
|
|
97
|
+
protected static INTEGER_VARS: string[];
|
|
98
|
+
protected static BOOLEAN_VARS: string[];
|
|
99
|
+
protected static DEFAULTS: {
|
|
100
|
+
defaultKeyValueStoreId: string;
|
|
101
|
+
defaultDatasetId: string;
|
|
102
|
+
defaultRequestQueueId: string;
|
|
103
|
+
inputKey: string;
|
|
104
|
+
apiBaseUrl: string;
|
|
105
|
+
apiPublicBaseUrl: string;
|
|
106
|
+
proxyStatusUrl: string;
|
|
107
|
+
proxyHostname: string;
|
|
108
|
+
proxyPort: number;
|
|
109
|
+
containerPort: number;
|
|
110
|
+
containerUrl: string;
|
|
111
|
+
standbyPort: number;
|
|
112
|
+
metamorphAfterSleepMillis: number;
|
|
113
|
+
persistStateIntervalMillis: number;
|
|
114
|
+
testPayPerEvent: boolean;
|
|
115
|
+
useChargingLogDataset: boolean;
|
|
116
|
+
};
|
|
117
|
+
get<T extends keyof ConfigurationOptions, U extends ConfigurationOptions[T]>(key: T, defaultValue?: U): U;
|
|
118
|
+
set(key: keyof ConfigurationOptions, value?: any): void;
|
|
119
|
+
static getGlobalConfig(): Configuration;
|
|
120
|
+
static resetGlobalState(): void;
|
|
121
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Configuration = void 0;
|
|
4
|
+
const core_1 = require("@crawlee/core");
|
|
5
|
+
class Configuration extends core_1.Configuration {
|
|
6
|
+
static globalConfig;
|
|
7
|
+
static ENV_MAP = {
|
|
8
|
+
...core_1.Configuration.ENV_MAP,
|
|
9
|
+
APIFY_AVAILABLE_MEMORY_RATIO: 'availableMemoryRatio',
|
|
10
|
+
APIFY_PURGE_ON_START: 'purgeOnStart',
|
|
11
|
+
APIFY_MEMORY_MBYTES: 'memoryMbytes',
|
|
12
|
+
APIFY_DEFAULT_DATASET_ID: 'defaultDatasetId',
|
|
13
|
+
APIFY_DEFAULT_KEY_VALUE_STORE_ID: 'defaultKeyValueStoreId',
|
|
14
|
+
APIFY_DEFAULT_REQUEST_QUEUE_ID: 'defaultRequestQueueId',
|
|
15
|
+
APIFY_INPUT_KEY: 'inputKey',
|
|
16
|
+
APIFY_PERSIST_STATE_INTERVAL_MILLIS: 'persistStateIntervalMillis',
|
|
17
|
+
APIFY_HEADLESS: 'headless',
|
|
18
|
+
APIFY_XVFB: 'xvfb',
|
|
19
|
+
APIFY_CHROME_EXECUTABLE_PATH: 'chromeExecutablePath',
|
|
20
|
+
APIFY_DEFAULT_BROWSER_PATH: 'defaultBrowserPath',
|
|
21
|
+
APIFY_DISABLE_BROWSER_SANDBOX: 'disableBrowserSandbox',
|
|
22
|
+
APIFY_TOKEN: 'token',
|
|
23
|
+
APIFY_METAMORPH_AFTER_SLEEP_MILLIS: 'metamorphAfterSleepMillis',
|
|
24
|
+
APIFY_TEST_PERSIST_INTERVAL_MILLIS: 'persistStateIntervalMillis',
|
|
25
|
+
APIFY_ACTOR_EVENTS_WS_URL: 'actorEventsWsUrl',
|
|
26
|
+
APIFY_ACTOR_ID: 'actorId',
|
|
27
|
+
APIFY_API_BASE_URL: 'apiBaseUrl',
|
|
28
|
+
APIFY_API_PUBLIC_BASE_URL: 'apiPublicBaseUrl',
|
|
29
|
+
APIFY_IS_AT_HOME: 'isAtHome',
|
|
30
|
+
APIFY_ACTOR_RUN_ID: 'actorRunId',
|
|
31
|
+
APIFY_ACTOR_TASK_ID: 'actorTaskId',
|
|
32
|
+
APIFY_CONTAINER_PORT: 'containerPort',
|
|
33
|
+
APIFY_CONTAINER_URL: 'containerUrl',
|
|
34
|
+
APIFY_USER_ID: 'userId',
|
|
35
|
+
APIFY_USER_IS_PAYING: 'userIsPaying',
|
|
36
|
+
APIFY_PROXY_HOSTNAME: 'proxyHostname',
|
|
37
|
+
APIFY_PROXY_PASSWORD: 'proxyPassword',
|
|
38
|
+
APIFY_PROXY_STATUS_URL: 'proxyStatusUrl',
|
|
39
|
+
APIFY_PROXY_PORT: 'proxyPort',
|
|
40
|
+
APIFY_INPUT_SECRETS_PRIVATE_KEY_FILE: 'inputSecretsPrivateKeyFile',
|
|
41
|
+
APIFY_INPUT_SECRETS_PRIVATE_KEY_PASSPHRASE: 'inputSecretsPrivateKeyPassphrase',
|
|
42
|
+
APIFY_META_ORIGIN: 'metaOrigin',
|
|
43
|
+
SCRAPELY_TOKEN: 'token',
|
|
44
|
+
SCRAPELY_API_URL: 'apiBaseUrl',
|
|
45
|
+
SCRAPELY_IS_AT_HOME: 'isAtHome',
|
|
46
|
+
ACTOR_DEFAULT_DATASET_ID: 'defaultDatasetId',
|
|
47
|
+
ACTOR_DEFAULT_KEY_VALUE_STORE_ID: 'defaultKeyValueStoreId',
|
|
48
|
+
ACTOR_DEFAULT_REQUEST_QUEUE_ID: 'defaultRequestQueueId',
|
|
49
|
+
ACTOR_EVENTS_WEBSOCKET_URL: 'actorEventsWsUrl',
|
|
50
|
+
ACTOR_ID: 'actorId',
|
|
51
|
+
ACTOR_INPUT_KEY: 'inputKey',
|
|
52
|
+
ACTOR_MEMORY_MBYTES: 'memoryMbytes',
|
|
53
|
+
ACTOR_PERMISSION_LEVEL: 'actorPermissionLevel',
|
|
54
|
+
ACTOR_RUN_ID: 'actorRunId',
|
|
55
|
+
ACTOR_STANDBY_PORT: 'standbyPort',
|
|
56
|
+
ACTOR_STANDBY_URL: 'standbyUrl',
|
|
57
|
+
ACTOR_TASK_ID: 'actorTaskId',
|
|
58
|
+
ACTOR_WEB_SERVER_PORT: 'containerPort',
|
|
59
|
+
ACTOR_WEB_SERVER_URL: 'containerUrl',
|
|
60
|
+
ACTOR_MAX_TOTAL_CHARGE_USD: 'maxTotalChargeUsd',
|
|
61
|
+
ACTOR_TEST_PAY_PER_EVENT: 'testPayPerEvent',
|
|
62
|
+
ACTOR_USE_CHARGING_LOG_DATASET: 'useChargingLogDataset',
|
|
63
|
+
APIFY_ACTOR_PRICING_INFO: 'actorPricingInfo',
|
|
64
|
+
APIFY_CHARGED_ACTOR_EVENT_COUNTS: 'chargedEventCounts',
|
|
65
|
+
ACTOR_STORAGES_JSON: 'actorStoragesJson',
|
|
66
|
+
ACTOR_META_ORIGIN: 'metaOrigin',
|
|
67
|
+
};
|
|
68
|
+
static INTEGER_VARS = [
|
|
69
|
+
...core_1.Configuration.INTEGER_VARS,
|
|
70
|
+
'proxyPort',
|
|
71
|
+
'containerPort',
|
|
72
|
+
'standbyPort',
|
|
73
|
+
'metamorphAfterSleepMillis',
|
|
74
|
+
'maxTotalChargeUsd',
|
|
75
|
+
];
|
|
76
|
+
static BOOLEAN_VARS = [
|
|
77
|
+
...core_1.Configuration.BOOLEAN_VARS,
|
|
78
|
+
'isAtHome',
|
|
79
|
+
'testPayPerEvent',
|
|
80
|
+
'useChargingLogDataset',
|
|
81
|
+
'userIsPaying',
|
|
82
|
+
];
|
|
83
|
+
static DEFAULTS = {
|
|
84
|
+
...core_1.Configuration.DEFAULTS,
|
|
85
|
+
defaultKeyValueStoreId: 'default',
|
|
86
|
+
defaultDatasetId: 'default',
|
|
87
|
+
defaultRequestQueueId: 'default',
|
|
88
|
+
inputKey: 'INPUT',
|
|
89
|
+
apiBaseUrl: 'https://api-staging.scrape.ly',
|
|
90
|
+
apiPublicBaseUrl: 'https://api-staging.scrape.ly',
|
|
91
|
+
proxyStatusUrl: 'http://proxy.scrape.ly',
|
|
92
|
+
proxyHostname: 'proxy.scrape.ly',
|
|
93
|
+
proxyPort: 8000,
|
|
94
|
+
containerPort: 4321,
|
|
95
|
+
containerUrl: 'http://localhost:4321',
|
|
96
|
+
standbyPort: 4321,
|
|
97
|
+
metamorphAfterSleepMillis: 300e3,
|
|
98
|
+
persistStateIntervalMillis: 60e3,
|
|
99
|
+
testPayPerEvent: false,
|
|
100
|
+
useChargingLogDataset: false,
|
|
101
|
+
};
|
|
102
|
+
get(key, defaultValue) {
|
|
103
|
+
return super.get(key, defaultValue);
|
|
104
|
+
}
|
|
105
|
+
set(key, value) {
|
|
106
|
+
super.set(key, value);
|
|
107
|
+
}
|
|
108
|
+
static getGlobalConfig() {
|
|
109
|
+
if (Configuration.storage.getStore()) {
|
|
110
|
+
return Configuration.storage.getStore();
|
|
111
|
+
}
|
|
112
|
+
Configuration.globalConfig ??= new Configuration();
|
|
113
|
+
return Configuration.globalConfig;
|
|
114
|
+
}
|
|
115
|
+
static resetGlobalState() {
|
|
116
|
+
delete this.globalConfig;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.Configuration = Configuration;
|
|
120
|
+
core_1.Configuration.getGlobalConfig = Configuration.getGlobalConfig;
|
|
121
|
+
// @ts-expect-error protected property
|
|
122
|
+
core_1.Configuration.ENV_MAP = Configuration.ENV_MAP;
|
|
123
|
+
// @ts-expect-error protected property
|
|
124
|
+
core_1.Configuration.INTEGER_VARS = Configuration.INTEGER_VARS;
|
|
125
|
+
// @ts-expect-error protected property
|
|
126
|
+
core_1.Configuration.BOOLEAN_VARS = Configuration.BOOLEAN_VARS;
|
|
127
|
+
// @ts-expect-error protected property
|
|
128
|
+
core_1.Configuration.DEFAULTS = Configuration.DEFAULTS;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the private key file from disk and decrypt it with the run passphrase.
|
|
3
|
+
* The private key file is encrypted with AES-256-GCM using a key derived from the passphrase.
|
|
4
|
+
*
|
|
5
|
+
* @param privateKeyFilePath - Path to the encrypted private key file (e.g., /run/secrets/private_key.pem)
|
|
6
|
+
* @param passphrase - The run passphrase from APIFY_INPUT_SECRETS_PRIVATE_KEY_PASSPHRASE
|
|
7
|
+
* @returns The decrypted private key as a PEM string
|
|
8
|
+
*/
|
|
9
|
+
export declare function decryptPrivateKeyFile(privateKeyFilePath: string, passphrase: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Decrypt a single secret value encrypted with RSA-OAEP.
|
|
12
|
+
*
|
|
13
|
+
* @param encryptedBase64 - Base64-encoded encrypted data
|
|
14
|
+
* @param privateKeyPem - The decrypted private key PEM
|
|
15
|
+
* @returns The decrypted plaintext value
|
|
16
|
+
*/
|
|
17
|
+
export declare function decryptSecretValue(encryptedBase64: string, privateKeyPem: string): any;
|
|
18
|
+
/**
|
|
19
|
+
* Decrypt all secret values from the encrypted input payload.
|
|
20
|
+
*
|
|
21
|
+
* @param encryptedSecrets - Map of field name → base64 encrypted value
|
|
22
|
+
* @param privateKeyPem - The decrypted private key PEM
|
|
23
|
+
* @returns Map of field name → decrypted value
|
|
24
|
+
*/
|
|
25
|
+
export declare function decryptAllSecrets(encryptedSecrets: Record<string, string>, privateKeyPem: string): Record<string, any>;
|
|
26
|
+
/**
|
|
27
|
+
* Merge decrypted secret values into the original input object.
|
|
28
|
+
* Supports nested paths like "credentials.apiKey" or "database.password".
|
|
29
|
+
*
|
|
30
|
+
* @param input - The original input object (with secrets stripped/masked)
|
|
31
|
+
* @param decryptedSecrets - Map of field name → decrypted value
|
|
32
|
+
* @returns A new input object with secrets populated
|
|
33
|
+
*/
|
|
34
|
+
export declare function mergeDecryptedInput(input: Record<string, any>, decryptedSecrets: Record<string, any>): Record<string, any>;
|
|
35
|
+
/**
|
|
36
|
+
* Full decryption flow: read private key from file, decrypt it with passphrase,
|
|
37
|
+
* then decrypt all secrets and merge into input.
|
|
38
|
+
*
|
|
39
|
+
* This is called from Actor.init() when APIFY_INPUT_SECRETS_PRIVATE_KEY_FILE is set.
|
|
40
|
+
*
|
|
41
|
+
* @param input - Current actor input object
|
|
42
|
+
* @returns The input object with decrypted secret values merged in
|
|
43
|
+
*/
|
|
44
|
+
export declare function decryptSecretInputs(input: Record<string, any>): Record<string, any>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.decryptPrivateKeyFile = decryptPrivateKeyFile;
|
|
7
|
+
exports.decryptSecretValue = decryptSecretValue;
|
|
8
|
+
exports.decryptAllSecrets = decryptAllSecrets;
|
|
9
|
+
exports.mergeDecryptedInput = mergeDecryptedInput;
|
|
10
|
+
exports.decryptSecretInputs = decryptSecretInputs;
|
|
11
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
14
|
+
const RSA_PADDING = crypto_1.default.constants.RSA_PKCS1_OAEP_PADDING;
|
|
15
|
+
const RSA_OAEP_HASH = 'sha256';
|
|
16
|
+
const AES_ALGORITHM = 'aes-256-gcm';
|
|
17
|
+
const IV_LENGTH = 12;
|
|
18
|
+
const AUTH_TAG_LENGTH = 16;
|
|
19
|
+
const PBKDF2_ITERATIONS = 100_000;
|
|
20
|
+
const PBKDF2_HASH = 'sha256';
|
|
21
|
+
const SALT_LENGTH = 32;
|
|
22
|
+
const KEY_LENGTH = 32;
|
|
23
|
+
// ─── Private Key Decryption ───────────────────────────────────────────────────
|
|
24
|
+
/**
|
|
25
|
+
* Read the private key file from disk and decrypt it with the run passphrase.
|
|
26
|
+
* The private key file is encrypted with AES-256-GCM using a key derived from the passphrase.
|
|
27
|
+
*
|
|
28
|
+
* @param privateKeyFilePath - Path to the encrypted private key file (e.g., /run/secrets/private_key.pem)
|
|
29
|
+
* @param passphrase - The run passphrase from APIFY_INPUT_SECRETS_PRIVATE_KEY_PASSPHRASE
|
|
30
|
+
* @returns The decrypted private key as a PEM string
|
|
31
|
+
*/
|
|
32
|
+
function decryptPrivateKeyFile(privateKeyFilePath, passphrase) {
|
|
33
|
+
const encryptedData = (0, fs_1.readFileSync)(privateKeyFilePath);
|
|
34
|
+
const salt = encryptedData.subarray(0, SALT_LENGTH);
|
|
35
|
+
const iv = encryptedData.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
|
|
36
|
+
const authTag = encryptedData.subarray(SALT_LENGTH + IV_LENGTH, SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
37
|
+
const encrypted = encryptedData.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
38
|
+
// Derive key from passphrase
|
|
39
|
+
const key = crypto_1.default.pbkdf2Sync(passphrase, salt, PBKDF2_ITERATIONS, KEY_LENGTH, PBKDF2_HASH);
|
|
40
|
+
// Decrypt
|
|
41
|
+
const decipher = crypto_1.default.createDecipheriv(AES_ALGORITHM, key, iv);
|
|
42
|
+
decipher.setAuthTag(authTag);
|
|
43
|
+
const decrypted = Buffer.concat([
|
|
44
|
+
decipher.update(encrypted),
|
|
45
|
+
decipher.final(),
|
|
46
|
+
]);
|
|
47
|
+
return decrypted.toString('utf8');
|
|
48
|
+
}
|
|
49
|
+
// ─── Secret Value Decryption ──────────────────────────────────────────────────
|
|
50
|
+
/**
|
|
51
|
+
* Decrypt a single secret value encrypted with RSA-OAEP.
|
|
52
|
+
*
|
|
53
|
+
* @param encryptedBase64 - Base64-encoded encrypted data
|
|
54
|
+
* @param privateKeyPem - The decrypted private key PEM
|
|
55
|
+
* @returns The decrypted plaintext value
|
|
56
|
+
*/
|
|
57
|
+
function decryptSecretValue(encryptedBase64, privateKeyPem) {
|
|
58
|
+
const encrypted = Buffer.from(encryptedBase64, 'base64');
|
|
59
|
+
const decryptedData = crypto_1.default.privateDecrypt({
|
|
60
|
+
key: privateKeyPem,
|
|
61
|
+
padding: RSA_PADDING,
|
|
62
|
+
oaepHash: RSA_OAEP_HASH,
|
|
63
|
+
}, encrypted);
|
|
64
|
+
const plaintext = decryptedData.toString('utf8');
|
|
65
|
+
// Try to parse as JSON (for objects, arrays, numbers, booleans)
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(plaintext);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Return as plain string
|
|
71
|
+
return plaintext;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Decrypt all secret values from the encrypted input payload.
|
|
76
|
+
*
|
|
77
|
+
* @param encryptedSecrets - Map of field name → base64 encrypted value
|
|
78
|
+
* @param privateKeyPem - The decrypted private key PEM
|
|
79
|
+
* @returns Map of field name → decrypted value
|
|
80
|
+
*/
|
|
81
|
+
function decryptAllSecrets(encryptedSecrets, privateKeyPem) {
|
|
82
|
+
const decrypted = {};
|
|
83
|
+
for (const [fieldName, encryptedBase64] of Object.entries(encryptedSecrets)) {
|
|
84
|
+
try {
|
|
85
|
+
decrypted[fieldName] = decryptSecretValue(encryptedBase64, privateKeyPem);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.warn(`[Scrapely] Failed to decrypt secret field "${fieldName}": ${err.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return decrypted;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Merge decrypted secret values into the original input object.
|
|
95
|
+
* Supports nested paths like "credentials.apiKey" or "database.password".
|
|
96
|
+
*
|
|
97
|
+
* @param input - The original input object (with secrets stripped/masked)
|
|
98
|
+
* @param decryptedSecrets - Map of field name → decrypted value
|
|
99
|
+
* @returns A new input object with secrets populated
|
|
100
|
+
*/
|
|
101
|
+
function mergeDecryptedInput(input, decryptedSecrets) {
|
|
102
|
+
const merged = { ...input };
|
|
103
|
+
for (const [fieldName, value] of Object.entries(decryptedSecrets)) {
|
|
104
|
+
if (fieldName.includes('.')) {
|
|
105
|
+
// Handle nested paths
|
|
106
|
+
const parts = fieldName.split('.');
|
|
107
|
+
let current = merged;
|
|
108
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
109
|
+
if (!current[parts[i]] || typeof current[parts[i]] !== 'object') {
|
|
110
|
+
current[parts[i]] = {};
|
|
111
|
+
}
|
|
112
|
+
current = current[parts[i]];
|
|
113
|
+
}
|
|
114
|
+
current[parts[parts.length - 1]] = value;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
merged[fieldName] = value;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return merged;
|
|
121
|
+
}
|
|
122
|
+
// ─── Full Decryption Flow ─────────────────────────────────────────────────────
|
|
123
|
+
/**
|
|
124
|
+
* Full decryption flow: read private key from file, decrypt it with passphrase,
|
|
125
|
+
* then decrypt all secrets and merge into input.
|
|
126
|
+
*
|
|
127
|
+
* This is called from Actor.init() when APIFY_INPUT_SECRETS_PRIVATE_KEY_FILE is set.
|
|
128
|
+
*
|
|
129
|
+
* @param input - Current actor input object
|
|
130
|
+
* @returns The input object with decrypted secret values merged in
|
|
131
|
+
*/
|
|
132
|
+
function decryptSecretInputs(input) {
|
|
133
|
+
const privateKeyFile = process.env.APIFY_INPUT_SECRETS_PRIVATE_KEY_FILE;
|
|
134
|
+
const passphrase = process.env.APIFY_INPUT_SECRETS_PRIVATE_KEY_PASSPHRASE;
|
|
135
|
+
if (!privateKeyFile || !passphrase) {
|
|
136
|
+
return input; // No secrets to decrypt
|
|
137
|
+
}
|
|
138
|
+
console.log('[Scrapely] Decrypting secret inputs...');
|
|
139
|
+
try {
|
|
140
|
+
// 1. Read and decrypt the private key file
|
|
141
|
+
const privateKeyPem = decryptPrivateKeyFile(privateKeyFile, passphrase);
|
|
142
|
+
console.log('[Scrapely] Private key decrypted successfully');
|
|
143
|
+
// 2. Get the encrypted secrets from the input
|
|
144
|
+
// The encrypted secrets are stored in a special __encrypted property
|
|
145
|
+
const encryptedSecrets = input.__encrypted_secrets;
|
|
146
|
+
if (!encryptedSecrets || Object.keys(encryptedSecrets).length === 0) {
|
|
147
|
+
console.log('[Scrapely] No encrypted secrets found in input');
|
|
148
|
+
return input;
|
|
149
|
+
}
|
|
150
|
+
// 3. Decrypt all secret values
|
|
151
|
+
const decryptedSecrets = decryptAllSecrets(encryptedSecrets, privateKeyPem);
|
|
152
|
+
console.log(`[Scrapely] Decrypted ${Object.keys(decryptedSecrets).length} secret field(s)`);
|
|
153
|
+
// 4. Merge decrypted values into input and remove the __encrypted_secrets key
|
|
154
|
+
const merged = mergeDecryptedInput(input, decryptedSecrets);
|
|
155
|
+
delete merged.__encrypted_secrets;
|
|
156
|
+
delete merged.__encrypted_secret_fields;
|
|
157
|
+
return merged;
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
console.error(`[Scrapely] Failed to decrypt secret inputs: ${err.message}`);
|
|
161
|
+
// Return input as-is - secrets won't be available but actor can still run
|
|
162
|
+
return input;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
/**
|
|
3
|
+
* System event received from the Scrapely platform via WebSocket.
|
|
4
|
+
* Matches the format documented for ACTOR_EVENTS_WEBSOCKET_URL.
|
|
5
|
+
*/
|
|
6
|
+
export interface SystemEvent {
|
|
7
|
+
name: 'systemInfo' | 'cpuInfo' | 'aborting' | 'persistState' | 'migrating';
|
|
8
|
+
createdAt: string;
|
|
9
|
+
data: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface SystemInfoData {
|
|
12
|
+
createdAt: string;
|
|
13
|
+
isCpuOverloaded: boolean;
|
|
14
|
+
cpuCurrentUsage: number;
|
|
15
|
+
cpuAvgUsage: number;
|
|
16
|
+
cpuMaxUsage: number;
|
|
17
|
+
memCurrentBytes: number;
|
|
18
|
+
memAvgBytes: number;
|
|
19
|
+
memMaxBytes: number;
|
|
20
|
+
}
|
|
21
|
+
export interface CpuInfoData {
|
|
22
|
+
isCpuOverloaded: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface PersistStateData {
|
|
25
|
+
isMigrating: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare const events: EventEmitter<any>;
|
|
28
|
+
/**
|
|
29
|
+
* Connect to the Scrapely events WebSocket server.
|
|
30
|
+
* Called during Actor.init().
|
|
31
|
+
*
|
|
32
|
+
* @param eventsWsUrl - The ACTOR_EVENTS_WEBSOCKET_URL env var value
|
|
33
|
+
* @param token - The SCRAPELY_TOKEN for authentication
|
|
34
|
+
* @param runId - The ACTOR_RUN_ID
|
|
35
|
+
*/
|
|
36
|
+
export declare function connectEvents(eventsWsUrl: string, token: string, runId: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Disconnect from the events WebSocket server.
|
|
39
|
+
* Called during Actor.exit().
|
|
40
|
+
*/
|
|
41
|
+
export declare function disconnectEvents(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Check if the events WebSocket is connected.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isEventsConnected(): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Subscribe to system events.
|
|
48
|
+
* Use in conjunction with Actor.on() to receive typed events.
|
|
49
|
+
*/
|
|
50
|
+
export declare function onSystemEvent(listener: (event: SystemEvent) => void): void;
|
|
51
|
+
/**
|
|
52
|
+
* Subscribe to specific system event types.
|
|
53
|
+
*/
|
|
54
|
+
export declare function onEvent(name: SystemEvent['name'], listener: (data: Record<string, unknown>) => void): void;
|
|
55
|
+
/**
|
|
56
|
+
* Remove a specific event listener.
|
|
57
|
+
*/
|
|
58
|
+
export declare function offEvent(name: SystemEvent['name'], listener: (...args: any[]) => void): void;
|
|
59
|
+
export { events };
|