apify-client 3.0.0-beta.88 → 3.0.0-beta.89
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 +6 -0
- package/dist/bundle.js +10 -10
- package/dist/bundle.js.map +1 -1
- package/dist/http_clients/axios.d.ts +2 -1
- package/dist/http_clients/axios.js +6 -4
- package/dist/http_clients/base.d.ts +64 -14
- package/dist/http_clients/base.js +68 -30
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/resource_clients/actor.d.ts +30 -0
- package/dist/resource_clients/actor.js +43 -0
- package/dist/resource_clients/actor_server.d.ts +141 -0
- package/dist/resource_clients/actor_server.js +195 -0
- package/package.json +1 -1
|
@@ -54,7 +54,8 @@ export declare class AxiosHttpClient extends HttpClient {
|
|
|
54
54
|
close(): Promise<void>;
|
|
55
55
|
/**
|
|
56
56
|
* Sends the request through axios and hands the raw response back. A stream body is sent without following
|
|
57
|
-
* redirects, since the part of it already sent could not be replayed
|
|
57
|
+
* redirects, since the part of it already sent could not be replayed, and so is a request with
|
|
58
|
+
* `followRedirects: false`. In the browser, the browser follows redirects itself.
|
|
58
59
|
*/
|
|
59
60
|
sendRequest(request: HttpRequest): Promise<HttpResponse>;
|
|
60
61
|
}
|
|
@@ -78,11 +78,12 @@ export class AxiosHttpClient extends HttpClient {
|
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
80
|
* Sends the request through axios and hands the raw response back. A stream body is sent without following
|
|
81
|
-
* redirects, since the part of it already sent could not be replayed
|
|
81
|
+
* redirects, since the part of it already sent could not be replayed, and so is a request with
|
|
82
|
+
* `followRedirects: false`. In the browser, the browser follows redirects itself.
|
|
82
83
|
*/
|
|
83
84
|
async sendRequest(request) {
|
|
84
85
|
await this.#ensureHttpAgents();
|
|
85
|
-
const { method, url, headers, body, timeoutMillis, stream, signal } = request;
|
|
86
|
+
const { method, url, headers, body, timeoutMillis, stream, followRedirects, signal } = request;
|
|
86
87
|
const response = await this.axios.request({
|
|
87
88
|
method,
|
|
88
89
|
url,
|
|
@@ -92,8 +93,9 @@ export class AxiosHttpClient extends HttpClient {
|
|
|
92
93
|
timeout: timeoutMillis ?? 0,
|
|
93
94
|
signal,
|
|
94
95
|
responseType: stream ? 'stream' : 'arraybuffer',
|
|
95
|
-
// See axios/axios#1045 for why axios must not buffer a stream body to follow redirects.
|
|
96
|
-
|
|
96
|
+
// See axios/axios#1045 for why axios must not buffer a stream body to follow redirects. The XHR adapter
|
|
97
|
+
// ignores `maxRedirects`, since the browser follows redirects itself.
|
|
98
|
+
...(isStream(body) || !followRedirects ? { maxRedirects: 0 } : {}),
|
|
97
99
|
});
|
|
98
100
|
return {
|
|
99
101
|
status: response.status,
|
|
@@ -84,6 +84,40 @@ export interface ApifyRequestConfig {
|
|
|
84
84
|
stringifyFunctions?: boolean;
|
|
85
85
|
/** Give up on the first transport timeout instead of retrying it. */
|
|
86
86
|
doNotRetryTimeouts?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* How to compress the request body: an {@link HttpCompressor} for this request only, or `false` to send the body
|
|
89
|
+
* uncompressed. Defaults to the client's {@link HttpClient.httpCompressor}.
|
|
90
|
+
*/
|
|
91
|
+
compression?: HttpCompressor | false;
|
|
92
|
+
/**
|
|
93
|
+
* Header that carries the client's API token for this request. The Apify API reads `Authorization`. Actor
|
|
94
|
+
* servers also accept `X-Apify-Authorization`, which the platform removes before the request reaches the Actor,
|
|
95
|
+
* so the `Authorization` header stays free for the Actor's own authentication. A per-request header of either
|
|
96
|
+
* name still wins.
|
|
97
|
+
* @default 'Authorization'
|
|
98
|
+
*/
|
|
99
|
+
authorizationHeader?: 'Authorization' | 'X-Apify-Authorization';
|
|
100
|
+
/**
|
|
101
|
+
* Whether an error status throws {@link ApifyApiError}. With `false`, the call resolves to the error response
|
|
102
|
+
* once no retry follows, and a rate limit (429) doesn't count toward the client's statistics, which track the
|
|
103
|
+
* API's rate limits.
|
|
104
|
+
* @default true
|
|
105
|
+
*/
|
|
106
|
+
throwOnErrorStatus?: boolean;
|
|
107
|
+
/** Status codes to retry. Defaults to rate limits (429) and server errors (5xx). */
|
|
108
|
+
retryStatusCodes?: readonly number[];
|
|
109
|
+
/**
|
|
110
|
+
* Whether transport errors, timeouts included, are retried. With `false`, the first one fails the call, since the
|
|
111
|
+
* request may have reached the server before it failed.
|
|
112
|
+
* @default true
|
|
113
|
+
*/
|
|
114
|
+
retryTransportErrors?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Whether the transport follows redirects. With `false`, a redirect response is handled like an error status, so
|
|
117
|
+
* headers that carry the token never go to a host the server picked.
|
|
118
|
+
* @default true
|
|
119
|
+
*/
|
|
120
|
+
followRedirects?: boolean;
|
|
87
121
|
/**
|
|
88
122
|
* Aborts the call. Once the signal aborts, the attempt in flight is ended, no retry follows, and the call
|
|
89
123
|
* rejects with the signal's `reason`.
|
|
@@ -91,7 +125,8 @@ export interface ApifyRequestConfig {
|
|
|
91
125
|
signal?: AbortSignal;
|
|
92
126
|
}
|
|
93
127
|
/**
|
|
94
|
-
* A
|
|
128
|
+
* A response as {@link HttpClient.call} resolves to it: a success, or an error response when the request sets
|
|
129
|
+
* {@link ApifyRequestConfig.throwOnErrorStatus} to `false`.
|
|
95
130
|
*
|
|
96
131
|
* @template T - Type of the parsed body.
|
|
97
132
|
*/
|
|
@@ -124,6 +159,11 @@ export interface HttpRequest {
|
|
|
124
159
|
timeoutMillis?: number;
|
|
125
160
|
/** Whether to hand the body back unread as a `Readable`, so the caller can stream it. */
|
|
126
161
|
stream: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Whether to follow redirects. With `false`, return a redirect response as it is: the request carries a token
|
|
164
|
+
* that must not follow a redirect to another host.
|
|
165
|
+
*/
|
|
166
|
+
followRedirects: boolean;
|
|
127
167
|
/**
|
|
128
168
|
* The caller's abort signal, to hand to the HTTP library alongside the timeout. The pipeline stops retrying
|
|
129
169
|
* once it aborts, so the transport only has to end the request in flight.
|
|
@@ -191,8 +231,13 @@ export interface HttpClientOptions {
|
|
|
191
231
|
*
|
|
192
232
|
* Overriding {@link call} itself is also supported and bypasses the transport hooks entirely. Such a client has
|
|
193
233
|
* to send the default headers from `defaultHeaders` with every request, otherwise the `Authorization` header never
|
|
194
|
-
* reaches the API.
|
|
195
|
-
*
|
|
234
|
+
* reaches the API. It also has to honor the per-request options {@link ApifyRequestConfig.compression},
|
|
235
|
+
* {@link ApifyRequestConfig.authorizationHeader}, {@link ApifyRequestConfig.throwOnErrorStatus},
|
|
236
|
+
* {@link ApifyRequestConfig.retryStatusCodes}, {@link ApifyRequestConfig.retryTransportErrors} and
|
|
237
|
+
* {@link ApifyRequestConfig.followRedirects}.
|
|
238
|
+
* {@link ActorServerClient.request} relies on them to keep the token away from the Actor's code, to return the
|
|
239
|
+
* server's error responses and to avoid sending a request to the Actor twice. The protected helpers
|
|
240
|
+
* `prepareRequest()`, `buildUrl()` and `computeTimeoutMillis()` stay available to it.
|
|
196
241
|
*
|
|
197
242
|
* @example
|
|
198
243
|
* ```javascript
|
|
@@ -281,7 +326,8 @@ export declare abstract class HttpClient {
|
|
|
281
326
|
* propagate unwrapped: {@link call} classifies them through {@link isRetryableTransportError} and
|
|
282
327
|
* {@link isTimeoutError}. Return error responses as they are too, the pipeline turns them into
|
|
283
328
|
* {@link ApifyApiError} and decides whether to retry. Reject when the connection drops before the body is
|
|
284
|
-
* complete: the pipeline takes a returned body as whole and does not retry one that fails to parse.
|
|
329
|
+
* complete: the pipeline takes a returned body as whole and does not retry one that fails to parse. When
|
|
330
|
+
* `request.followRedirects` is `false`, return a redirect response as it is, since the request carries a token.
|
|
285
331
|
*
|
|
286
332
|
* @param request - The request to send, with the headers merged, the body serialized and the query encoded.
|
|
287
333
|
* @returns The response, with the body unread when `request.stream` is set and as raw bytes otherwise.
|
|
@@ -291,8 +337,9 @@ export declare abstract class HttpClient {
|
|
|
291
337
|
* Makes an API request with automatic retries and exponential backoff.
|
|
292
338
|
*
|
|
293
339
|
* Network errors the transport classifies as retryable, rate limits (HTTP 429) and server errors (HTTP 5xx)
|
|
294
|
-
* are retried up to {@link maxRetries} times.
|
|
295
|
-
*
|
|
340
|
+
* are retried up to {@link maxRetries} times, and `config.retryStatusCodes` picks other statuses to retry. Any
|
|
341
|
+
* other error status is thrown as {@link ApifyApiError} right away, or returned when `config.throwOnErrorStatus`
|
|
342
|
+
* is `false`. A request whose body is a `Readable` is never retried, since part of the stream has already been
|
|
296
343
|
* consumed by the time the failure shows. A `Blob` body is read again for every attempt, so it is retried like
|
|
297
344
|
* any other, unless reading the blob itself fails: that error is thrown right away, with the transport error
|
|
298
345
|
* as its `cause` when that error does not already wrap it. Aborting `config.signal` ends the attempt in flight,
|
|
@@ -300,19 +347,22 @@ export declare abstract class HttpClient {
|
|
|
300
347
|
*
|
|
301
348
|
* @template T - Type of the parsed response body.
|
|
302
349
|
* @param config - The request to make.
|
|
303
|
-
* @returns The successful response.
|
|
304
|
-
* @throws {ApifyApiError} When the API responds with an error status the retries could not fix
|
|
350
|
+
* @returns The successful response, or the error response when `config.throwOnErrorStatus` is `false`.
|
|
351
|
+
* @throws {ApifyApiError} When the API responds with an error status the retries could not fix, unless
|
|
352
|
+
* `config.throwOnErrorStatus` is `false`.
|
|
305
353
|
*/
|
|
306
354
|
call<T = any>(config: ApifyRequestConfig): Promise<ApifyResponse<T>>;
|
|
307
355
|
/**
|
|
308
356
|
* Prepares the headers and the body of a request.
|
|
309
357
|
*
|
|
310
|
-
* Merges the client's default headers with the per-request ones, header names compared case-insensitively and
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
* `Content-
|
|
314
|
-
*
|
|
315
|
-
* body is
|
|
358
|
+
* Merges the client's default headers with the per-request ones, header names compared case-insensitively and the
|
|
359
|
+
* per-request values winning, after moving the default `Authorization` header to `X-Apify-Authorization` when
|
|
360
|
+
* `config.authorizationHeader` asks for it. Serializes an object body to JSON, setting
|
|
361
|
+
* `Content-Type: application/json` unless the caller supplied a content type. Runs the body through
|
|
362
|
+
* {@link httpCompressor}, or the compressor in `config.compression`, unless `config.compression` is `false`, a
|
|
363
|
+
* `Content-Encoding` header is already set, the body is too small, or its content type says the payload is already
|
|
364
|
+
* compressed. A caller-supplied `Content-Encoding` is forwarded as it is, which is how a pre-encoded body is
|
|
365
|
+
* uploaded, and `Content-Encoding: identity` opts a single request out of compression.
|
|
316
366
|
*/
|
|
317
367
|
protected prepareRequest(config: ApifyRequestConfig): Promise<{
|
|
318
368
|
headers: Record<string, string>;
|
|
@@ -42,8 +42,13 @@ const UNSERIALIZABLE_OBJECT_TAGS = new Set(['FormData', 'ReadableStream']);
|
|
|
42
42
|
*
|
|
43
43
|
* Overriding {@link call} itself is also supported and bypasses the transport hooks entirely. Such a client has
|
|
44
44
|
* to send the default headers from `defaultHeaders` with every request, otherwise the `Authorization` header never
|
|
45
|
-
* reaches the API.
|
|
46
|
-
*
|
|
45
|
+
* reaches the API. It also has to honor the per-request options {@link ApifyRequestConfig.compression},
|
|
46
|
+
* {@link ApifyRequestConfig.authorizationHeader}, {@link ApifyRequestConfig.throwOnErrorStatus},
|
|
47
|
+
* {@link ApifyRequestConfig.retryStatusCodes}, {@link ApifyRequestConfig.retryTransportErrors} and
|
|
48
|
+
* {@link ApifyRequestConfig.followRedirects}.
|
|
49
|
+
* {@link ActorServerClient.request} relies on them to keep the token away from the Actor's code, to return the
|
|
50
|
+
* server's error responses and to avoid sending a request to the Actor twice. The protected helpers
|
|
51
|
+
* `prepareRequest()`, `buildUrl()` and `computeTimeoutMillis()` stay available to it.
|
|
47
52
|
*
|
|
48
53
|
* @example
|
|
49
54
|
* ```javascript
|
|
@@ -170,7 +175,8 @@ export class HttpClient {
|
|
|
170
175
|
* propagate unwrapped: {@link call} classifies them through {@link isRetryableTransportError} and
|
|
171
176
|
* {@link isTimeoutError}. Return error responses as they are too, the pipeline turns them into
|
|
172
177
|
* {@link ApifyApiError} and decides whether to retry. Reject when the connection drops before the body is
|
|
173
|
-
* complete: the pipeline takes a returned body as whole and does not retry one that fails to parse.
|
|
178
|
+
* complete: the pipeline takes a returned body as whole and does not retry one that fails to parse. When
|
|
179
|
+
* `request.followRedirects` is `false`, return a redirect response as it is, since the request carries a token.
|
|
174
180
|
*
|
|
175
181
|
* @param request - The request to send, with the headers merged, the body serialized and the query encoded.
|
|
176
182
|
* @returns The response, with the body unread when `request.stream` is set and as raw bytes otherwise.
|
|
@@ -182,8 +188,9 @@ export class HttpClient {
|
|
|
182
188
|
* Makes an API request with automatic retries and exponential backoff.
|
|
183
189
|
*
|
|
184
190
|
* Network errors the transport classifies as retryable, rate limits (HTTP 429) and server errors (HTTP 5xx)
|
|
185
|
-
* are retried up to {@link maxRetries} times.
|
|
186
|
-
*
|
|
191
|
+
* are retried up to {@link maxRetries} times, and `config.retryStatusCodes` picks other statuses to retry. Any
|
|
192
|
+
* other error status is thrown as {@link ApifyApiError} right away, or returned when `config.throwOnErrorStatus`
|
|
193
|
+
* is `false`. A request whose body is a `Readable` is never retried, since part of the stream has already been
|
|
187
194
|
* consumed by the time the failure shows. A `Blob` body is read again for every attempt, so it is retried like
|
|
188
195
|
* any other, unless reading the blob itself fails: that error is thrown right away, with the transport error
|
|
189
196
|
* as its `cause` when that error does not already wrap it. Aborting `config.signal` ends the attempt in flight,
|
|
@@ -191,8 +198,9 @@ export class HttpClient {
|
|
|
191
198
|
*
|
|
192
199
|
* @template T - Type of the parsed response body.
|
|
193
200
|
* @param config - The request to make.
|
|
194
|
-
* @returns The successful response.
|
|
195
|
-
* @throws {ApifyApiError} When the API responds with an error status the retries could not fix
|
|
201
|
+
* @returns The successful response, or the error response when `config.throwOnErrorStatus` is `false`.
|
|
202
|
+
* @throws {ApifyApiError} When the API responds with an error status the retries could not fix, unless
|
|
203
|
+
* `config.throwOnErrorStatus` is `false`.
|
|
196
204
|
*/
|
|
197
205
|
async call(config) {
|
|
198
206
|
config.signal?.throwIfAborted();
|
|
@@ -204,22 +212,25 @@ export class HttpClient {
|
|
|
204
212
|
/**
|
|
205
213
|
* Prepares the headers and the body of a request.
|
|
206
214
|
*
|
|
207
|
-
* Merges the client's default headers with the per-request ones, header names compared case-insensitively and
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
* `Content-
|
|
211
|
-
*
|
|
212
|
-
* body is
|
|
215
|
+
* Merges the client's default headers with the per-request ones, header names compared case-insensitively and the
|
|
216
|
+
* per-request values winning, after moving the default `Authorization` header to `X-Apify-Authorization` when
|
|
217
|
+
* `config.authorizationHeader` asks for it. Serializes an object body to JSON, setting
|
|
218
|
+
* `Content-Type: application/json` unless the caller supplied a content type. Runs the body through
|
|
219
|
+
* {@link httpCompressor}, or the compressor in `config.compression`, unless `config.compression` is `false`, a
|
|
220
|
+
* `Content-Encoding` header is already set, the body is too small, or its content type says the payload is already
|
|
221
|
+
* compressed. A caller-supplied `Content-Encoding` is forwarded as it is, which is how a pre-encoded body is
|
|
222
|
+
* uploaded, and `Content-Encoding: identity` opts a single request out of compression.
|
|
213
223
|
*/
|
|
214
224
|
async prepareRequest(config) {
|
|
215
|
-
let headers = mergeHeaders(this.defaultHeaders, config.headers);
|
|
225
|
+
let headers = mergeHeaders(withAuthorizationHeader(this.defaultHeaders, config.authorizationHeader), config.headers);
|
|
216
226
|
let body = serializeBody(config.data, headers, config.stringifyFunctions);
|
|
217
|
-
const
|
|
227
|
+
const compressor = config.compression ?? this.httpCompressor;
|
|
228
|
+
const bytes = compressor && compressibleBytes(body, headers);
|
|
218
229
|
if (bytes) {
|
|
219
|
-
body = await
|
|
230
|
+
body = await compressor.compress(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength));
|
|
220
231
|
// Compressing changes the body length, so a `Content-Length` the caller set describes the wrong body
|
|
221
232
|
// and would leave the server waiting for bytes that never arrive.
|
|
222
|
-
headers = mergeHeaders(headers, { 'Content-Encoding':
|
|
233
|
+
headers = mergeHeaders(headers, { 'Content-Encoding': compressor.contentEncoding });
|
|
223
234
|
deleteHeader(headers, 'content-length');
|
|
224
235
|
}
|
|
225
236
|
// A blob reaches a Node.js transport as a stream, which would go out chunked without a known length.
|
|
@@ -304,8 +315,8 @@ export class HttpClient {
|
|
|
304
315
|
}
|
|
305
316
|
/**
|
|
306
317
|
* Executes one attempt: sends the request, decodes the body and turns an error status into {@link ApifyApiError}.
|
|
307
|
-
* Throws for anything that is not a success,
|
|
308
|
-
* retry could not fix it.
|
|
318
|
+
* Throws for anything that is not a success, unless `config.throwOnErrorStatus` is `false` and no retry follows,
|
|
319
|
+
* and flags the failure as final through `stopRetrying()` when a retry could not fix it.
|
|
309
320
|
*/
|
|
310
321
|
async #makeRequest(options) {
|
|
311
322
|
const { config, url, headers, body, attempt, stopRetrying } = options;
|
|
@@ -327,6 +338,7 @@ export class HttpClient {
|
|
|
327
338
|
body: attemptBody,
|
|
328
339
|
timeoutMillis: this.computeTimeoutMillis(attempt, config.timeoutSecs),
|
|
329
340
|
stream: config.responseType === 'stream',
|
|
341
|
+
followRedirects: config.followRedirects ?? true,
|
|
330
342
|
signal: config.signal,
|
|
331
343
|
});
|
|
332
344
|
}
|
|
@@ -347,10 +359,16 @@ export class HttpClient {
|
|
|
347
359
|
if (attemptBody !== body && isStream(attemptBody))
|
|
348
360
|
attemptBody.destroy();
|
|
349
361
|
}
|
|
362
|
+
const isError = response.status >= 300;
|
|
363
|
+
const isRetryable = isError && this.#isStatusCodeRetryable(response.status, config.retryStatusCodes);
|
|
364
|
+
// An error response goes back to the caller as it is once no retry follows, which a stream body rules out.
|
|
365
|
+
const shouldReturnError = isError &&
|
|
366
|
+
config.throwOnErrorStatus === false &&
|
|
367
|
+
(!isRetryable || requestIsStream || attempt > this.maxRetries);
|
|
350
368
|
// A failed streaming request carries the API error body in the stream, so read it and let
|
|
351
369
|
// `ApifyApiError` parse it like any other error body. A body that cannot be read leaves the error
|
|
352
370
|
// without a message, which beats losing the status code to a stream error.
|
|
353
|
-
if (
|
|
371
|
+
if (isError && !shouldReturnError && isStream(response.body)) {
|
|
354
372
|
response.body = await concatStreamToBuffer(response.body).catch(() => undefined);
|
|
355
373
|
}
|
|
356
374
|
let data;
|
|
@@ -360,7 +378,7 @@ export class HttpClient {
|
|
|
360
378
|
catch (err) {
|
|
361
379
|
// The body of a success arrived in full, since a transfer cut short fails in `sendRequest()`, so a
|
|
362
380
|
// retry would get the same body back.
|
|
363
|
-
if (
|
|
381
|
+
if (!isError) {
|
|
364
382
|
stopRetrying();
|
|
365
383
|
throw err;
|
|
366
384
|
}
|
|
@@ -373,13 +391,17 @@ export class HttpClient {
|
|
|
373
391
|
data: data,
|
|
374
392
|
config,
|
|
375
393
|
};
|
|
376
|
-
if (
|
|
394
|
+
if (!isError)
|
|
377
395
|
return apifyResponse;
|
|
378
|
-
|
|
396
|
+
// The statistics track the API's rate limits. Only API calls throw on an error status, so a call with
|
|
397
|
+
// `throwOnErrorStatus: false` is taken for a non-API one and its 429s stay out of the statistics.
|
|
398
|
+
if (response.status === RATE_LIMIT_EXCEEDED_STATUS_CODE && config.throwOnErrorStatus !== false) {
|
|
379
399
|
this.stats.addRateLimitError(attempt);
|
|
380
400
|
}
|
|
401
|
+
if (shouldReturnError)
|
|
402
|
+
return apifyResponse;
|
|
381
403
|
const apiError = ApifyApiError.fromResponse(apifyResponse, attempt);
|
|
382
|
-
if (!
|
|
404
|
+
if (!isRetryable) {
|
|
383
405
|
stopRetrying();
|
|
384
406
|
}
|
|
385
407
|
else if (requestIsStream) {
|
|
@@ -409,15 +431,16 @@ export class HttpClient {
|
|
|
409
431
|
}
|
|
410
432
|
}
|
|
411
433
|
/**
|
|
412
|
-
* Decides whether a transport error ends the call. Timeouts the request opted out of retrying
|
|
413
|
-
* transport does not classify as retryable
|
|
434
|
+
* Decides whether a transport error ends the call. Timeouts the request opted out of retrying, any error on a
|
|
435
|
+
* request that retries no transport errors, errors the transport does not classify as retryable and any error on
|
|
436
|
+
* a request with a stream body are final.
|
|
414
437
|
*/
|
|
415
438
|
#handleRequestError(err, config, stopRetrying) {
|
|
416
439
|
if (config.doNotRetryTimeouts && this.isTimeoutError(err)) {
|
|
417
440
|
stopRetrying();
|
|
418
441
|
return;
|
|
419
442
|
}
|
|
420
|
-
if (!this.isRetryableTransportError(err)) {
|
|
443
|
+
if (config.retryTransportErrors === false || !this.isRetryableTransportError(err)) {
|
|
421
444
|
stopRetrying();
|
|
422
445
|
return;
|
|
423
446
|
}
|
|
@@ -427,10 +450,13 @@ export class HttpClient {
|
|
|
427
450
|
}
|
|
428
451
|
}
|
|
429
452
|
/**
|
|
430
|
-
* Rate limits (429) and server errors (500+) are retried
|
|
431
|
-
* cannot follow or invalid input, which repeating the request
|
|
453
|
+
* Rate limits (429) and server errors (500+) are retried, unless the request lists its own `retryStatusCodes`.
|
|
454
|
+
* Anything else in 300-499 is a redirect the client cannot follow or invalid input, which repeating the request
|
|
455
|
+
* cannot fix.
|
|
432
456
|
*/
|
|
433
|
-
#isStatusCodeRetryable(statusCode) {
|
|
457
|
+
#isStatusCodeRetryable(statusCode, retryStatusCodes) {
|
|
458
|
+
if (retryStatusCodes)
|
|
459
|
+
return retryStatusCodes.includes(statusCode);
|
|
434
460
|
return statusCode === RATE_LIMIT_EXCEEDED_STATUS_CODE || statusCode >= 500;
|
|
435
461
|
}
|
|
436
462
|
#informAboutStreamNoRetry() {
|
|
@@ -466,6 +492,18 @@ function mergeHeaders(base, override) {
|
|
|
466
492
|
}
|
|
467
493
|
return merged;
|
|
468
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* Moves the `Authorization` header of `headers` to the header named `name`, or returns `headers` unchanged when
|
|
497
|
+
* `name` is `Authorization`.
|
|
498
|
+
*/
|
|
499
|
+
function withAuthorizationHeader(headers, name = 'Authorization') {
|
|
500
|
+
const authorization = getHeader(headers, 'authorization');
|
|
501
|
+
if (name === 'Authorization' || authorization === undefined)
|
|
502
|
+
return headers;
|
|
503
|
+
const moved = { ...headers };
|
|
504
|
+
deleteHeader(moved, 'authorization');
|
|
505
|
+
return mergeHeaders(moved, { [name]: authorization });
|
|
506
|
+
}
|
|
469
507
|
/**
|
|
470
508
|
* Removes a header by name, compared case-insensitively.
|
|
471
509
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from './resource_clients/actor.js';
|
|
|
3
3
|
export * from './resource_clients/actor_collection.js';
|
|
4
4
|
export * from './resource_clients/actor_env_var.js';
|
|
5
5
|
export * from './resource_clients/actor_env_var_collection.js';
|
|
6
|
+
export * from './resource_clients/actor_server.js';
|
|
6
7
|
export * from './resource_clients/actor_version.js';
|
|
7
8
|
export * from './resource_clients/actor_version_collection.js';
|
|
8
9
|
export * from './resource_clients/build.js';
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from './resource_clients/actor.js';
|
|
|
3
3
|
export * from './resource_clients/actor_collection.js';
|
|
4
4
|
export * from './resource_clients/actor_env_var.js';
|
|
5
5
|
export * from './resource_clients/actor_env_var_collection.js';
|
|
6
|
+
export * from './resource_clients/actor_server.js';
|
|
6
7
|
export * from './resource_clients/actor_version.js';
|
|
7
8
|
export * from './resource_clients/actor_version_collection.js';
|
|
8
9
|
export * from './resource_clients/build.js';
|
|
@@ -5,6 +5,7 @@ import { ResourceClient } from '../base/resource_client.js';
|
|
|
5
5
|
import type { ActorResource, Run } from '../models.js';
|
|
6
6
|
import type { TimeoutOptions } from '../timeouts.js';
|
|
7
7
|
import type { Dictionary } from '../utils.js';
|
|
8
|
+
import { ActorServerClient } from './actor_server.js';
|
|
8
9
|
import { ActorVersionClient } from './actor_version.js';
|
|
9
10
|
import { ActorVersionCollectionClient } from './actor_version_collection.js';
|
|
10
11
|
import type { Build, BuildClientGetOptions } from './build.js';
|
|
@@ -37,6 +38,7 @@ export type { ActorResource, ActorChargeEvent, ActorChargeEvents, ActorDefinitio
|
|
|
37
38
|
* @see https://docs.apify.com/platform/actors
|
|
38
39
|
*/
|
|
39
40
|
export declare class ActorClient extends ResourceClient {
|
|
41
|
+
#private;
|
|
40
42
|
/**
|
|
41
43
|
* @hidden
|
|
42
44
|
*/
|
|
@@ -293,6 +295,34 @@ export declare class ActorClient extends ResourceClient {
|
|
|
293
295
|
* @see https://docs.apify.com/api/v2/act-webhooks-get
|
|
294
296
|
*/
|
|
295
297
|
webhooks(): WebhookCollectionClient;
|
|
298
|
+
/**
|
|
299
|
+
* Returns a client for the server interface of this Actor.
|
|
300
|
+
*
|
|
301
|
+
* A Server Actor runs a web server, its server interface, that is called over HTTP without starting a run for
|
|
302
|
+
* each call. Server Actors were formerly called Actor Standby, and {@link standby} is a deprecated alias of this
|
|
303
|
+
* method. Every call returns the same client, so the server URL it fetches is reused.
|
|
304
|
+
*
|
|
305
|
+
* @returns The client for the server interface of this Actor
|
|
306
|
+
* @see https://docs.apify.com/platform/actors/running/standby
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```javascript
|
|
310
|
+
* const response = await client.actor('username/actor-name').server().request('/search', {
|
|
311
|
+
* params: { q: 'apify' },
|
|
312
|
+
* });
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
server(): ActorServerClient;
|
|
316
|
+
/**
|
|
317
|
+
* Returns a client for the server interface of this Actor in Standby mode.
|
|
318
|
+
*
|
|
319
|
+
* Named after Actor Standby, the former name of Server Actors.
|
|
320
|
+
*
|
|
321
|
+
* @deprecated Use {@link server} instead. Will be removed in v4.
|
|
322
|
+
* @returns The client for the server interface of this Actor
|
|
323
|
+
* @see https://docs.apify.com/platform/actors/running/standby
|
|
324
|
+
*/
|
|
325
|
+
standby(): ActorServerClient;
|
|
296
326
|
}
|
|
297
327
|
/**
|
|
298
328
|
* Fields that can be updated when modifying an Actor.
|
|
@@ -6,6 +6,7 @@ import * as schemas from '../schemas.js';
|
|
|
6
6
|
import { timeoutOptionsSchema, timeoutOptionsShape } from '../timeouts.js';
|
|
7
7
|
import { anyObjectSchema, isStream, parseArgument, parseResponse, stringifyWebhooksToBase64 } from '../utils.js';
|
|
8
8
|
import { startWaitingForResources } from '../wait_for_resources.js';
|
|
9
|
+
import { ActorServerClient } from './actor_server.js';
|
|
9
10
|
import { ActorVersionClient } from './actor_version.js';
|
|
10
11
|
import { ActorVersionCollectionClient } from './actor_version_collection.js';
|
|
11
12
|
import { BuildClient } from './build.js';
|
|
@@ -81,6 +82,7 @@ const lastRunOptionsSchema = z.strictObject({
|
|
|
81
82
|
* @see https://docs.apify.com/platform/actors
|
|
82
83
|
*/
|
|
83
84
|
export class ActorClient extends ResourceClient {
|
|
85
|
+
#serverClient;
|
|
84
86
|
/**
|
|
85
87
|
* @hidden
|
|
86
88
|
*/
|
|
@@ -475,4 +477,45 @@ export class ActorClient extends ResourceClient {
|
|
|
475
477
|
webhooks() {
|
|
476
478
|
return new WebhookCollectionClient(this.subResourceOptions());
|
|
477
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Returns a client for the server interface of this Actor.
|
|
482
|
+
*
|
|
483
|
+
* A Server Actor runs a web server, its server interface, that is called over HTTP without starting a run for
|
|
484
|
+
* each call. Server Actors were formerly called Actor Standby, and {@link standby} is a deprecated alias of this
|
|
485
|
+
* method. Every call returns the same client, so the server URL it fetches is reused.
|
|
486
|
+
*
|
|
487
|
+
* @returns The client for the server interface of this Actor
|
|
488
|
+
* @see https://docs.apify.com/platform/actors/running/standby
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* ```javascript
|
|
492
|
+
* const response = await client.actor('username/actor-name').server().request('/search', {
|
|
493
|
+
* params: { q: 'apify' },
|
|
494
|
+
* });
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
server() {
|
|
498
|
+
this.#serverClient ??= new ActorServerClient({
|
|
499
|
+
baseUrl: this.baseUrl,
|
|
500
|
+
publicBaseUrl: this.publicBaseUrl,
|
|
501
|
+
apifyClient: this.apifyClient,
|
|
502
|
+
httpClient: this.httpClient,
|
|
503
|
+
id: this.id,
|
|
504
|
+
params: this.params,
|
|
505
|
+
});
|
|
506
|
+
return this.#serverClient;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Returns a client for the server interface of this Actor in Standby mode.
|
|
510
|
+
*
|
|
511
|
+
* Named after Actor Standby, the former name of Server Actors.
|
|
512
|
+
*
|
|
513
|
+
* @deprecated Use {@link server} instead. Will be removed in v4.
|
|
514
|
+
* @returns The client for the server interface of this Actor
|
|
515
|
+
* @see https://docs.apify.com/platform/actors/running/standby
|
|
516
|
+
*/
|
|
517
|
+
standby() {
|
|
518
|
+
this.httpClient.logger.deprecated('`standby()` is deprecated and will be removed in v4. Use `server()` instead.');
|
|
519
|
+
return this.server();
|
|
520
|
+
}
|
|
478
521
|
}
|