apify-client 3.0.0-beta.70 → 3.0.0-beta.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.js +10 -10
- package/dist/bundle.js.map +1 -1
- package/dist/generated/api.d.ts +125 -74
- package/dist/generated/schemas.d.ts +124 -79
- package/dist/generated/schemas.js +51 -30
- package/dist/http_clients/base.d.ts +10 -6
- package/dist/http_clients/base.js +52 -6
- package/dist/models.d.ts +5 -0
- package/dist/resource_clients/key_value_store.d.ts +9 -6
- package/dist/resource_clients/key_value_store.js +14 -9
- package/dist/runtime/node.js +31 -0
- package/dist/runtime/types.d.ts +8 -0
- package/dist/runtime/web.js +3 -0
- package/dist/schemas.d.ts +200 -3
- package/dist/schemas.js +18 -3
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/package.json +1 -1
|
@@ -57,10 +57,13 @@ export const ErrorDetail = lazySchema(() => z.looseObject({
|
|
|
57
57
|
export const ErrorResponse = lazySchema(() => z.looseObject({
|
|
58
58
|
error: ErrorDetail(),
|
|
59
59
|
}));
|
|
60
|
-
|
|
61
|
-
export const
|
|
60
|
+
/** Determines the permission level that the Actor requires to run. For details, see [Actor permissions](https://docs.apify.com/platform/actors/development/permissions). */
|
|
61
|
+
export const ActorPermissionLevel = lazySchema(() => z.enum(["LIMITED_PERMISSIONS", "FULL_PERMISSIONS"]).or(z.string()));
|
|
62
|
+
/** The version source types accepted in request bodies. Same as `VersionSourceType`, without `SOURCE_CODE`. */
|
|
63
|
+
export const WritableVersionSourceType = lazySchema(() => z.enum(["SOURCE_FILES", "GIT_REPO", "TARBALL", "GITHUB_GIST"]).or(z.string()));
|
|
64
|
+
export const EnvVarRequest = lazySchema(() => z.looseObject({
|
|
62
65
|
name: z.string(),
|
|
63
|
-
value: z.string()
|
|
66
|
+
value: z.string(),
|
|
64
67
|
isSecret: z.boolean().nullable().optional(),
|
|
65
68
|
}));
|
|
66
69
|
export const SourceCodeFileFormat = lazySchema(() => z.enum(["BASE64", "TEXT"]).or(z.string()));
|
|
@@ -79,19 +82,17 @@ export const SourceCodeFolder = lazySchema(() => z.looseObject({
|
|
|
79
82
|
folder: z.boolean(),
|
|
80
83
|
}));
|
|
81
84
|
export const VersionSourceFiles = lazySchema(() => z.array(z.union([SourceCodeFile(), SourceCodeFolder()])));
|
|
82
|
-
export const
|
|
85
|
+
export const CreateVersionRequest = lazySchema(() => z.looseObject({
|
|
83
86
|
versionNumber: z.string().regex(/^([0-9]|[1-9][0-9])\.([0-9]|[1-9][0-9])$/),
|
|
84
|
-
sourceType:
|
|
85
|
-
envVars: z.array(
|
|
87
|
+
sourceType: WritableVersionSourceType(),
|
|
88
|
+
envVars: z.array(EnvVarRequest()).optional(),
|
|
86
89
|
applyEnvVarsToBuild: z.boolean().nullable().optional(),
|
|
87
90
|
buildTag: z.string().nullable().optional(),
|
|
88
91
|
sourceFiles: VersionSourceFiles().optional(),
|
|
89
|
-
gitRepoUrl: z.string().
|
|
90
|
-
tarballUrl: z.string().
|
|
91
|
-
gitHubGistUrl: z.string().
|
|
92
|
+
gitRepoUrl: z.string().optional(),
|
|
93
|
+
tarballUrl: z.string().optional(),
|
|
94
|
+
gitHubGistUrl: z.string().optional(),
|
|
92
95
|
}));
|
|
93
|
-
/** Determines the permission level that the Actor requires to run. For details, see [Actor permissions](https://docs.apify.com/platform/actors/development/permissions). */
|
|
94
|
-
export const ActorPermissionLevel = lazySchema(() => z.enum(["LIMITED_PERMISSIONS", "FULL_PERMISSIONS"]).or(z.string()));
|
|
95
96
|
/** The default settings applied to an Actor run. Can be overridden elsewhere. */
|
|
96
97
|
export const DefaultRunOptions = lazySchema(() => z.looseObject({
|
|
97
98
|
build: z.string().optional(),
|
|
@@ -119,17 +120,35 @@ export const CreateActorRequest = lazySchema(() => z.looseObject({
|
|
|
119
120
|
name: z.string().nullable().optional(),
|
|
120
121
|
description: z.string().nullable().optional(),
|
|
121
122
|
title: z.string().nullable().optional(),
|
|
122
|
-
isPublic: z.boolean().
|
|
123
|
+
isPublic: z.boolean().optional(),
|
|
124
|
+
actorPermissionLevel: ActorPermissionLevel().optional(),
|
|
123
125
|
seoTitle: z.string().nullable().optional(),
|
|
124
126
|
seoDescription: z.string().nullable().optional(),
|
|
125
127
|
restartOnError: z.boolean().optional(),
|
|
126
|
-
versions: z.array(
|
|
128
|
+
versions: z.array(CreateVersionRequest()).min(1).optional(),
|
|
127
129
|
categories: z.array(z.string()).nullable().optional(),
|
|
128
130
|
defaultRunOptions: DefaultRunOptions().optional(),
|
|
129
131
|
actorStandby: ActorStandby().nullable().optional(),
|
|
130
132
|
exampleRunInput: ExampleRunInput().nullable().optional(),
|
|
131
133
|
isDeprecated: z.boolean().nullable().optional(),
|
|
132
134
|
}));
|
|
135
|
+
export const VersionSourceType = lazySchema(() => z.enum(["SOURCE_FILES", "GIT_REPO", "TARBALL", "GITHUB_GIST", "SOURCE_CODE"]).or(z.string()));
|
|
136
|
+
export const EnvVar = lazySchema(() => z.looseObject({
|
|
137
|
+
name: z.string(),
|
|
138
|
+
value: z.string().optional(),
|
|
139
|
+
isSecret: z.boolean().nullable().optional(),
|
|
140
|
+
}));
|
|
141
|
+
export const Version = lazySchema(() => z.looseObject({
|
|
142
|
+
versionNumber: z.string().regex(/^([0-9]|[1-9][0-9])\.([0-9]|[1-9][0-9])$/),
|
|
143
|
+
sourceType: VersionSourceType().nullable(),
|
|
144
|
+
envVars: z.array(EnvVar()).nullable().optional(),
|
|
145
|
+
applyEnvVarsToBuild: z.boolean().nullable().optional(),
|
|
146
|
+
buildTag: z.string().nullable().optional(),
|
|
147
|
+
sourceFiles: VersionSourceFiles().optional(),
|
|
148
|
+
gitRepoUrl: z.string().nullable().optional(),
|
|
149
|
+
tarballUrl: z.string().nullable().optional(),
|
|
150
|
+
gitHubGistUrl: z.string().nullable().optional(),
|
|
151
|
+
}));
|
|
133
152
|
export const CommonActorPricingInfo = lazySchema(() => z.looseObject({
|
|
134
153
|
apifyMarginPercentage: z.number(),
|
|
135
154
|
createdAt: z.iso.datetime({ offset: true }).pipe(z.coerce.date()),
|
|
@@ -240,17 +259,6 @@ export const ActorResource = lazySchema(() => z.looseObject({
|
|
|
240
259
|
export const ActorResponse = lazySchema(() => z.looseObject({
|
|
241
260
|
data: ActorResource(),
|
|
242
261
|
}));
|
|
243
|
-
export const CreateOrUpdateVersionRequest = lazySchema(() => z.looseObject({
|
|
244
|
-
versionNumber: z.string().regex(/^([0-9]|[1-9][0-9])\.([0-9]|[1-9][0-9])$/).nullable().optional(),
|
|
245
|
-
sourceType: VersionSourceType().nullable().optional(),
|
|
246
|
-
envVars: z.array(EnvVar()).nullable().optional(),
|
|
247
|
-
applyEnvVarsToBuild: z.boolean().nullable().optional(),
|
|
248
|
-
buildTag: z.string().nullable().optional(),
|
|
249
|
-
sourceFiles: VersionSourceFiles().optional(),
|
|
250
|
-
gitRepoUrl: z.string().nullable().optional(),
|
|
251
|
-
tarballUrl: z.string().nullable().optional(),
|
|
252
|
-
gitHubGistUrl: z.string().nullable().optional(),
|
|
253
|
-
}));
|
|
254
262
|
/** The name of the build tag. */
|
|
255
263
|
export const UpdatedBuildProperty = lazySchema(() => z.looseObject({
|
|
256
264
|
buildId: z.string(),
|
|
@@ -259,12 +267,12 @@ export const UpdateActorRequest = lazySchema(() => z.looseObject({
|
|
|
259
267
|
name: z.string().optional(),
|
|
260
268
|
description: z.string().nullable().optional(),
|
|
261
269
|
isPublic: z.boolean().optional(),
|
|
262
|
-
actorPermissionLevel: ActorPermissionLevel().
|
|
270
|
+
actorPermissionLevel: ActorPermissionLevel().optional(),
|
|
263
271
|
seoTitle: z.string().nullable().optional(),
|
|
264
272
|
seoDescription: z.string().nullable().optional(),
|
|
265
273
|
title: z.string().nullable().optional(),
|
|
266
274
|
restartOnError: z.boolean().optional(),
|
|
267
|
-
versions: z.array(
|
|
275
|
+
versions: z.array(CreateVersionRequest()).min(1).optional(),
|
|
268
276
|
pricingInfos: z.array(ActorRunPricingInfo()).optional(),
|
|
269
277
|
categories: z.array(z.string()).nullable().optional(),
|
|
270
278
|
defaultRunOptions: DefaultRunOptions().nullable().optional(),
|
|
@@ -283,6 +291,17 @@ export const ListOfVersionsResponse = lazySchema(() => z.looseObject({
|
|
|
283
291
|
export const VersionResponse = lazySchema(() => z.looseObject({
|
|
284
292
|
data: Version(),
|
|
285
293
|
}));
|
|
294
|
+
export const UpdateVersionRequest = lazySchema(() => z.looseObject({
|
|
295
|
+
versionNumber: z.string().regex(/^([0-9]|[1-9][0-9])\.([0-9]|[1-9][0-9])$/).optional(),
|
|
296
|
+
sourceType: WritableVersionSourceType().optional(),
|
|
297
|
+
envVars: z.array(EnvVarRequest()).optional(),
|
|
298
|
+
applyEnvVarsToBuild: z.boolean().nullable().optional(),
|
|
299
|
+
buildTag: z.string().nullable().optional(),
|
|
300
|
+
sourceFiles: VersionSourceFiles().optional(),
|
|
301
|
+
gitRepoUrl: z.string().optional(),
|
|
302
|
+
tarballUrl: z.string().optional(),
|
|
303
|
+
gitHubGistUrl: z.string().optional(),
|
|
304
|
+
}));
|
|
286
305
|
export const ListOfEnvVars = lazySchema(() => z.looseObject({
|
|
287
306
|
total: z.int(),
|
|
288
307
|
items: z.array(EnvVar()),
|
|
@@ -290,7 +309,6 @@ export const ListOfEnvVars = lazySchema(() => z.looseObject({
|
|
|
290
309
|
export const ListOfEnvVarsResponse = lazySchema(() => z.looseObject({
|
|
291
310
|
data: ListOfEnvVars(),
|
|
292
311
|
}));
|
|
293
|
-
export const EnvVarRequest = lazySchema(() => EnvVar().required({ value: true }));
|
|
294
312
|
export const EnvVarResponse = lazySchema(() => z.looseObject({
|
|
295
313
|
data: EnvVar(),
|
|
296
314
|
}));
|
|
@@ -1228,6 +1246,10 @@ export const ListOfSchedules = lazySchema(() => PaginationResponse().extend({
|
|
|
1228
1246
|
export const ListOfSchedulesResponse = lazySchema(() => z.looseObject({
|
|
1229
1247
|
data: ListOfSchedules(),
|
|
1230
1248
|
}));
|
|
1249
|
+
/** Notification settings for this schedule. */
|
|
1250
|
+
export const ScheduleNotifications = lazySchema(() => z.looseObject({
|
|
1251
|
+
email: z.boolean().optional(),
|
|
1252
|
+
}));
|
|
1231
1253
|
export const ScheduleActionRunInput = lazySchema(() => z.looseObject({
|
|
1232
1254
|
body: z.string().nullable().optional(),
|
|
1233
1255
|
contentType: z.string().nullable().optional(),
|
|
@@ -1252,6 +1274,7 @@ export const CreateOrUpdateScheduleRequest = lazySchema(() => z.looseObject({
|
|
|
1252
1274
|
timezone: z.string().nullable().optional(),
|
|
1253
1275
|
description: z.string().nullable().optional(),
|
|
1254
1276
|
title: z.string().nullable().optional(),
|
|
1277
|
+
notifications: ScheduleNotifications().optional(),
|
|
1255
1278
|
actions: z.array(ScheduleCreateAction()).nullable().optional(),
|
|
1256
1279
|
}));
|
|
1257
1280
|
export const ScheduleActionRunActor = lazySchema(() => z.looseObject({
|
|
@@ -1271,9 +1294,7 @@ export const ScheduleAction = lazySchema(() => z.discriminatedUnion("type", [Sch
|
|
|
1271
1294
|
export const Schedule = lazySchema(() => ScheduleBase().extend({
|
|
1272
1295
|
description: z.string().nullable().optional(),
|
|
1273
1296
|
title: z.string().nullable().optional(),
|
|
1274
|
-
notifications:
|
|
1275
|
-
email: z.boolean().optional(),
|
|
1276
|
-
}).optional(),
|
|
1297
|
+
notifications: ScheduleNotifications().optional(),
|
|
1277
1298
|
actions: z.array(ScheduleAction()),
|
|
1278
1299
|
}));
|
|
1279
1300
|
export const ScheduleResponse = lazySchema(() => z.looseObject({
|
|
@@ -27,9 +27,10 @@ export declare const httpClientOptionsShape: {
|
|
|
27
27
|
export type HttpResponseHeaders = Record<string, string | string[] | undefined>;
|
|
28
28
|
/**
|
|
29
29
|
* A request body as the transport receives it: already serialized, and compressed when that paid off. A `Readable`
|
|
30
|
-
* is passed through untouched and is only available in Node.js.
|
|
30
|
+
* is passed through untouched and is only available in Node.js. A `Blob` body reaches a Node.js transport as a
|
|
31
|
+
* fresh `Readable` over its bytes for every attempt, and a transport in any other runtime as the `Blob` itself.
|
|
31
32
|
*/
|
|
32
|
-
export type HttpRequestBody = string | Buffer | ArrayBuffer | ArrayBufferView | Readable;
|
|
33
|
+
export type HttpRequestBody = string | Buffer | ArrayBuffer | ArrayBufferView | Readable | Blob;
|
|
33
34
|
/**
|
|
34
35
|
* A response body as the transport hands it back: the raw bytes, a `Readable` when the request asked for a streamed
|
|
35
36
|
* response, or `undefined` when the response carried no body.
|
|
@@ -63,8 +64,9 @@ export interface ApifyRequestConfig {
|
|
|
63
64
|
* Request body. A plain object or array is serialized to JSON and sent as `application/json`, or form-encoded
|
|
64
65
|
* when a `Content-Type: application/x-www-form-urlencoded` header asks for it. A string, binary value
|
|
65
66
|
* (`Buffer`, `ArrayBuffer`, typed array) or `Readable` is sent as it is, and `URLSearchParams` is sent
|
|
66
|
-
* form-encoded. A `Blob
|
|
67
|
-
*
|
|
67
|
+
* form-encoded. A `Blob` or `File`, such as one from `fs.openAsBlob()`, is streamed without being held in
|
|
68
|
+
* memory, under its own `type` when no `Content-Type` header is set. A `FormData` or web `ReadableStream` is
|
|
69
|
+
* rejected with a `TypeError`, since serializing one would send an empty body.
|
|
68
70
|
*/
|
|
69
71
|
data?: unknown;
|
|
70
72
|
/**
|
|
@@ -290,8 +292,10 @@ export declare abstract class HttpClient {
|
|
|
290
292
|
* Network errors the transport classifies as retryable, rate limits (HTTP 429) and server errors (HTTP 5xx)
|
|
291
293
|
* are retried up to {@link maxRetries} times. Any other error status is thrown as {@link ApifyApiError} right
|
|
292
294
|
* away. A request whose body is a `Readable` is never retried, since part of the stream has already been
|
|
293
|
-
* consumed by the time the failure shows.
|
|
294
|
-
*
|
|
295
|
+
* consumed by the time the failure shows. A `Blob` body is read again for every attempt, so it is retried like
|
|
296
|
+
* any other, unless reading the blob itself fails: that error is thrown right away, with the transport error
|
|
297
|
+
* as its `cause` when that error does not already wrap it. Aborting `config.signal` ends the attempt in flight,
|
|
298
|
+
* skips the remaining retries and rejects the call with the signal's `reason`.
|
|
295
299
|
*
|
|
296
300
|
* @template T - Type of the parsed response body.
|
|
297
301
|
* @param config - The request to make.
|
|
@@ -9,7 +9,7 @@ import { InvalidResponseBodyError } from '../invalid_response_body_error.js';
|
|
|
9
9
|
import { Statistics } from '../statistics.js';
|
|
10
10
|
import { DEFAULT_TIMEOUT_LONG_SECS, DEFAULT_TIMEOUT_MAX_SECS, DEFAULT_TIMEOUT_MEDIUM_SECS, DEFAULT_TIMEOUT_SHORT_SECS, } from '../timeouts.js';
|
|
11
11
|
import { runtime } from '#runtime';
|
|
12
|
-
import { asArray, getEnv, isBuffer, isCompressibleContentType, isStream, MIN_COMPRESS_BYTES, parseArgument, sleep, toBytes, version, } from '../utils.js';
|
|
12
|
+
import { asArray, getEnv, isBlob, isBuffer, isCompressibleContentType, isStream, MIN_COMPRESS_BYTES, parseArgument, sleep, toBytes, version, } from '../utils.js';
|
|
13
13
|
export const DEFAULT_MAX_RETRIES = 8;
|
|
14
14
|
export const DEFAULT_MIN_DELAY_BETWEEN_RETRIES_MILLIS = 500;
|
|
15
15
|
const RATE_LIMIT_EXCEEDED_STATUS_CODE = 429;
|
|
@@ -29,7 +29,7 @@ export const httpClientOptionsShape = {
|
|
|
29
29
|
};
|
|
30
30
|
const httpClientOptionsSchema = z.looseObject(httpClientOptionsShape).partial();
|
|
31
31
|
/** Objects whose payload does not live in their own enumerable keys, so JSON serialization loses it. */
|
|
32
|
-
const UNSERIALIZABLE_OBJECT_TAGS = new Set(['
|
|
32
|
+
const UNSERIALIZABLE_OBJECT_TAGS = new Set(['FormData', 'ReadableStream']);
|
|
33
33
|
/**
|
|
34
34
|
* Base class for the HTTP clients used by {@link ApifyClient}.
|
|
35
35
|
*
|
|
@@ -183,8 +183,10 @@ export class HttpClient {
|
|
|
183
183
|
* Network errors the transport classifies as retryable, rate limits (HTTP 429) and server errors (HTTP 5xx)
|
|
184
184
|
* are retried up to {@link maxRetries} times. Any other error status is thrown as {@link ApifyApiError} right
|
|
185
185
|
* away. A request whose body is a `Readable` is never retried, since part of the stream has already been
|
|
186
|
-
* consumed by the time the failure shows.
|
|
187
|
-
*
|
|
186
|
+
* consumed by the time the failure shows. A `Blob` body is read again for every attempt, so it is retried like
|
|
187
|
+
* any other, unless reading the blob itself fails: that error is thrown right away, with the transport error
|
|
188
|
+
* as its `cause` when that error does not already wrap it. Aborting `config.signal` ends the attempt in flight,
|
|
189
|
+
* skips the remaining retries and rejects the call with the signal's `reason`.
|
|
188
190
|
*
|
|
189
191
|
* @template T - Type of the parsed response body.
|
|
190
192
|
* @param config - The request to make.
|
|
@@ -219,6 +221,11 @@ export class HttpClient {
|
|
|
219
221
|
headers = mergeHeaders(headers, { 'Content-Encoding': this.httpCompressor.contentEncoding });
|
|
220
222
|
deleteHeader(headers, 'content-length');
|
|
221
223
|
}
|
|
224
|
+
// A blob reaches a Node.js transport as a stream, which would go out chunked without a known length.
|
|
225
|
+
// Browsers set the header themselves and refuse to let a page set it.
|
|
226
|
+
if (isBlob(body) && runtime.isNode) {
|
|
227
|
+
headers = mergeHeaders(headers, { 'Content-Length': String(body.size) });
|
|
228
|
+
}
|
|
222
229
|
return { headers, body };
|
|
223
230
|
}
|
|
224
231
|
/**
|
|
@@ -303,13 +310,20 @@ export class HttpClient {
|
|
|
303
310
|
const { config, url, headers, body, attempt, stopRetrying } = options;
|
|
304
311
|
this.stats.requests++;
|
|
305
312
|
const requestIsStream = isStream(config.data);
|
|
313
|
+
// The transport reports a failure of the blob as an error of its own, which may pass as transient.
|
|
314
|
+
let sourceFailure;
|
|
315
|
+
const attemptBody = isBlob(body)
|
|
316
|
+
? runtime.openBlobBody(body, (error) => {
|
|
317
|
+
sourceFailure = { error };
|
|
318
|
+
})
|
|
319
|
+
: body;
|
|
306
320
|
let response;
|
|
307
321
|
try {
|
|
308
322
|
response = await this.sendRequest({
|
|
309
323
|
method: config.method,
|
|
310
324
|
url,
|
|
311
325
|
headers,
|
|
312
|
-
body,
|
|
326
|
+
body: attemptBody,
|
|
313
327
|
timeoutMillis: this.computeTimeoutMillis(attempt, config.timeoutSecs),
|
|
314
328
|
stream: config.responseType === 'stream',
|
|
315
329
|
signal: config.signal,
|
|
@@ -318,9 +332,20 @@ export class HttpClient {
|
|
|
318
332
|
catch (err) {
|
|
319
333
|
// Every HTTP library reports an abort with an error of its own, so the caller gets the reason instead.
|
|
320
334
|
config.signal?.throwIfAborted();
|
|
335
|
+
if (sourceFailure) {
|
|
336
|
+
// Sending the body again cannot fix its source.
|
|
337
|
+
stopRetrying();
|
|
338
|
+
throw withCause(sourceFailure.error, err);
|
|
339
|
+
}
|
|
321
340
|
this.#handleRequestError(err, config, stopRetrying);
|
|
322
341
|
throw err;
|
|
323
342
|
}
|
|
343
|
+
finally {
|
|
344
|
+
// A transport may stop reading the body early, for example on an error response sent before the whole
|
|
345
|
+
// body arrived, which would leave the blob's file open.
|
|
346
|
+
if (attemptBody !== body && isStream(attemptBody))
|
|
347
|
+
attemptBody.destroy();
|
|
348
|
+
}
|
|
324
349
|
// A failed streaming request carries the API error body in the stream, so read it and let
|
|
325
350
|
// `ApifyApiError` parse it like any other error body. A body that cannot be read leaves the error
|
|
326
351
|
// without a message, which beats losing the status code to a stream error.
|
|
@@ -451,7 +476,7 @@ function deleteHeader(headers, name) {
|
|
|
451
476
|
/**
|
|
452
477
|
* Views a serialized body as the bytes to compress, or `undefined` when compressing it would not pay off: the
|
|
453
478
|
* caller already labeled the body with a `Content-Encoding`, its content type carries its own compression, it is
|
|
454
|
-
* a stream or smaller than {@link MIN_COMPRESS_BYTES}, or the runtime has no compression to offer.
|
|
479
|
+
* a stream or a blob, it is smaller than {@link MIN_COMPRESS_BYTES}, or the runtime has no compression to offer.
|
|
455
480
|
*/
|
|
456
481
|
function compressibleBytes(body, headers) {
|
|
457
482
|
if (body === undefined || !runtime.isNode)
|
|
@@ -463,6 +488,22 @@ function compressibleBytes(body, headers) {
|
|
|
463
488
|
const bytes = toBytes(body);
|
|
464
489
|
return bytes && bytes.byteLength >= MIN_COMPRESS_BYTES ? bytes : undefined;
|
|
465
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* Sets `cause` on an error that has none, and returns the error. A `cause` that already wraps the error, as an axios
|
|
493
|
+
* error wraps the error of its request body, is left out, since linking it would make the chain circular.
|
|
494
|
+
*/
|
|
495
|
+
function withCause(error, cause) {
|
|
496
|
+
if (!(error instanceof Error) || error.cause !== undefined)
|
|
497
|
+
return error;
|
|
498
|
+
const seen = new Set();
|
|
499
|
+
for (let link = cause; link instanceof Error && !seen.has(link); link = link.cause) {
|
|
500
|
+
if (link === error)
|
|
501
|
+
return error;
|
|
502
|
+
seen.add(link);
|
|
503
|
+
}
|
|
504
|
+
error.cause = cause;
|
|
505
|
+
return error;
|
|
506
|
+
}
|
|
466
507
|
function headerValue(value) {
|
|
467
508
|
return Array.isArray(value) ? value[0] : value;
|
|
468
509
|
}
|
|
@@ -481,6 +522,11 @@ function serializeBody(data, headers, stringifyFunctions) {
|
|
|
481
522
|
return undefined;
|
|
482
523
|
if (isStream(data))
|
|
483
524
|
return data;
|
|
525
|
+
if (isBlob(data)) {
|
|
526
|
+
if (getHeader(headers, 'content-type') === undefined && data.type)
|
|
527
|
+
headers['Content-Type'] = data.type;
|
|
528
|
+
return data;
|
|
529
|
+
}
|
|
484
530
|
if (isBuffer(data)) {
|
|
485
531
|
// The axios Node.js adapter refuses a body that is neither a `Buffer`, an `ArrayBuffer` nor a string.
|
|
486
532
|
if (!runtime.isNode || Buffer.isBuffer(data))
|
package/dist/models.d.ts
CHANGED
|
@@ -437,6 +437,7 @@ export interface ActorListItem extends Omit<Schemas['ActorListItem'], keyof Acto
|
|
|
437
437
|
type GeneratedBuildUsage = Schemas['BuildUsage'];
|
|
438
438
|
type GeneratedBuildStats = Schemas['BuildStats'];
|
|
439
439
|
type GeneratedBuildOptions = Schemas['BuildOptions'];
|
|
440
|
+
type GeneratedBuildActVersion = NonNullable<Schemas['Build']['actVersion']>;
|
|
440
441
|
/**
|
|
441
442
|
* Resource usage for an Actor build.
|
|
442
443
|
* @since Added in 2.7.2
|
|
@@ -463,6 +464,10 @@ export interface BuildRePointed {
|
|
|
463
464
|
usageUsd?: BuildUsage | null;
|
|
464
465
|
actorDefinition?: ActorDefinition | null;
|
|
465
466
|
status: ValueOf<typeof ACTOR_JOB_STATUSES>;
|
|
467
|
+
/** The API returns `buildTag` as `null` for some builds, which the specification types as a string. */
|
|
468
|
+
actVersion?: Omit<GeneratedBuildActVersion, 'buildTag'> & {
|
|
469
|
+
buildTag?: string | null;
|
|
470
|
+
};
|
|
466
471
|
}
|
|
467
472
|
/**
|
|
468
473
|
* Represents an Actor build.
|
|
@@ -210,15 +210,18 @@ export declare class KeyValueStoreClient extends ResourceClient {
|
|
|
210
210
|
* to be valid JSON already, or {@link getRecord} fails to parse it back.
|
|
211
211
|
*
|
|
212
212
|
* **Note about streams:** If the value is a stream object (has `.pipe` and `.on` methods),
|
|
213
|
-
* the upload cannot be retried on failure or follow redirects. For reliable uploads,
|
|
214
|
-
*
|
|
213
|
+
* the upload cannot be retried on failure or follow redirects. For reliable uploads of a file,
|
|
214
|
+
* pass a `Blob` from `fs.openAsBlob()` instead: it is read again for every attempt and never held
|
|
215
|
+
* in memory whole.
|
|
215
216
|
*
|
|
216
217
|
* @param record - The record to store
|
|
217
218
|
* @param record.key - Record key (unique identifier)
|
|
218
|
-
* @param record.value - Record value (a JSON-serializable value, Buffer, ArrayBuffer, typed array, or
|
|
219
|
+
* @param record.value - Record value (a JSON-serializable value, Buffer, ArrayBuffer, typed array, Blob, or
|
|
220
|
+
* Readable)
|
|
219
221
|
* @param record.contentType - Optional MIME type. Auto-detected if not provided:
|
|
220
222
|
* - Objects: `'application/json; charset=utf-8'`
|
|
221
223
|
* - Strings: `'text/plain; charset=utf-8'`
|
|
224
|
+
* - Blobs: their `type`, or `'application/octet-stream'` when it is empty
|
|
222
225
|
* - Binary values and streams: `'application/octet-stream'`
|
|
223
226
|
*
|
|
224
227
|
* Worth setting for media and archives: the client skips compressing a body
|
|
@@ -349,10 +352,10 @@ export interface KeyValueClientGetRecordOptions extends TimeoutOptions {
|
|
|
349
352
|
* A value that `setRecord` accepts.
|
|
350
353
|
*
|
|
351
354
|
* Anything JSON-serializable, or binary content the client uploads as bytes instead of JSON-encoding
|
|
352
|
-
* it: a `Buffer`, an `ArrayBuffer`, a typed array, or a readable stream. `Buffer` is a
|
|
353
|
-
* `TypedArray` covers it.
|
|
355
|
+
* it: a `Buffer`, an `ArrayBuffer`, a typed array, a `Blob`, or a readable stream. `Buffer` is a
|
|
356
|
+
* `Uint8Array`, so `TypedArray` covers it.
|
|
354
357
|
*/
|
|
355
|
-
export type KeyValueStoreRecordValue = JsonValue | ArrayBuffer | TypedArray | Readable;
|
|
358
|
+
export type KeyValueStoreRecordValue = JsonValue | ArrayBuffer | TypedArray | Blob | Readable;
|
|
356
359
|
/**
|
|
357
360
|
* Represents a record (key-value pair) in a Key-Value Store.
|
|
358
361
|
*
|
|
@@ -5,7 +5,7 @@ import { ResourceClient } from '../base/resource_client.js';
|
|
|
5
5
|
import * as schemas from '../schemas.js';
|
|
6
6
|
import { timeoutOptionsSchema, timeoutOptionsShape } from '../timeouts.js';
|
|
7
7
|
import { runtime } from '#runtime';
|
|
8
|
-
import { anyObjectSchema, applyQueryParamsToUrl, catchNotFoundOrThrow, isBuffer, isStream, minForLimitParam, parseArgument, parseResponse, } from '../utils.js';
|
|
8
|
+
import { anyObjectSchema, applyQueryParamsToUrl, catchNotFoundOrThrow, isBlob, isBuffer, isStream, minForLimitParam, parseArgument, parseResponse, } from '../utils.js';
|
|
9
9
|
const listKeysOptionsSchema = z.strictObject({
|
|
10
10
|
limit: z.number().min(0).optional(),
|
|
11
11
|
chunkSize: z.number().positive().optional(),
|
|
@@ -364,15 +364,18 @@ export class KeyValueStoreClient extends ResourceClient {
|
|
|
364
364
|
* to be valid JSON already, or {@link getRecord} fails to parse it back.
|
|
365
365
|
*
|
|
366
366
|
* **Note about streams:** If the value is a stream object (has `.pipe` and `.on` methods),
|
|
367
|
-
* the upload cannot be retried on failure or follow redirects. For reliable uploads,
|
|
368
|
-
*
|
|
367
|
+
* the upload cannot be retried on failure or follow redirects. For reliable uploads of a file,
|
|
368
|
+
* pass a `Blob` from `fs.openAsBlob()` instead: it is read again for every attempt and never held
|
|
369
|
+
* in memory whole.
|
|
369
370
|
*
|
|
370
371
|
* @param record - The record to store
|
|
371
372
|
* @param record.key - Record key (unique identifier)
|
|
372
|
-
* @param record.value - Record value (a JSON-serializable value, Buffer, ArrayBuffer, typed array, or
|
|
373
|
+
* @param record.value - Record value (a JSON-serializable value, Buffer, ArrayBuffer, typed array, Blob, or
|
|
374
|
+
* Readable)
|
|
373
375
|
* @param record.contentType - Optional MIME type. Auto-detected if not provided:
|
|
374
376
|
* - Objects: `'application/json; charset=utf-8'`
|
|
375
377
|
* - Strings: `'text/plain; charset=utf-8'`
|
|
378
|
+
* - Blobs: their `type`, or `'application/octet-stream'` when it is empty
|
|
376
379
|
* - Binary values and streams: `'application/octet-stream'`
|
|
377
380
|
*
|
|
378
381
|
* Worth setting for media and archives: the client skips compressing a body
|
|
@@ -435,18 +438,20 @@ export class KeyValueStoreClient extends ResourceClient {
|
|
|
435
438
|
if (contentEncoding !== undefined && !runtime.isNode) {
|
|
436
439
|
throw new Error('The contentEncoding option can only be used in Node.js environment.');
|
|
437
440
|
}
|
|
438
|
-
const
|
|
441
|
+
const isBinaryValue = isStream(value) || isBuffer(value) || isBlob(value);
|
|
439
442
|
// The client forwards the header without inspecting the body, so a string or a value serialized to JSON, which
|
|
440
443
|
// cannot hold compressed bytes, would be stored under an encoding that misdescribes it.
|
|
441
444
|
const declaresCompression = contentEncoding !== undefined && contentEncoding.trim().toLowerCase() !== 'identity';
|
|
442
|
-
if (declaresCompression && !
|
|
445
|
+
if (declaresCompression && !isBinaryValue) {
|
|
443
446
|
throw new TypeError(`Cannot upload a value of type ${typeof value} with Content-Encoding: ${contentEncoding}. ` +
|
|
444
447
|
'An encoding other than identity declares the value is already compressed, so pass the ' +
|
|
445
|
-
'compressed bytes as a Buffer, an ArrayBuffer, a typed array, or a readable stream.');
|
|
448
|
+
'compressed bytes as a Buffer, an ArrayBuffer, a typed array, a Blob, or a readable stream.');
|
|
446
449
|
}
|
|
447
450
|
// To allow saving Objects to JSON without providing content type
|
|
448
451
|
if (!contentType) {
|
|
449
|
-
if (
|
|
452
|
+
if (isBlob(value))
|
|
453
|
+
contentType = value.type || 'application/octet-stream';
|
|
454
|
+
else if (isBinaryValue)
|
|
450
455
|
contentType = 'application/octet-stream';
|
|
451
456
|
else if (typeof value === 'string')
|
|
452
457
|
contentType = 'text/plain; charset=utf-8';
|
|
@@ -454,7 +459,7 @@ export class KeyValueStoreClient extends ResourceClient {
|
|
|
454
459
|
contentType = 'application/json; charset=utf-8';
|
|
455
460
|
}
|
|
456
461
|
const isContentTypeJson = contentType.startsWith('application/json');
|
|
457
|
-
if (isContentTypeJson && !
|
|
462
|
+
if (isContentTypeJson && !isBinaryValue && typeof value !== 'string') {
|
|
458
463
|
try {
|
|
459
464
|
value = JSON.stringify(value, null, 2);
|
|
460
465
|
}
|
package/dist/runtime/node.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
2
3
|
import { brotliCompress, constants, gzip } from 'node:zlib';
|
|
3
4
|
/**
|
|
4
5
|
* The Node.js implementation of {@link Runtime}. Deno and Bun resolve the `node` condition too and
|
|
@@ -46,4 +47,34 @@ export const runtime = {
|
|
|
46
47
|
});
|
|
47
48
|
return { httpAgent: agent, httpsAgent: agent };
|
|
48
49
|
},
|
|
50
|
+
openBlobBody(blob, onSourceError) {
|
|
51
|
+
return Readable.from(readBlob(blob, onSourceError), { objectMode: false });
|
|
52
|
+
},
|
|
49
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Yields the bytes of a blob. Ending the iteration early, which destroying the `Readable` around it does, cancels
|
|
56
|
+
* the read and releases a file the blob reads from. Only a failing read is reported: an error the `Readable` is
|
|
57
|
+
* destroyed with is thrown in at the `yield` and belongs to the transport. A `for await` loop would surface both at
|
|
58
|
+
* the same place, so the loop calls `next()` itself to catch only the read.
|
|
59
|
+
*/
|
|
60
|
+
async function* readBlob(blob, onSourceError) {
|
|
61
|
+
const chunks = blob.stream()[Symbol.asyncIterator]();
|
|
62
|
+
try {
|
|
63
|
+
while (true) {
|
|
64
|
+
let chunk;
|
|
65
|
+
try {
|
|
66
|
+
chunk = await chunks.next();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
onSourceError(error);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
if (chunk.done)
|
|
73
|
+
return;
|
|
74
|
+
yield chunk.value;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
await chunks.return?.();
|
|
79
|
+
}
|
|
80
|
+
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type http from 'node:http';
|
|
2
2
|
import type https from 'node:https';
|
|
3
|
+
import type { Readable } from 'node:stream';
|
|
3
4
|
/**
|
|
4
5
|
* What to compress a request body with, as the built-in `HttpCompressor` implementations ask for it.
|
|
5
6
|
* @internal
|
|
@@ -49,4 +50,11 @@ export interface Runtime {
|
|
|
49
50
|
createHttpAgents(options: {
|
|
50
51
|
timeoutMillis: number;
|
|
51
52
|
}): Promise<HttpAgents | undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* Opens a `Blob` request body for one attempt. The Node.js implementation reads the blob into a `Readable` and
|
|
55
|
+
* reports an error the blob raises while it is read to `onSourceError` before the stream fails with it, so the
|
|
56
|
+
* pipeline can tell a failing source apart from a failing connection. The Web API one returns the blob itself,
|
|
57
|
+
* which `fetch` and `XMLHttpRequest` read on their own, and never calls `onSourceError`.
|
|
58
|
+
*/
|
|
59
|
+
openBlobBody(blob: Blob, onSourceError: (error: unknown) => void): Readable | Blob;
|
|
52
60
|
}
|