apify-client 3.0.0-beta.71 → 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/http_clients/base.d.ts +10 -6
- package/dist/http_clients/base.js +52 -6
- 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/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/package.json +1 -1
|
@@ -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))
|
|
@@ -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
|
}
|
package/dist/runtime/web.js
CHANGED
package/dist/utils.d.ts
CHANGED
|
@@ -101,6 +101,11 @@ export declare function splitIntoJsonArrayBatches<T extends {
|
|
|
101
101
|
maxByteLength: number;
|
|
102
102
|
}): T[][];
|
|
103
103
|
export declare function isBuffer(value: unknown): value is Buffer | ArrayBuffer | TypedArray;
|
|
104
|
+
/**
|
|
105
|
+
* Whether a value is a `Blob`, a `File` included. Unlike a stream, a blob can be read again for every attempt of a
|
|
106
|
+
* request, so a request with a blob body can be retried.
|
|
107
|
+
*/
|
|
108
|
+
export declare function isBlob(value: unknown): value is Blob;
|
|
104
109
|
export declare function isStream(value: unknown): value is Readable;
|
|
105
110
|
/**
|
|
106
111
|
* Helper class to create async iterators from paginated list endpoints.
|
package/dist/utils.js
CHANGED
|
@@ -236,6 +236,13 @@ export function isBuffer(value) {
|
|
|
236
236
|
return !isTagged(value, 'DataView');
|
|
237
237
|
return isTagged(value, 'ArrayBuffer');
|
|
238
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Whether a value is a `Blob`, a `File` included. Unlike a stream, a blob can be read again for every attempt of a
|
|
241
|
+
* request, so a request with a blob body can be retried.
|
|
242
|
+
*/
|
|
243
|
+
export function isBlob(value) {
|
|
244
|
+
return isTagged(value, 'Blob') || isTagged(value, 'File');
|
|
245
|
+
}
|
|
239
246
|
function isTagged(value, tag) {
|
|
240
247
|
return Object.prototype.toString.call(value) === `[object ${tag}]`;
|
|
241
248
|
}
|