diff --git a/docs/02_concepts/02_error-handling.md b/docs/02_concepts/02_error-handling.md
index 515de370d..415437f28 100644
--- a/docs/02_concepts/02_error-handling.md
+++ b/docs/02_concepts/02_error-handling.md
@@ -160,6 +160,7 @@ const client = new ApifyClient({
token: 'MY-APIFY-TOKEN',
maxRetries: 8,
minDelayBetweenRetriesMillis: 500, // 0.5s
- timeoutSecs: 360, // 6 mins
});
```
+
+A request that times out is retried the same way, with a longer timeout on each attempt. For how the timeouts are set, see [Timeouts](./06_timeouts.md).
diff --git a/docs/02_concepts/06_timeouts.md b/docs/02_concepts/06_timeouts.md
new file mode 100644
index 000000000..5f24e6042
--- /dev/null
+++ b/docs/02_concepts/06_timeouts.md
@@ -0,0 +1,66 @@
+---
+id: timeouts
+title: Timeouts
+sidebar_label: Timeouts
+description: 'Configure the tiered timeouts that bound how long API requests made by the Apify API client for JavaScript can take.'
+---
+
+import ApiLink from '@theme/ApiLink';
+
+The client gives every API request a timeout from one of three tiers, each with a default duration suited to the kind of request it covers. Methods that poll for a job to finish run without one:
+
+| Tier | Default | Purpose |
+| --- | --- | --- |
+| `short` | 5 seconds | Metadata reads and writes (`get()`, `update()`, `delete()`) |
+| `medium` | 30 seconds | Listing, batch and trigger operations (`list()`, `start()`, `batchAddRequests()`) |
+| `long` | 360 seconds | Downloads, uploads and streaming (`listItems()`, `setRecord()`, `log().get()`) |
+| `noTimeout` | none | Polling that waits for a job to finish (`call()`, `waitForFinish()`) |
+
+Every client method is assigned the tier that matches the expected duration of its request. The reference of each method names its tier. You don't need to change the tiers unless you work with unusually large payloads or a slow network.
+
+The client never aborts a request that runs without a timeout, so a connection that stalls is not retried either. `ActorClient.call()` and `RunClient.waitForFinish()` poll until the job ends; an explicit `timeoutSecs` bounds the requests they send, and has to leave room for the minute the API may hold each poll.
+
+Methods such as `ActorClient.start()` and `RunClient.get()` take a `waitForFinish` parameter, which asks the API to hold the response until the job finishes, for up to a minute. Such a request gets the requested wait on top of its tier, so the client doesn't abort it while the API is still holding it.
+
+## Configuring the tiers
+
+Set the duration of each tier on the `ApifyClient` constructor. The `timeoutMaxSecs` option caps the timeout of any single request attempt. It bounds the growth of the timeout across retries, and it caps tier and per-call timeouts alike, so raise it whenever you need a request timeout longer than the default 360 seconds. A tier configured above the cap is capped too, and the client logs a warning to make the cut-off visible.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+ timeoutShortSecs: 10,
+ timeoutMediumSecs: 60,
+ timeoutLongSecs: 600,
+ timeoutMaxSecs: 600,
+});
+```
+
+A single request queue client can be held to a shorter budget with `client.requestQueue(id, { timeoutSecs })`, which caps the default tier of every request that client sends. A per-call `timeoutSecs` is not capped by it.
+
+## Per-call overrides
+
+Every method that sends a request accepts a `timeoutSecs` option, which replaces the tier of the method for that call. Pass a number of seconds for an exact duration, a tier name to switch tiers, or `'noTimeout'` to let the request run for as long as it takes. For the type, see `TimeoutOptions`.
+
+```js
+const datasetClient = client.dataset('my-dataset-id');
+
+// An exact timeout for this call.
+const { items } = await datasetClient.listItems({ timeoutSecs: 120 });
+
+// Another tier.
+const dataset = await datasetClient.get({ timeoutSecs: 'long' });
+
+// No timeout at all.
+await datasetClient.pushItems(items, { timeoutSecs: 'noTimeout' });
+```
+
+A number above `timeoutMaxSecs` is capped at it, and the client logs a warning. To let such a call use its full timeout, raise `timeoutMaxSecs` in the client constructor.
+
+Methods that start an Actor run keep the API's run timeout apart from the request timeout. The `runTimeoutSecs` option of `start()`, `call()` and `resurrect()` bounds how long the run may execute on the platform, while `timeoutSecs` bounds the request that starts it.
+
+## Interaction with retries
+
+Timeouts work together with [retries](./02_error-handling.md#retries-with-exponential-backoff). A request that times out counts as a failed attempt and is retried, up to `maxRetries` times. The timeout applies to each attempt on its own, and doubles with every retry up to `timeoutMaxSecs`, so a request that timed out at 5 seconds gets 10 seconds on the second attempt and 20 on the third.
diff --git a/docs/04_upgrading/upgrading_v3.md b/docs/04_upgrading/upgrading_v3.md
index f4ed5982b..fd68e85f8 100644
--- a/docs/04_upgrading/upgrading_v3.md
+++ b/docs/04_upgrading/upgrading_v3.md
@@ -70,8 +70,8 @@ await client.actor('my-actor').update([{ name: 'my-actor' }]);
`ow` only checked the type, so `Infinity` passed as a number and an invalid `Date` passed as a date. Zod additionally requires a *finite* number and a *valid* date, so both now throw:
```js
-// Now throws: Invalid input: expected a finite number at `timeout`, got `Infinity`
-await client.actor('my-actor').call(undefined, { timeout: Infinity });
+// Now throws: Invalid input: expected a finite number at `runTimeoutSecs`, got `Infinity`
+await client.actor('my-actor').call(undefined, { runTimeoutSecs: Infinity });
// Now throws: Invalid input: expected a valid date at `startedBefore`
// Invalid input: expected string, received Date at `startedBefore`
@@ -80,7 +80,7 @@ await client.actor('my-actor').runs().list({ startedBefore: new Date('nonsense')
The second example reports a line per arm, because `startedBefore` accepts either a `Date` or a string.
-This affects numeric options such as `waitSecs`, `timeout` and `memory`, and date options such as `startedBefore` / `startedAfter`. `KeyValueStoreClient.setRecord()` rejects `NaN` and `Infinity` as a record value too, since `JSON.stringify()` turns both into `null`.
+This affects numeric options such as `waitSecs`, `runTimeoutSecs` and `memory`, and date options such as `startedBefore` / `startedAfter`. `KeyValueStoreClient.setRecord()` rejects `NaN` and `Infinity` as a record value too, since `JSON.stringify()` turns both into `null`.
### A few always-rejected options are gone from the types
@@ -268,9 +268,45 @@ A trailing slash appears only on a field the API returns without a path, so on `
A URL field whose value isn't a valid absolute URL now fails response validation and throws `ResponseValidationError`, the same as any other field that doesn't match the specification.
-## `versions().list()` and `envVars().list()` take no options
+## Timeouts are configured per tier
-`ActorVersionCollectionClient.list()` and `ActorEnvVarCollectionClient.list()` now take no arguments. Neither endpoint reads `offset`, `limit` or `desc`, and both return every item in one response, so `chunkSize` had nothing to size either. The `ActorVersionCollectionListOptions` and `ActorEnvVarCollectionListOptions` types that declared those four options, deprecated since v2.21.0, are gone from the package. A call that passed an options object no longer compiles. Drop the argument and the call returns the same items as before.
+The `timeoutSecs` option of the `ApifyClient` constructor is gone. Every method takes its timeout from one of three tiers, and the constructor sets the duration of each tier, along with a cap on any single request attempt:
+
+```diff
+- const client = new ApifyClient({ token: 'MY-APIFY-TOKEN', timeoutSecs: 360 }); // v2
++ const client = new ApifyClient({ // v3
++ token: 'MY-APIFY-TOKEN',
++ timeoutShortSecs: 5,
++ timeoutMediumSecs: 30,
++ timeoutLongSecs: 360,
++ timeoutMaxSecs: 360,
++ });
+```
+
+For what each tier covers and which one a method is assigned, see [Timeouts](../02_concepts/06_timeouts.md).
+
+In v2 only the dataset, key-value store and request queue clients picked a timeout per method, and everything else ran with the global 360 seconds. A metadata call such as `actor.get()` now gets the 5 seconds of the `short` tier and a `list()` call the 30 of `medium`, so a call that used to wait out a slow API can fail sooner. `RequestQueueClient.unlockRequests()` moves the other way, from 30 seconds to 360. Where you need a different duration, every method that sends a request takes a `timeoutSecs` option, which replaces the tier of the method for that call:
+
+```js
+await client.actor('my-actor').get({ timeoutSecs: 'long' });
+```
+
+`timeoutSecs` means the request timeout on every method, so the run timeout of `ActorClient.start()` and `call()`, `TaskClient.start()` and `call()`, and `RunClient.resurrect()` is renamed:
+
+```diff
+- await client.actor('my-actor').call(input, { timeout: 300 }); // v2
++ await client.actor('my-actor').call(input, { runTimeoutSecs: 300 }); // v3
+```
+
+TypeScript reports a leftover `timeout` at compile time, and in JavaScript the option schemas reject it as an unrecognized key, so the call throws an `ArgumentValidationError` instead of silently dropping the run limit. `{ timeout: 0 }`, which asked for an unlimited run in v2, becomes `{ runTimeoutSecs: 0 }`.
+
+The `timeoutSecs` option of `KeyValueStoreClient.setRecord()` keeps its name and its meaning for a number of seconds, and now takes a tier name or `'noTimeout'` as well. The same goes for `client.requestQueue(id, { timeoutSecs })`, which caps the tier of every request that queue client sends rather than timing each one at the given value.
+
+`client.httpClient.call()` takes `timeoutSecs` where it read the axios `timeout` in milliseconds. A direct call that passed `timeout: 30000` has to become `timeoutSecs: 30`.
+
+## `versions().list()` and `envVars().list()` lose their pagination options
+
+`ActorVersionCollectionClient.list()` and `ActorEnvVarCollectionClient.list()` accept only the `timeoutSecs` option. Neither endpoint reads `offset`, `limit` or `desc`, and both return every item in one response, so `chunkSize` had nothing to size either. The `ActorVersionCollectionListOptions` and `ActorEnvVarCollectionListOptions` types that declared those four options, deprecated since v2.21.0, are gone from the package. A call that passed any of them no longer compiles, and throws an `ArgumentValidationError` about an unrecognized key in JavaScript. Drop them and the call returns the same items as before.
## `limit` counts scanned rows when iterating `listItems()`
diff --git a/docs/public-api/apify-client.api.md b/docs/public-api/apify-client.api.md
index 5f9a7b356..e47be349d 100644
--- a/docs/public-api/apify-client.api.md
+++ b/docs/public-api/apify-client.api.md
@@ -52,7 +52,7 @@ export interface Actor extends Omit, Act
}
// @public
-export interface ActorBuildOptions {
+export interface ActorBuildOptions extends TimeoutOptions {
// (undocumented)
betaPackages?: boolean;
// (undocumented)
@@ -90,12 +90,12 @@ export class ActorClient extends ResourceClient {
builds(): BuildCollectionClient;
call(input?: ActorInput, options?: ActorCallOptions): Promise;
defaultBuild(options?: BuildClientGetOptions): Promise;
- delete(): Promise;
- get(): Promise;
+ delete(options?: TimeoutOptions): Promise;
+ get(options?: TimeoutOptions): Promise;
lastRun(options?: ActorLastRunOptions): RunClient;
runs(): RunCollectionClient;
start(input?: ActorInput, options?: ActorStartOptions): Promise;
- update(newFields: ActorUpdateOptions): Promise;
+ update(newFields: ActorUpdateOptions, options?: TimeoutOptions): Promise;
validateInput(input?: ActorInput, options?: ActorValidateInputOptions): Promise;
version(versionNumber: string): ActorVersionClient;
versions(): ActorVersionCollectionClient;
@@ -105,7 +105,7 @@ export class ActorClient extends ResourceClient {
// @public
export class ActorCollectionClient extends ResourceCollectionClient {
constructor(options: ApiClientSubResourceOptions);
- create(actor: ActorCollectionCreateOptions): Promise;
+ create(actor: ActorCollectionCreateOptions, options?: TimeoutOptions): Promise;
list(options?: ActorCollectionListOptions): PaginatedIterator;
}
@@ -145,7 +145,7 @@ interface ActorCollectionListItemRePointed {
}
// @public (undocumented)
-export interface ActorCollectionListOptions extends PaginationOptions {
+export interface ActorCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
// (undocumented)
@@ -184,16 +184,16 @@ export interface ActorEnvironmentVariable extends GeneratedEnvVar {
// @public
export class ActorEnvVarClient extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
- delete(): Promise;
- get(): Promise;
- update(actorEnvVar: ActorEnvironmentVariable): Promise;
+ delete(options?: TimeoutOptions): Promise;
+ get(options?: TimeoutOptions): Promise;
+ update(actorEnvVar: ActorEnvironmentVariable, options?: TimeoutOptions): Promise;
}
// @public
export class ActorEnvVarCollectionClient extends ResourceCollectionClient {
constructor(options: ApiClientSubResourceOptions);
- create(actorEnvVar: ActorEnvironmentVariable): Promise;
- list(): Promise & AsyncIterable;
+ create(actorEnvVar: ActorEnvironmentVariable, options?: TimeoutOptions): Promise;
+ list(options?: TimeoutOptions): Promise & AsyncIterable;
}
// @public
@@ -355,7 +355,7 @@ export interface ActorStandby extends GeneratedActorStandby {
}
// @public (undocumented)
-export interface ActorStartOptions {
+export interface ActorStartOptions extends TimeoutOptions {
build?: string;
contentType?: string;
forcePermissionLevel?: ACTOR_PERMISSION_LEVEL;
@@ -363,7 +363,7 @@ export interface ActorStartOptions {
maxTotalChargeUsd?: number;
memory?: number;
restartOnError?: boolean;
- timeout?: number;
+ runTimeoutSecs?: number;
waitForFinish?: number;
webhooks?: readonly WebhookUpdateData[];
}
@@ -396,7 +396,7 @@ export type ActorTaggedBuilds = Record;
export type ActorUpdateOptions = Partial>;
// @public
-export interface ActorValidateInputOptions {
+export interface ActorValidateInputOptions extends TimeoutOptions {
build?: string;
contentType?: string;
}
@@ -407,11 +407,11 @@ export type ActorVersion = ActorVersionSourceFiles | ActorVersionGitRepo | Actor
// @public
export class ActorVersionClient extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
- delete(): Promise;
+ delete(options?: TimeoutOptions): Promise;
envVar(envVarName: string): ActorEnvVarClient;
envVars(): ActorEnvVarCollectionClient;
- get(): Promise;
- update(newFields: ActorVersionUpdateData): Promise;
+ get(options?: TimeoutOptions): Promise;
+ update(newFields: ActorVersionUpdateData, options?: TimeoutOptions): Promise;
}
// Not exported by the entry point; reachable only as a referenced type.
@@ -424,8 +424,8 @@ interface ActorVersionClientNarrowings {
// @public
export class ActorVersionCollectionClient extends ResourceCollectionClient {
constructor(options: ApiClientSubResourceOptions);
- create(actorVersion: ActorVersion): Promise;
- list(): Promise & AsyncIterable;
+ create(actorVersion: ActorVersion, options?: TimeoutOptions): Promise;
+ list(options?: TimeoutOptions): Promise & AsyncIterable;
}
// @public
@@ -493,29 +493,29 @@ abstract class ApiClient {
// (undocumented)
baseUrl: string;
// (undocumented)
+ protected buildParams(endpointParams?: T): Record;
+ // (undocumented)
+ protected buildPublicUrl(path?: string | string[]): string;
+ // (undocumented)
+ protected buildUrl(path?: string | string[]): string;
+ // (undocumented)
httpClient: HttpClient;
// (undocumented)
id?: string;
- protected _listPaginatedFromCallback>(getPaginatedList: (options?: T) => Promise, options?: T): AsyncIterable & Promise;
+ protected listPaginatedFromCallback>(getPaginatedList: (options?: T) => Promise, options?: T): AsyncIterable & Promise;
// (undocumented)
params?: Record;
- // (undocumented)
- protected _params(endpointParams?: T): Record;
publicBaseUrl: string;
// (undocumented)
- protected _publicUrl(path?: string | string[]): string;
- // (undocumented)
resourcePath: string;
// (undocumented)
safeId?: string;
// (undocumented)
- protected _subResourceOptions(moreOptions?: T): BaseOptions & T;
+ protected subResourceOptions(moreOptions?: T): BaseOptions & T;
// (undocumented)
- protected _toSafeId(id: string): string;
+ protected toSafeId(id: string): string;
// (undocumented)
url: string;
- // (undocumented)
- protected _url(path?: string | string[]): string;
}
// Not exported by the entry point; reachable only as a referenced type.
@@ -615,7 +615,10 @@ export interface ApifyClientOptions {
minDelayBetweenRetriesMillis?: number;
publicBaseUrl?: string;
requestInterceptors?: RequestInterceptorFunction[];
- timeoutSecs?: number;
+ timeoutLongSecs?: number;
+ timeoutMaxSecs?: number;
+ timeoutMediumSecs?: number;
+ timeoutShortSecs?: number;
// (undocumented)
token?: string;
userAgentSuffix?: string | string[];
@@ -623,13 +626,14 @@ export interface ApifyClientOptions {
// Not exported by the entry point; reachable only as a referenced type.
// @public (undocumented)
-interface ApifyRequestConfig extends AxiosRequestConfig {
+interface ApifyRequestConfig extends Omit {
// (undocumented)
doNotRetryTimeouts?: boolean;
// (undocumented)
forceBuffer?: boolean;
// (undocumented)
stringifyFunctions?: boolean;
+ timeoutSecs?: Timeout;
}
// Not exported by the entry point; reachable only as a referenced type.
@@ -669,22 +673,22 @@ export interface Build extends Omit, Bui
// @public
export class BuildClient extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
- abort(): Promise;
- delete(): Promise;
+ abort(options?: TimeoutOptions): Promise;
+ delete(options?: TimeoutOptions): Promise;
get(options?: BuildClientGetOptions): Promise;
- getOpenApiDefinition(): Promise;
+ getOpenApiDefinition(options?: TimeoutOptions): Promise;
log(): LogClient;
waitForFinish(options?: BuildClientWaitForFinishOptions): Promise;
}
// @public
-export interface BuildClientGetOptions {
+export interface BuildClientGetOptions extends TimeoutOptions {
// (undocumented)
waitForFinish?: number;
}
// @public
-export interface BuildClientWaitForFinishOptions {
+export interface BuildClientWaitForFinishOptions extends TimeoutOptions {
waitSecs?: number;
}
@@ -708,7 +712,7 @@ interface BuildCollectionClientListItemRePointed {
}
// @public (undocumented)
-export interface BuildCollectionClientListOptions extends PaginationOptions {
+export interface BuildCollectionClientListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
}
@@ -2421,13 +2425,13 @@ export interface Dataset extends Omit = Record> extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
createItemsPublicUrl(options?: DatasetClientCreateItemsUrlOptions): Promise;
- delete(): Promise;
+ delete(options?: TimeoutOptions): Promise;
downloadItems(format: `${DownloadItemsFormat}`, options?: DatasetClientDownloadItemsOptions): Promise;
- get(): Promise;
- getStatistics(): Promise;
+ get(options?: TimeoutOptions): Promise;
+ getStatistics(options?: TimeoutOptions): Promise;
listItems(options?: DatasetClientListItemOptions): PaginatedIterator;
- pushItems(items: Data | Data[] | string | string[]): Promise;
- update(newFields: DatasetClientUpdateOptions): Promise;
+ pushItems(items: Data | Data[] | string | string[], options?: TimeoutOptions): Promise;
+ update(newFields: DatasetClientUpdateOptions, options?: TimeoutOptions): Promise;
}
// @public
@@ -2453,7 +2457,7 @@ export interface DatasetClientDownloadItemsOptions extends Omit;
}
// @public (undocumented)
-export interface DatasetCollectionClientListOptions extends PaginationOptions {
+export interface DatasetCollectionClientListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
ownership?: STORAGE_OWNERSHIP_FILTER;
@@ -2838,7 +2842,7 @@ type GeneratedWebhookDispatchEventData = NonNullable;
// (undocumented)
userProvidedRequestInterceptors: RequestInterceptorFunction[];
// (undocumented)
@@ -2887,7 +2891,13 @@ interface HttpClientOptions {
// (undocumented)
requestInterceptors: RequestInterceptorFunction[];
// (undocumented)
- timeoutSecs: number;
+ timeoutLongSecs: number;
+ // (undocumented)
+ timeoutMaxSecs: number;
+ // (undocumented)
+ timeoutMediumSecs: number;
+ // (undocumented)
+ timeoutShortSecs: number;
// (undocumented)
token?: string;
// (undocumented)
@@ -2916,7 +2926,7 @@ export interface KeyValueClientCreateKeysUrlOptions extends Omit;
- delete(): Promise;
- deleteRecord(key: string): Promise;
- get(): Promise;
+ delete(options?: TimeoutOptions): Promise;
+ deleteRecord(key: string, options?: TimeoutOptions): Promise;
+ get(options?: TimeoutOptions): Promise;
getRecord(key: string): Promise | undefined>;
// (undocumented)
getRecord(key: string, options: Options): Promise> | undefined>;
- getRecordPublicUrl(key: string): Promise;
+ getRecordPublicUrl(key: string, options?: TimeoutOptions): Promise;
listKeys(options?: KeyValueClientListKeysOptions): Promise & AsyncIterable;
- recordExists(key: string): Promise;
+ recordExists(key: string, options?: TimeoutOptions): Promise;
setRecord(record: KeyValueStoreRecord, options?: KeyValueStoreRecordOptions): Promise;
- update(newFields: KeyValueClientUpdateOptions): Promise;
+ update(newFields: KeyValueClientUpdateOptions, options?: TimeoutOptions): Promise;
}
// @public
@@ -2988,13 +2998,13 @@ export class KeyValueStoreCollectionClient extends ResourceCollectionClient {
}
// @public
-export interface KeyValueStoreCollectionClientGetOrCreateOptions {
+export interface KeyValueStoreCollectionClientGetOrCreateOptions extends TimeoutOptions {
// (undocumented)
schema?: Record;
}
// @public (undocumented)
-export interface KeyValueStoreCollectionClientListOptions extends PaginationOptions {
+export interface KeyValueStoreCollectionClientListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
ownership?: STORAGE_OWNERSHIP_FILTER;
@@ -3018,11 +3028,9 @@ export interface KeyValueStoreRecord {
}
// @public
-export interface KeyValueStoreRecordOptions {
+export interface KeyValueStoreRecordOptions extends TimeoutOptions {
// (undocumented)
doNotRetryTimeouts?: boolean;
- // (undocumented)
- timeoutSecs?: number;
}
// @public
@@ -3074,7 +3082,7 @@ export class LoggerActorRedirect extends Logger {
}
// @public
-export interface LogOptions {
+export interface LogOptions extends TimeoutOptions {
raw?: boolean;
}
@@ -3274,28 +3282,28 @@ export interface RequestQueue extends Omit;
- batchAddRequests(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise;
- protected _batchAddRequests(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientAddRequestOptions): Promise;
+ protected addRequestBatch(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientAddRequestOptions): Promise;
// (undocumented)
- protected _batchAddRequestsWithRetries(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise;
- batchDeleteRequests(requests: RequestQueueClientRequestToDelete[]): Promise;
- delete(): Promise;
- deleteRequest(id: string): Promise;
+ protected addRequestBatchWithRetries(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise;
+ batchAddRequests(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise;
+ batchDeleteRequests(requests: RequestQueueClientRequestToDelete[], options?: TimeoutOptions): Promise;
+ delete(options?: TimeoutOptions): Promise;
+ deleteRequest(id: string, options?: TimeoutOptions): Promise;
deleteRequestLock(id: string, options?: RequestQueueClientDeleteRequestLockOptions): Promise;
- get(): Promise;
- getRequest(id: string): Promise;
+ get(options?: TimeoutOptions): Promise;
+ getRequest(id: string, options?: TimeoutOptions): Promise;
listAndLockHead(options: RequestQueueClientListAndLockHeadOptions): Promise;
listHead(options?: RequestQueueClientListHeadOptions): Promise;
listRequests(options?: RequestQueueClientListRequestsOptions): Promise & AsyncIterable;
paginateRequests(options?: RequestQueueClientPaginateRequestsOptions): RequestQueueRequestsAsyncIterable;
prolongRequestLock(id: string, options: RequestQueueClientProlongRequestLockOptions): Promise;
- unlockRequests(): Promise;
- update(newFields: RequestQueueClientUpdateOptions): Promise;
+ unlockRequests(options?: TimeoutOptions): Promise;
+ update(newFields: RequestQueueClientUpdateOptions, options?: TimeoutOptions): Promise;
updateRequest(request: RequestQueueClientRequestToUpdate, options?: RequestQueueClientAddRequestOptions): Promise;
}
// @public (undocumented)
-export interface RequestQueueClientAddRequestOptions {
+export interface RequestQueueClientAddRequestOptions extends TimeoutOptions {
// (undocumented)
forefront?: boolean;
}
@@ -3305,7 +3313,7 @@ export interface RequestQueueClientAddRequestResult extends GeneratedRequestRegi
}
// @public
-export interface RequestQueueClientBatchAddRequestWithRetriesOptions {
+export interface RequestQueueClientBatchAddRequestWithRetriesOptions extends TimeoutOptions {
// (undocumented)
forefront?: boolean;
// (undocumented)
@@ -3324,7 +3332,7 @@ export interface RequestQueueClientBatchRequestsOperationResult extends Generate
}
// @public
-export interface RequestQueueClientDeleteRequestLockOptions {
+export interface RequestQueueClientDeleteRequestLockOptions extends TimeoutOptions {
// (undocumented)
forefront?: boolean;
}
@@ -3333,7 +3341,7 @@ export interface RequestQueueClientDeleteRequestLockOptions {
export type RequestQueueClientGetRequestResult = RequestQueueClientRequestSchema;
// @public
-export interface RequestQueueClientListAndLockHeadOptions {
+export interface RequestQueueClientListAndLockHeadOptions extends TimeoutOptions {
// (undocumented)
limit?: number;
// (undocumented)
@@ -3352,7 +3360,7 @@ interface RequestQueueClientListAndLockHeadResultRePointed {
}
// @public
-export interface RequestQueueClientListHeadOptions {
+export interface RequestQueueClientListHeadOptions extends TimeoutOptions {
// (undocumented)
limit?: number;
}
@@ -3373,7 +3381,7 @@ export interface RequestQueueClientListItem extends GeneratedHeadRequest {
}
// @public
-export interface RequestQueueClientListRequestsOptions {
+export interface RequestQueueClientListRequestsOptions extends TimeoutOptions {
cursor?: string;
filter?: readonly RequestQueueListRequestsFilter[];
// (undocumented)
@@ -3396,7 +3404,7 @@ export interface RequestQueueClientLockedListItem extends GeneratedLockedHeadReq
}
// @public
-export interface RequestQueueClientPaginateRequestsOptions {
+export interface RequestQueueClientPaginateRequestsOptions extends TimeoutOptions {
cursor?: string;
filter?: readonly RequestQueueListRequestsFilter[];
// (undocumented)
@@ -3406,7 +3414,7 @@ export interface RequestQueueClientPaginateRequestsOptions {
}
// @public
-export interface RequestQueueClientProlongRequestLockOptions {
+export interface RequestQueueClientProlongRequestLockOptions extends TimeoutOptions {
// (undocumented)
forefront?: boolean;
// (undocumented)
@@ -3447,12 +3455,12 @@ export interface RequestQueueClientUpdateOptions {
// @public
export class RequestQueueCollectionClient extends ResourceCollectionClient {
constructor(options: ApiClientSubResourceOptions);
- getOrCreate(name?: string): Promise;
+ getOrCreate(name?: string, options?: TimeoutOptions): Promise;
list(options?: RequestQueueCollectionListOptions): Promise & AsyncIterable;
}
// @public (undocumented)
-export interface RequestQueueCollectionListOptions extends PaginationOptions {
+export interface RequestQueueCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
ownership?: STORAGE_OWNERSHIP_FILTER;
@@ -3512,11 +3520,12 @@ export interface RequestQueueUserOptions {
// Not exported by the entry point; reachable only as a referenced type.
// @public
class ResourceClient extends ApiClient {
- protected _delete(timeoutMillis?: number): Promise;
- protected _get(schema: z.ZodType, options?: T, timeoutMillis?: number): Promise;
+ protected deleteResource(timeoutSecs: Timeout): Promise;
+ protected getResource(schema: z.ZodType, options: T, timeoutSecs: Timeout): Promise;
+ protected timeoutForWaitForFinish(timeoutSecs: Timeout | undefined, tier: TimeoutTier, waitForFinishSecs: number | undefined): Timeout;
// (undocumented)
- protected _update(schema: z.ZodType, newFields: T, timeoutMillis?: number): Promise;
- protected _waitForFinish(schema: z.ZodType, newFields: T, timeoutSecs: Timeout): Promise;
+ protected waitForJobFinish(schema: z.ZodType, options?: WaitForFinishOptions): Promise;
}
@@ -3525,12 +3534,12 @@ class ResourceClient extends ApiClient {
// @public
class ResourceCollectionClient extends ApiClient {
// (undocumented)
- protected _create(schema: z.ZodType, resource: D): Promise;
+ protected createResource(schema: z.ZodType, resource: D, timeoutSecs: Timeout): Promise;
// (undocumented)
- protected _getOrCreate(schema: z.ZodType, name?: string, resource?: D): Promise;
+ protected getOrCreateResource(schema: z.ZodType, name: string | undefined, resource: D | undefined, timeoutSecs: Timeout): Promise;
// (undocumented)
- protected _list(schema: z.ZodType, options?: T): Promise;
- protected _listPaginated>(schema: z.ZodType, options?: T): AsyncIterable & Promise;
+ protected listResources(schema: z.ZodType, options: T | undefined, timeoutSecs: Timeout): Promise;
+ protected listResourcesPaginated>(schema: z.ZodType, options: T, defaultTimeoutSecs: Timeout): AsyncIterable & Promise;
}
// @public
@@ -3548,13 +3557,13 @@ export class ResponseValidationError extends Error {
export type ReturnTypeFromOptions = Options['stream'] extends true ? Readable : Options['buffer'] extends true ? Buffer : JsonValue;
// @public
-export interface RunAbortOptions {
+export interface RunAbortOptions extends TimeoutOptions {
// (undocumented)
gracefully?: boolean;
}
// @public
-export interface RunChargeOptions {
+export interface RunChargeOptions extends TimeoutOptions {
count?: number;
eventName: string;
idempotencyKey?: string;
@@ -3566,16 +3575,16 @@ export class RunClient extends ResourceClient {
abort(options?: RunAbortOptions): Promise;
charge(options: RunChargeOptions): Promise>>;
dataset(): DatasetClient;
- delete(): Promise;
+ delete(options?: TimeoutOptions): Promise;
get(options?: RunGetOptions): Promise;
getStreamedLog(options?: GetStreamedLogOptions): Promise;
keyValueStore(): KeyValueStoreClient;
log(): LogClient;
metamorph(targetActorId: string, input?: ActorInput, options?: RunMetamorphOptions): Promise;
- reboot(): Promise;
+ reboot(options?: TimeoutOptions): Promise;
requestQueue(): RequestQueueClient;
resurrect(options?: RunResurrectOptions): Promise;
- update(newFields: RunUpdateOptions): Promise;
+ update(newFields: RunUpdateOptions, options?: TimeoutOptions): Promise;
waitForFinish(options?: RunWaitForFinishOptions): Promise;
}
@@ -3586,7 +3595,7 @@ export class RunCollectionClient extends ResourceCollectionClient {
}
// @public (undocumented)
-export interface RunCollectionListOptions extends PaginationOptions {
+export interface RunCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
startedAfter?: Date | string;
@@ -3596,20 +3605,20 @@ export interface RunCollectionListOptions extends PaginationOptions {
}
// @public
-export interface RunGetOptions {
+export interface RunGetOptions extends TimeoutOptions {
// (undocumented)
waitForFinish?: number;
}
// @public
-export interface RunMetamorphOptions {
+export interface RunMetamorphOptions extends TimeoutOptions {
// (undocumented)
build?: string;
contentType?: string;
}
// @public
-export interface RunResurrectOptions {
+export interface RunResurrectOptions extends TimeoutOptions {
// (undocumented)
build?: string;
maxItems?: number;
@@ -3617,8 +3626,7 @@ export interface RunResurrectOptions {
// (undocumented)
memory?: number;
restartOnError?: boolean;
- // (undocumented)
- timeout?: number;
+ runTimeoutSecs?: number;
}
// @public
@@ -3630,7 +3638,7 @@ export interface RunUpdateOptions {
}
// @public
-export interface RunWaitForFinishOptions {
+export interface RunWaitForFinishOptions extends TimeoutOptions {
waitSecs?: number;
}
@@ -3678,10 +3686,10 @@ export enum ScheduleActions {
// @public
export class ScheduleClient extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
- delete(): Promise;
- get(): Promise;
- getLog(): Promise;
- update(newFields: ScheduleCreateOrUpdateData): Promise;
+ delete(options?: TimeoutOptions): Promise;
+ get(options?: TimeoutOptions): Promise;
+ getLog(options?: TimeoutOptions): Promise;
+ update(newFields: ScheduleCreateOrUpdateData, options?: TimeoutOptions): Promise;
}
// Not exported by the entry point; reachable only as a referenced type.
@@ -3694,12 +3702,12 @@ interface ScheduleClientNarrowings {
// @public
export class ScheduleCollectionClient extends ResourceCollectionClient {
constructor(options: ApiClientSubResourceOptions);
- create(schedule?: ScheduleCreateOrUpdateData): Promise;
+ create(schedule?: ScheduleCreateOrUpdateData, options?: TimeoutOptions): Promise;
list(options?: ScheduleCollectionListOptions): PaginatedIterator;
}
// @public (undocumented)
-export interface ScheduleCollectionListOptions extends PaginationOptions {
+export interface ScheduleCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
}
@@ -3759,7 +3767,7 @@ export class StoreCollectionClient extends ResourceCollectionClient {
}
// @public
-export interface StoreCollectionListOptions extends PaginationOptions {
+export interface StoreCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
category?: string;
includeUnrunnableActors?: boolean;
@@ -3801,28 +3809,28 @@ export interface TaskCallOptions extends Omit
export class TaskClient extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
call(input?: Dictionary, options?: TaskCallOptions): Promise;
- delete(): Promise;
- get(): Promise;
- getInput(): Promise;
+ delete(options?: TimeoutOptions): Promise;
+ get(options?: TimeoutOptions): Promise;
+ getInput(options?: TimeoutOptions): Promise;
lastRun(options?: TaskLastRunOptions): RunClient;
- publish(): Promise;
+ publish(options?: TimeoutOptions): Promise;
runs(): RunCollectionClient;
start(input?: Dictionary, options?: TaskStartOptions): Promise;
- unpublish(): Promise;
- update(newFields: TaskUpdateData): Promise;
- updateInput(newFields: Dictionary | Dictionary[]): Promise;
+ unpublish(options?: TimeoutOptions): Promise;
+ update(newFields: TaskUpdateData, options?: TimeoutOptions): Promise;
+ updateInput(newFields: Dictionary | Dictionary[], options?: TimeoutOptions): Promise;
webhooks(): WebhookCollectionClient;
}
// @public
export class TaskCollectionClient extends ResourceCollectionClient {
constructor(options: ApiClientSubResourceOptions);
- create(task: TaskCreateData): Promise;
+ create(task: TaskCreateData, options?: TimeoutOptions): Promise;
list(options?: TaskCollectionListOptions): PaginatedIterator;
}
// @public (undocumented)
-export interface TaskCollectionListOptions extends PaginationOptions {
+export interface TaskCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
}
@@ -3917,6 +3925,17 @@ export interface TieredPricingPerEvent {
export interface TieredPricingPerEventEntry extends GeneratedTieredPricingPerEventEntry {
}
+// @public
+export type Timeout = TimeoutTier | 'noTimeout' | number;
+
+// @public
+export interface TimeoutOptions {
+ timeoutSecs?: Timeout;
+}
+
+// @public
+export type TimeoutTier = 'short' | 'medium' | 'long';
+
// Not exported by the entry point; reachable only as a referenced type.
// @public (undocumented)
type Timezone = (typeof timezones)[number];
@@ -3951,10 +3970,10 @@ export interface User extends Omit;
- limits(): Promise;
- monthlyUsage(): Promise;
- updateLimits(options: LimitsUpdateOptions): Promise;
+ get(options?: TimeoutOptions): Promise;
+ limits(options?: TimeoutOptions): Promise;
+ monthlyUsage(options?: TimeoutOptions): Promise;
+ updateLimits(newLimits: LimitsUpdateOptions, options?: TimeoutOptions): Promise;
}
// @public
@@ -3998,7 +4017,7 @@ interface UserRePointed {
// Not exported by the entry point; reachable only as a referenced type.
// @public (undocumented)
-interface WaitForFinishOptions {
+interface WaitForFinishOptions extends TimeoutOptions {
// (undocumented)
waitSecs?: number;
}
@@ -4028,22 +4047,22 @@ export interface WebhookCertainRunCondition {
// @public
export class WebhookClient extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
- delete(): Promise;
+ delete(options?: TimeoutOptions): Promise;
dispatches(): WebhookDispatchCollectionClient;
- get(): Promise;
- test(): Promise;
- update(newFields: WebhookUpdateData): Promise;
+ get(options?: TimeoutOptions): Promise;
+ test(options?: TimeoutOptions): Promise;
+ update(newFields: WebhookUpdateData, options?: TimeoutOptions): Promise;
}
// @public
export class WebhookCollectionClient extends ResourceCollectionClient {
constructor(options: ApiClientSubResourceOptions);
- create(webhook?: WebhookUpdateData): Promise;
+ create(webhook?: WebhookUpdateData, options?: TimeoutOptions): Promise;
list(options?: WebhookCollectionListOptions): PaginatedIterator>;
}
// @public (undocumented)
-export interface WebhookCollectionListOptions extends PaginationOptions {
+export interface WebhookCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
}
@@ -4062,7 +4081,7 @@ export interface WebhookDispatchCall extends GeneratedWebhookDispatchCall {
// @public
export class WebhookDispatchClient extends ResourceClient {
constructor(options: ApiClientSubResourceOptions);
- get(): Promise;
+ get(options?: TimeoutOptions): Promise;
}
// @public
@@ -4072,7 +4091,7 @@ export class WebhookDispatchCollectionClient extends ResourceCollectionClient {
}
// @public (undocumented)
-export interface WebhookDispatchCollectionListOptions extends PaginationOptions {
+export interface WebhookDispatchCollectionListOptions extends PaginationOptions, TimeoutOptions {
// (undocumented)
desc?: boolean;
}
diff --git a/src/apify_api_error.ts b/src/apify_api_error.ts
index e51c021ff..145717faa 100644
--- a/src/apify_api_error.ts
+++ b/src/apify_api_error.ts
@@ -8,15 +8,32 @@ import { isBuffer } from './utils.js';
export type { ApifyApiErrorType } from './models.js';
/**
- * Examples of capturing groups for "...at ActorCollectionClient._list (/Users/..."
- * 0: "at ActorCollectionClient._list ("
+ * Examples of capturing groups for "...at ActorCollectionClient.listResources (/Users/..."
+ * 0: "at ActorCollectionClient.listResources ("
* 1: undefined
* 2: "ActorCollectionClient"
* 3: undefined
- * 4: "list"
+ * 4: "listResources"
* @private
*/
-const CLIENT_METHOD_REGEX = /at( async)? ([A-Za-z]+(Collection)?Client)\._?([A-Za-z]+) \(/;
+const CLIENT_METHOD_REGEX = /at( async)? ([A-Za-z]+(Collection)?Client)\.([A-Za-z]+) \(/;
+
+/**
+ * A public method that returns the promise of a shared helper without awaiting it leaves only the helper on
+ * the stack, so the helper is reported under the method the caller invoked.
+ * @private
+ */
+const PUBLIC_METHOD_BY_HELPER: Record = {
+ getResource: 'get',
+ updateResource: 'update',
+ deleteResource: 'delete',
+ waitForJobFinish: 'waitForFinish',
+ listResources: 'list',
+ createResource: 'create',
+ getOrCreateResource: 'getOrCreate',
+ addRequestBatch: 'batchAddRequests',
+ addRequestBatchWithRetries: 'batchAddRequests',
+};
/**
* An `ApifyApiError` is thrown for successful HTTP requests that reach the API,
@@ -152,8 +169,8 @@ export class ApifyApiError extends Error {
private _extractClientAndMethodFromStack() {
const match = this.stack!.match(CLIENT_METHOD_REGEX);
- if (match) return `${match[2]}.${match[4]}`;
- return 'unknown';
+ if (!match) return 'unknown';
+ return `${match[2]}.${PUBLIC_METHOD_BY_HELPER[match[4]] ?? match[4]}`;
}
/**
diff --git a/src/apify_client.ts b/src/apify_client.ts
index b9536bbaa..9b3240242 100644
--- a/src/apify_client.ts
+++ b/src/apify_client.ts
@@ -32,24 +32,31 @@ import { WebhookCollectionClient } from './resource_clients/webhook_collection.j
import { WebhookDispatchClient } from './resource_clients/webhook_dispatch.js';
import { WebhookDispatchCollectionClient } from './resource_clients/webhook_dispatch_collection.js';
import { Statistics } from './statistics.js';
+import {
+ DEFAULT_TIMEOUT_LONG_SECS,
+ DEFAULT_TIMEOUT_MAX_SECS,
+ DEFAULT_TIMEOUT_MEDIUM_SECS,
+ DEFAULT_TIMEOUT_SHORT_SECS,
+} from './timeouts.js';
import { parseArgument } from './utils.js';
-const DEFAULT_TIMEOUT_SECS = 360;
-
const clientOptionsSchema = z.strictObject({
baseUrl: z.string().default('https://api.apify.com'),
publicBaseUrl: z.string().default('https://api.apify.com'),
maxRetries: z.number().default(8),
minDelayBetweenRetriesMillis: z.number().default(500),
requestInterceptors: z.array(z.unknown()).default([]),
- timeoutSecs: z.number().default(DEFAULT_TIMEOUT_SECS),
+ timeoutShortSecs: z.number().positive().default(DEFAULT_TIMEOUT_SHORT_SECS),
+ timeoutMediumSecs: z.number().positive().default(DEFAULT_TIMEOUT_MEDIUM_SECS),
+ timeoutLongSecs: z.number().positive().default(DEFAULT_TIMEOUT_LONG_SECS),
+ timeoutMaxSecs: z.number().positive().default(DEFAULT_TIMEOUT_MAX_SECS),
token: z.string().optional(),
userAgentSuffix: z.union([z.string(), z.array(z.string())]).optional(),
});
const resourceIdSchema = z.string().min(1);
const requestQueueOptionsSchema = z.strictObject({
clientKey: z.string().min(1).optional(),
- timeoutSecs: z.number().optional(),
+ timeoutSecs: z.number().positive().optional(),
});
/**
@@ -98,7 +105,10 @@ export class ApifyClient {
maxRetries,
minDelayBetweenRetriesMillis,
requestInterceptors,
- timeoutSecs,
+ timeoutShortSecs,
+ timeoutMediumSecs,
+ timeoutLongSecs,
+ timeoutMaxSecs,
token,
} = parsed;
@@ -116,14 +126,17 @@ export class ApifyClient {
maxRetries,
minDelayBetweenRetriesMillis,
requestInterceptors,
- timeoutSecs,
+ timeoutShortSecs,
+ timeoutMediumSecs,
+ timeoutLongSecs,
+ timeoutMaxSecs,
logger: this.logger,
token: this.token,
userAgentSuffix: parsed.userAgentSuffix,
});
}
- private _options() {
+ private subClientOptions() {
return {
baseUrl: this.baseUrl,
publicBaseUrl: this.publicBaseUrl,
@@ -141,7 +154,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/acts-get
*/
actors(): ActorCollectionClient {
- return new ActorCollectionClient(this._options());
+ return new ActorCollectionClient(this.subClientOptions());
}
/**
@@ -165,7 +178,7 @@ export class ApifyClient {
return new ActorClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -178,7 +191,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/actor-builds-get
*/
builds(): BuildCollectionClient {
- return new BuildCollectionClient(this._options());
+ return new BuildCollectionClient(this.subClientOptions());
}
/**
@@ -195,7 +208,7 @@ export class ApifyClient {
return new BuildClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -208,7 +221,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/datasets-get
*/
datasets(): DatasetCollectionClient {
- return new DatasetCollectionClient(this._options());
+ return new DatasetCollectionClient(this.subClientOptions());
}
/**
@@ -241,7 +254,7 @@ export class ApifyClient {
return new DatasetClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -254,7 +267,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/key-value-stores-get
*/
keyValueStores(): KeyValueStoreCollectionClient {
- return new KeyValueStoreCollectionClient(this._options());
+ return new KeyValueStoreCollectionClient(this.subClientOptions());
}
/**
@@ -281,7 +294,7 @@ export class ApifyClient {
return new KeyValueStoreClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -297,7 +310,7 @@ export class ApifyClient {
return new LogClient({
id: buildOrRunId,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -310,7 +323,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/request-queues-get
*/
requestQueues(): RequestQueueCollectionClient {
- return new RequestQueueCollectionClient(this._options());
+ return new RequestQueueCollectionClient(this.subClientOptions());
}
/**
@@ -340,7 +353,7 @@ export class ApifyClient {
const apiClientOptions = {
id,
- ...this._options(),
+ ...this.subClientOptions(),
};
return new RequestQueueClient(apiClientOptions, parsed);
}
@@ -355,7 +368,7 @@ export class ApifyClient {
*/
runs(): RunCollectionClient {
return new RunCollectionClient({
- ...this._options(),
+ ...this.subClientOptions(),
resourcePath: 'actor-runs',
});
}
@@ -384,7 +397,7 @@ export class ApifyClient {
return new RunClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -397,7 +410,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/actor-tasks-get
*/
tasks(): TaskCollectionClient {
- return new TaskCollectionClient(this._options());
+ return new TaskCollectionClient(this.subClientOptions());
}
/**
@@ -420,7 +433,7 @@ export class ApifyClient {
return new TaskClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -433,7 +446,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/schedules-get
*/
schedules(): ScheduleCollectionClient {
- return new ScheduleCollectionClient(this._options());
+ return new ScheduleCollectionClient(this.subClientOptions());
}
/**
@@ -450,7 +463,7 @@ export class ApifyClient {
return new ScheduleClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -468,7 +481,7 @@ export class ApifyClient {
return new UserClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -481,7 +494,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/webhooks-get
*/
webhooks(): WebhookCollectionClient {
- return new WebhookCollectionClient(this._options());
+ return new WebhookCollectionClient(this.subClientOptions());
}
/**
@@ -498,7 +511,7 @@ export class ApifyClient {
return new WebhookClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -511,7 +524,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/webhook-dispatches-get
*/
webhookDispatches(): WebhookDispatchCollectionClient {
- return new WebhookDispatchCollectionClient(this._options());
+ return new WebhookDispatchCollectionClient(this.subClientOptions());
}
/**
@@ -526,7 +539,7 @@ export class ApifyClient {
return new WebhookDispatchClient({
id,
- ...this._options(),
+ ...this.subClientOptions(),
});
}
@@ -539,7 +552,7 @@ export class ApifyClient {
* @see https://docs.apify.com/api/v2/store-get
*/
store(): StoreCollectionClient {
- return new StoreCollectionClient(this._options());
+ return new StoreCollectionClient(this.subClientOptions());
}
/**
@@ -582,8 +595,27 @@ export interface ApifyClientOptions {
minDelayBetweenRetriesMillis?: number;
/** @default [] */
requestInterceptors?: RequestInterceptorFunction[];
- /** @default 360 */
- timeoutSecs?: number;
+ /**
+ * Duration of the `short` timeout tier, in seconds: simple metadata reads and writes.
+ * @default 5
+ */
+ timeoutShortSecs?: number;
+ /**
+ * Duration of the `medium` timeout tier, in seconds: listing, batch and trigger operations.
+ * @default 30
+ */
+ timeoutMediumSecs?: number;
+ /**
+ * Duration of the `long` timeout tier, in seconds: downloads, uploads and streaming.
+ * @default 360
+ */
+ timeoutLongSecs?: number;
+ /**
+ * Cap on the timeout of a single request attempt, in seconds. It bounds the doubling of the timeout across
+ * retries, and caps tier and per-call timeouts alike, so raise it to allow a request timeout above 360 s.
+ * @default 360
+ */
+ timeoutMaxSecs?: number;
token?: string;
/**
* @since Added in 2.10.0
diff --git a/src/base/api_client.ts b/src/base/api_client.ts
index 190e9435b..01331a31e 100644
--- a/src/base/api_client.ts
+++ b/src/base/api_client.ts
@@ -47,7 +47,7 @@ export abstract class ApiClient {
const { baseUrl, publicBaseUrl, apifyClient, httpClient, resourcePath, id, params = {} } = options;
this.id = id;
- this.safeId = id && this._toSafeId(id);
+ this.safeId = id && this.toSafeId(id);
this.baseUrl = baseUrl;
this.publicBaseUrl = publicBaseUrl;
this.resourcePath = resourcePath;
@@ -57,40 +57,40 @@ export abstract class ApiClient {
this.params = params;
}
- protected _subResourceOptions(moreOptions?: T): BaseOptions & T {
+ protected subResourceOptions(moreOptions?: T): BaseOptions & T {
const baseOptions: BaseOptions = {
- baseUrl: this._url(),
+ baseUrl: this.buildUrl(),
publicBaseUrl: this.publicBaseUrl,
apifyClient: this.apifyClient,
httpClient: this.httpClient,
- params: this._params(),
+ params: this.buildParams(),
};
return { ...baseOptions, ...moreOptions } as BaseOptions & T;
}
- protected _url(path?: string | string[]): string {
+ protected buildUrl(path?: string | string[]): string {
return path ? `${this.url}/${toPath(path)}` : this.url;
}
- protected _publicUrl(path?: string | string[]): string {
+ protected buildPublicUrl(path?: string | string[]): string {
const url = this.id
? `${this.publicBaseUrl}/${this.resourcePath}/${toPathSegment(this.safeId!)}`
: `${this.publicBaseUrl}/${this.resourcePath}`;
return path ? `${url}/${toPath(path)}` : url;
}
- protected _params(endpointParams?: T): Record {
+ protected buildParams(endpointParams?: T): Record {
return { ...this.params, ...endpointParams };
}
- protected _toSafeId(id: string): string {
+ protected toSafeId(id: string): string {
return id.replaceAll('/', '~');
}
/**
* Returns async iterator to iterate through all items and Promise that can be awaited to get first page of results.
*/
- protected _listPaginatedFromCallback>(
+ protected listPaginatedFromCallback>(
getPaginatedList: (options?: T) => Promise,
options: T = {} as T,
): AsyncIterable & Promise {
@@ -104,7 +104,8 @@ export abstract class ApiClient {
};
// `chunkSize` only sizes this loop's requests; it is not an API parameter, so it must not reach
- // `_params()` and the query string.
+ // `buildParams()` and the query string. The same goes for `timeoutSecs`, which callers take out before
+ // calling this, since it also picks the timeout of every page request.
const { chunkSize, ...listOptions } = options;
const paginatedListPromise = getPaginatedList({
diff --git a/src/base/resource_client.ts b/src/base/resource_client.ts
index 7739d45a6..5ffe062f1 100644
--- a/src/base/resource_client.ts
+++ b/src/base/resource_client.ts
@@ -4,6 +4,7 @@ import type { z } from 'zod';
import type { ApifyApiError } from '../apify_api_error.js';
import type { ApifyRequestConfig } from '../http_client.js';
+import type { Timeout, TimeoutOptions, TimeoutTier } from '../timeouts.js';
import { catchNotFoundForResourceOrThrow, catchNotFoundOrThrow, parseResponse } from '../utils.js';
import { ApiClient } from './api_client.js';
@@ -14,29 +15,44 @@ import { ApiClient } from './api_client.js';
*/
const MAX_WAIT_FOR_FINISH = 999999;
-export const SMALL_TIMEOUT_MILLIS = 5 * 1000; // For fast and common actions. Suitable for idempotent actions.
-export const MEDIUM_TIMEOUT_MILLIS = 30 * 1000; // For actions that may take longer.
-export const DEFAULT_TIMEOUT_MILLIS = 360 * 1000; // 6 minutes
+/** The API holds a `waitForFinish` response for at most a minute, however long the parameter asks for. */
+const MAX_WAIT_FOR_FINISH_HOLD_SECS = 60;
/**
* Resource client.
* @private
*/
export class ResourceClient extends ApiClient {
+ /**
+ * Picks the timeout of a request that asks the API to hold its response with `waitForFinish`. The request
+ * gets the hold the caller asked for plus the round trip its tier allows, so the client does not abort a
+ * request while the API is still holding it - which for `start()` and `build()` would retry a call that
+ * creates a resource. An explicit per-call `timeoutSecs` is used as given.
+ */
+ protected timeoutForWaitForFinish(
+ timeoutSecs: Timeout | undefined,
+ tier: TimeoutTier,
+ waitForFinishSecs: number | undefined,
+ ): Timeout {
+ if (timeoutSecs !== undefined) return timeoutSecs;
+ if (waitForFinishSecs === undefined) return tier;
+
+ const holdSecs = Math.min(waitForFinishSecs, MAX_WAIT_FOR_FINISH_HOLD_SECS);
+ if (holdSecs <= 0) return tier;
+
+ return holdSecs + this.httpClient.timeoutMillis[tier] / 1000;
+ }
+
/**
* A 404 resolves to `undefined` only when the client names its resource by ID. A chained client without one, such
* as `run.dataset()`, throws it instead (see `catchNotFoundForResourceOrThrow()`).
*/
- protected async _get(
- schema: z.ZodType,
- options: T = {} as T,
- timeoutMillis?: number,
- ): Promise {
+ protected async getResource(schema: z.ZodType, options: T, timeoutSecs: Timeout): Promise {
const requestOpts: ApifyRequestConfig = {
- url: this._url(),
+ url: this.buildUrl(),
method: 'GET',
- params: this._params(options),
- timeout: timeoutMillis,
+ params: this.buildParams(options),
+ timeoutSecs,
};
try {
const response = await this.httpClient.call(requestOpts);
@@ -48,13 +64,13 @@ export class ResourceClient extends ApiClient {
return undefined;
}
- protected async _update(schema: z.ZodType, newFields: T, timeoutMillis?: number): Promise {
+ protected async updateResource(schema: z.ZodType, newFields: T, timeoutSecs: Timeout): Promise {
const response = await this.httpClient.call({
- url: this._url(),
+ url: this.buildUrl(),
method: 'PUT',
- params: this._params(),
+ params: this.buildParams(),
data: newFields,
- timeout: timeoutMillis,
+ timeoutSecs,
});
return parseResponse(response, schema);
}
@@ -63,13 +79,13 @@ export class ResourceClient extends ApiClient {
* A 404 is swallowed, keeping the DELETE idempotent, only when the client names its resource by ID. A chained client
* without one throws it instead (see `catchNotFoundForResourceOrThrow()`).
*/
- protected async _delete(timeoutMillis?: number): Promise