apify-client 3.0.0-beta.46 → 3.0.0-beta.47

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.
@@ -85,6 +85,8 @@ export declare class ActorClient extends ResourceClient {
85
85
  * @param options.webhooks - Webhooks to trigger when the Actor run reaches a specific state (e.g., `SUCCEEDED`, `FAILED`).
86
86
  * @param options.maxItems - Maximum number of dataset items that will be charged (only for pay-per-result Actors).
87
87
  * @param options.maxTotalChargeUsd - Maximum cost in USD (only for pay-per-event Actors).
88
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
89
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds.
88
90
  * @param options.timeoutSecs - Timeout for the API request. Default is `'medium'`, extended to cover `waitForFinish`
89
91
  * when the API is asked to hold the response.
90
92
  * @returns The Actor run object with status, usage, and storage IDs
@@ -119,6 +121,9 @@ export declare class ActorClient extends ResourceClient {
119
121
  * @param options.build - Tag or number of the build to run (e.g., `'beta'` or `'1.2.345'`).
120
122
  * @param options.memory - Memory in megabytes allocated for the run.
121
123
  * @param options.runTimeoutSecs - Maximum run duration in seconds.
124
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
125
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds. The time spent
126
+ * retrying doesn't count toward `waitSecs`.
122
127
  * @param options.timeoutSecs - Timeout for each API request, the start and every poll alike. Default is `'noTimeout'`.
123
128
  * @returns The finished Actor run object with final status (`SUCCEEDED`, `FAILED`, `ABORTED`, or `TIMED-OUT`)
124
129
  * @see https://docs.apify.com/api/v2/act-runs-post
@@ -367,6 +372,20 @@ export interface ActorStartOptions extends TimeoutOptions {
367
372
  * @since Added in 2.17.0
368
373
  */
369
374
  forcePermissionLevel?: ACTOR_PERMISSION_LEVEL;
375
+ /**
376
+ * Retry the start while the account lacks the resources for the run, that is while the API rejects it with an
377
+ * `ApifyApiError` of type `actor-memory-limit-exceeded` or `concurrent-runs-limit-exceeded`. Both clear as
378
+ * other runs or builds finish. The start is retried every 10 seconds, and any other error is thrown right away.
379
+ *
380
+ * `true` retries until the run starts. A number stops retrying after that many seconds and throws the last
381
+ * error. Omitted or `false`, the first rejection is thrown.
382
+ *
383
+ * A run that requests more memory than the whole memory limit of the account is rejected with
384
+ * `actor-memory-limit-exceeded` as well and never starts, so `true` retries it forever.
385
+ *
386
+ * A `Readable` input can't be sent twice, so its start is never retried and the first rejection is thrown.
387
+ */
388
+ waitForResources?: boolean | number;
370
389
  }
371
390
  /**
372
391
  * Options for calling an Actor and waiting for it to finish.
@@ -4,7 +4,8 @@ import { Log } from '@apify/log';
4
4
  import { ResourceClient } from '../base/resource_client.js';
5
5
  import * as schemas from '../schemas.js';
6
6
  import { timeoutOptionsSchema, timeoutOptionsShape } from '../timeouts.js';
7
- import { anyObjectSchema, parseArgument, parseResponse, stringifyWebhooksToBase64 } from '../utils.js';
7
+ import { anyObjectSchema, isStream, parseArgument, parseResponse, stringifyWebhooksToBase64 } from '../utils.js';
8
+ import { startWaitingForResources } from '../wait_for_resources.js';
8
9
  import { ActorVersionClient } from './actor_version.js';
9
10
  import { ActorVersionCollectionClient } from './actor_version_collection.js';
10
11
  import { BuildClient } from './build.js';
@@ -23,6 +24,7 @@ const startOptionsSchema = z.strictObject({
23
24
  maxTotalChargeUsd: z.number().min(0).optional(),
24
25
  restartOnError: z.boolean().optional(),
25
26
  forcePermissionLevel: z.enum(ACTOR_PERMISSION_LEVEL).optional(),
27
+ waitForResources: z.union([z.boolean(), z.number().min(0)]).optional(),
26
28
  ...timeoutOptionsShape,
27
29
  });
28
30
  const callOptionsSchema = z.strictObject({
@@ -37,6 +39,7 @@ const callOptionsSchema = z.strictObject({
37
39
  log: z.union([z.null(), z.instanceof(Log), z.literal('default')]).optional(),
38
40
  restartOnError: z.boolean().optional(),
39
41
  forcePermissionLevel: z.enum(ACTOR_PERMISSION_LEVEL).optional(),
42
+ waitForResources: z.union([z.boolean(), z.number().min(0)]).optional(),
40
43
  ...timeoutOptionsShape,
41
44
  });
42
45
  const validateInputOptionsSchema = z.strictObject({
@@ -141,6 +144,8 @@ export class ActorClient extends ResourceClient {
141
144
  * @param options.webhooks - Webhooks to trigger when the Actor run reaches a specific state (e.g., `SUCCEEDED`, `FAILED`).
142
145
  * @param options.maxItems - Maximum number of dataset items that will be charged (only for pay-per-result Actors).
143
146
  * @param options.maxTotalChargeUsd - Maximum cost in USD (only for pay-per-event Actors).
147
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
148
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds.
144
149
  * @param options.timeoutSecs - Timeout for the API request. Default is `'medium'`, extended to cover `waitForFinish`
145
150
  * when the API is asked to hold the response.
146
151
  * @returns The Actor run object with status, usage, and storage IDs
@@ -161,7 +166,7 @@ export class ActorClient extends ResourceClient {
161
166
  */
162
167
  async start(input, options = {}) {
163
168
  const parsed = parseArgument(options, startOptionsSchema, 'ActorStartOptions');
164
- const { waitForFinish, runTimeoutSecs, memory, build, maxItems, maxTotalChargeUsd, restartOnError, forcePermissionLevel, timeoutSecs, signal, } = parsed;
169
+ const { waitForFinish, runTimeoutSecs, memory, build, maxItems, maxTotalChargeUsd, restartOnError, forcePermissionLevel, waitForResources, timeoutSecs, signal, } = parsed;
165
170
  // The API's `timeout` parameter bounds the run, not the request.
166
171
  const params = {
167
172
  waitForFinish,
@@ -189,7 +194,8 @@ export class ActorClient extends ResourceClient {
189
194
  'content-type': parsed.contentType,
190
195
  };
191
196
  }
192
- const response = await this.httpClient.call(request);
197
+ // A `Readable` input is consumed by the first attempt, so a retry would start the run with an empty input.
198
+ const response = await startWaitingForResources(async () => this.httpClient.call(request), isStream(input) ? false : waitForResources, this.httpClient.logger, signal);
193
199
  return parseResponse(response, schemas.Run());
194
200
  }
195
201
  /**
@@ -207,6 +213,9 @@ export class ActorClient extends ResourceClient {
207
213
  * @param options.build - Tag or number of the build to run (e.g., `'beta'` or `'1.2.345'`).
208
214
  * @param options.memory - Memory in megabytes allocated for the run.
209
215
  * @param options.runTimeoutSecs - Maximum run duration in seconds.
216
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
217
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds. The time spent
218
+ * retrying doesn't count toward `waitSecs`.
210
219
  * @param options.timeoutSecs - Timeout for each API request, the start and every poll alike. Default is `'noTimeout'`.
211
220
  * @returns The finished Actor run object with final status (`SUCCEEDED`, `FAILED`, `ABORTED`, or `TIMED-OUT`)
212
221
  * @see https://docs.apify.com/api/v2/act-runs-post
@@ -103,6 +103,8 @@ export declare class TaskClient extends ResourceClient {
103
103
  * @param options.maxItems - Maximum number of dataset items (for pay-per-result Actors).
104
104
  * @param options.maxTotalChargeUsd - Maximum cost in USD (for pay-per-event Actors).
105
105
  * @param options.restartOnError - Whether to restart the run on error.
106
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
107
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds.
106
108
  * @param options.timeoutSecs - Timeout for the API request. Default is `'medium'`, extended to cover `waitForFinish`
107
109
  * when the API is asked to hold the response.
108
110
  * @returns The Actor Run object.
@@ -123,6 +125,9 @@ export declare class TaskClient extends ResourceClient {
123
125
  * @param options.maxItems - Maximum number of dataset items (for pay-per-result Actors).
124
126
  * @param options.maxTotalChargeUsd - Maximum cost in USD (for pay-per-event Actors).
125
127
  * @param options.restartOnError - Whether to restart the run on error.
128
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
129
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds. The time spent
130
+ * retrying doesn't count toward `waitSecs`.
126
131
  * @param options.timeoutSecs - Timeout for each API request, the start and every poll alike. Default is `'noTimeout'`.
127
132
  * @returns The Actor run object.
128
133
  * @see https://docs.apify.com/api/v2/actor-task-runs-post
@@ -3,7 +3,8 @@ import { ACT_JOB_STATUSES, META_ORIGINS } from '@apify/consts';
3
3
  import { ResourceClient } from '../base/resource_client.js';
4
4
  import * as schemas from '../schemas.js';
5
5
  import { timeoutOptionsSchema, timeoutOptionsShape } from '../timeouts.js';
6
- import { anyObjectSchema, cast, parseArgument, parseResponse, stringifyWebhooksToBase64 } from '../utils.js';
6
+ import { anyObjectSchema, cast, isStream, parseArgument, parseResponse, stringifyWebhooksToBase64 } from '../utils.js';
7
+ import { startWaitingForResources } from '../wait_for_resources.js';
7
8
  import { RunClient } from './run.js';
8
9
  import { RunCollectionClient } from './run_collection.js';
9
10
  import { WebhookCollectionClient } from './webhook_collection.js';
@@ -17,6 +18,7 @@ const startOptionsSchema = z.strictObject({
17
18
  maxItems: z.number().min(0).optional(),
18
19
  maxTotalChargeUsd: z.number().min(0).optional(),
19
20
  restartOnError: z.boolean().optional(),
21
+ waitForResources: z.union([z.boolean(), z.number().min(0)]).optional(),
20
22
  ...timeoutOptionsShape,
21
23
  });
22
24
  const callOptionsSchema = z.strictObject({
@@ -28,6 +30,7 @@ const callOptionsSchema = z.strictObject({
28
30
  maxItems: z.number().min(0).optional(),
29
31
  maxTotalChargeUsd: z.number().min(0).optional(),
30
32
  restartOnError: z.boolean().optional(),
33
+ waitForResources: z.union([z.boolean(), z.number().min(0)]).optional(),
31
34
  ...timeoutOptionsShape,
32
35
  });
33
36
  const lastRunOptionsSchema = z.strictObject({
@@ -148,6 +151,8 @@ export class TaskClient extends ResourceClient {
148
151
  * @param options.maxItems - Maximum number of dataset items (for pay-per-result Actors).
149
152
  * @param options.maxTotalChargeUsd - Maximum cost in USD (for pay-per-event Actors).
150
153
  * @param options.restartOnError - Whether to restart the run on error.
154
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
155
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds.
151
156
  * @param options.timeoutSecs - Timeout for the API request. Default is `'medium'`, extended to cover `waitForFinish`
152
157
  * when the API is asked to hold the response.
153
158
  * @returns The Actor Run object.
@@ -156,7 +161,7 @@ export class TaskClient extends ResourceClient {
156
161
  async start(input, options = {}) {
157
162
  parseArgument(input, inputSchema);
158
163
  const parsed = parseArgument(options, startOptionsSchema, 'TaskStartOptions');
159
- const { waitForFinish, runTimeoutSecs, memory, build, maxItems, maxTotalChargeUsd, restartOnError, timeoutSecs, signal, } = parsed;
164
+ const { waitForFinish, runTimeoutSecs, memory, build, maxItems, maxTotalChargeUsd, restartOnError, waitForResources, timeoutSecs, signal, } = parsed;
160
165
  // The API's `timeout` parameter bounds the run, not the request.
161
166
  const params = {
162
167
  waitForFinish,
@@ -181,7 +186,8 @@ export class TaskClient extends ResourceClient {
181
186
  timeoutSecs: this.timeoutForWaitForFinish(timeoutSecs, 'medium', waitForFinish),
182
187
  signal,
183
188
  };
184
- const response = await this.httpClient.call(request);
189
+ // A `Readable` input is consumed by the first attempt, so a retry would start the run with an empty input.
190
+ const response = await startWaitingForResources(async () => this.httpClient.call(request), isStream(input) ? false : waitForResources, this.httpClient.logger, signal);
185
191
  return parseResponse(response, schemas.Run());
186
192
  }
187
193
  /**
@@ -198,6 +204,9 @@ export class TaskClient extends ResourceClient {
198
204
  * @param options.maxItems - Maximum number of dataset items (for pay-per-result Actors).
199
205
  * @param options.maxTotalChargeUsd - Maximum cost in USD (for pay-per-event Actors).
200
206
  * @param options.restartOnError - Whether to restart the run on error.
207
+ * @param options.waitForResources - Retry the start while the account lacks the memory or a concurrent-run slot for
208
+ * the run. `true` retries until the run starts, a number stops retrying after that many seconds. The time spent
209
+ * retrying doesn't count toward `waitSecs`.
201
210
  * @param options.timeoutSecs - Timeout for each API request, the start and every poll alike. Default is `'noTimeout'`.
202
211
  * @returns The Actor run object.
203
212
  * @see https://docs.apify.com/api/v2/actor-task-runs-post
@@ -0,0 +1,8 @@
1
+ import type { Log } from '@apify/log';
2
+ /**
3
+ * Makes the `start` request, retrying it every {@link WAIT_FOR_RESOURCES_COOLDOWN_MILLIS} while it fails with one of
4
+ * {@link RESOURCE_LIMIT_ERROR_TYPES}. `true` retries until the request succeeds, a number of seconds bounds the
5
+ * retrying, after which the last error is thrown. Any other error is thrown right away.
6
+ * @internal
7
+ */
8
+ export declare function startWaitingForResources<T>(start: () => Promise<T>, waitForResources: boolean | number | undefined, logger: Log, signal?: AbortSignal): Promise<T>;
@@ -0,0 +1,39 @@
1
+ import { ApifyApiError } from './apify_api_error.js';
2
+ import { sleep } from './utils.js';
3
+ /**
4
+ * Error types the API rejects a run start with while the account has no free memory or concurrent-run slot for it.
5
+ * Both clear as other runs or builds finish.
6
+ */
7
+ const RESOURCE_LIMIT_ERROR_TYPES = new Set([
8
+ 'actor-memory-limit-exceeded',
9
+ 'concurrent-runs-limit-exceeded',
10
+ ]);
11
+ /** Cooldown between two attempts to start a run that was rejected for lack of resources. */
12
+ const WAIT_FOR_RESOURCES_COOLDOWN_MILLIS = 10_000;
13
+ /**
14
+ * Makes the `start` request, retrying it every {@link WAIT_FOR_RESOURCES_COOLDOWN_MILLIS} while it fails with one of
15
+ * {@link RESOURCE_LIMIT_ERROR_TYPES}. `true` retries until the request succeeds, a number of seconds bounds the
16
+ * retrying, after which the last error is thrown. Any other error is thrown right away.
17
+ * @internal
18
+ */
19
+ export async function startWaitingForResources(start, waitForResources, logger, signal) {
20
+ if (!waitForResources)
21
+ return start();
22
+ const deadline = waitForResources === true ? Infinity : Date.now() + waitForResources * 1000;
23
+ for (;;) {
24
+ try {
25
+ return await start();
26
+ }
27
+ catch (err) {
28
+ if (!(err instanceof ApifyApiError) || !RESOURCE_LIMIT_ERROR_TYPES.has(err.type ?? ''))
29
+ throw err;
30
+ const remainingMillis = deadline - Date.now();
31
+ if (remainingMillis <= 0)
32
+ throw err;
33
+ const delayMillis = Math.min(WAIT_FOR_RESOURCES_COOLDOWN_MILLIS, remainingMillis);
34
+ logger.info(`Not enough resources to start the run, retrying in ${Number((delayMillis / 1000).toPrecision(3))}s: ${err.message}`);
35
+ await sleep(delayMillis, signal);
36
+ signal?.throwIfAborted();
37
+ }
38
+ }
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apify-client",
3
- "version": "3.0.0-beta.46",
3
+ "version": "3.0.0-beta.47",
4
4
  "description": "Apify API client for JavaScript",
5
5
  "engines": {
6
6
  "node": ">=22.13.0"