apify-client 3.0.0-beta.2 → 3.0.0-beta.20
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 +3 -1
- package/dist/apify_api_error.d.ts +60 -19
- package/dist/apify_api_error.js +88 -21
- package/dist/apify_client.d.ts +22 -3
- package/dist/apify_client.js +35 -29
- package/dist/base/api_client.d.ts +6 -6
- package/dist/base/api_client.js +24 -16
- package/dist/base/resource_client.d.ts +23 -9
- package/dist/base/resource_client.js +49 -25
- package/dist/base/resource_collection_client.d.ts +7 -4
- package/dist/base/resource_collection_client.js +21 -15
- package/dist/bundle.js +38 -48
- package/dist/bundle.js.map +1 -1
- package/dist/generated/api.d.ts +287 -214
- package/dist/generated/schemas.d.ts +6699 -0
- package/dist/generated/schemas.js +1521 -0
- package/dist/http_client.d.ts +18 -51
- package/dist/http_client.js +79 -42
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/interceptors.js +22 -4
- package/dist/lazy_schema.d.ts +8 -0
- package/dist/lazy_schema.js +11 -0
- package/dist/models.d.ts +32 -52
- package/dist/resource_clients/actor.d.ts +46 -31
- package/dist/resource_clients/actor.js +71 -53
- package/dist/resource_clients/actor_collection.d.ts +9 -5
- package/dist/resource_clients/actor_collection.js +12 -3
- package/dist/resource_clients/actor_env_var.d.ts +10 -3
- package/dist/resource_clients/actor_env_var.js +17 -6
- package/dist/resource_clients/actor_env_var_collection.d.ts +14 -18
- package/dist/resource_clients/actor_env_var_collection.js +18 -11
- package/dist/resource_clients/actor_version.d.ts +18 -3
- package/dist/resource_clients/actor_version.js +20 -9
- package/dist/resource_clients/actor_version_collection.d.ts +16 -17
- package/dist/resource_clients/actor_version_collection.js +20 -11
- package/dist/resource_clients/build.d.ts +18 -6
- package/dist/resource_clients/build.js +35 -17
- package/dist/resource_clients/build_collection.d.ts +3 -1
- package/dist/resource_clients/build_collection.js +5 -1
- package/dist/resource_clients/dataset.d.ts +23 -9
- package/dist/resource_clients/dataset.js +70 -48
- package/dist/resource_clients/dataset_collection.d.ts +6 -2
- package/dist/resource_clients/dataset_collection.js +13 -2
- package/dist/resource_clients/key_value_store.d.ts +48 -15
- package/dist/resource_clients/key_value_store.js +69 -39
- package/dist/resource_clients/key_value_store_collection.d.ts +6 -2
- package/dist/resource_clients/key_value_store_collection.js +13 -2
- package/dist/resource_clients/log.d.ts +9 -19
- package/dist/resource_clients/log.js +55 -44
- package/dist/resource_clients/request_queue.d.ts +56 -26
- package/dist/resource_clients/request_queue.js +178 -113
- package/dist/resource_clients/request_queue_collection.d.ts +6 -2
- package/dist/resource_clients/request_queue_collection.js +10 -3
- package/dist/resource_clients/run.d.ts +51 -17
- package/dist/resource_clients/run.js +83 -43
- package/dist/resource_clients/run_collection.d.ts +3 -1
- package/dist/resource_clients/run_collection.js +5 -1
- package/dist/resource_clients/schedule.d.ts +16 -7
- package/dist/resource_clients/schedule.js +31 -21
- package/dist/resource_clients/schedule_collection.d.ts +6 -2
- package/dist/resource_clients/schedule_collection.js +10 -3
- package/dist/resource_clients/store_collection.d.ts +3 -1
- package/dist/resource_clients/store_collection.js +5 -1
- package/dist/resource_clients/task.d.ts +28 -10
- package/dist/resource_clients/task.js +67 -44
- package/dist/resource_clients/task_collection.d.ts +6 -2
- package/dist/resource_clients/task_collection.js +10 -3
- package/dist/resource_clients/user.d.ts +17 -8
- package/dist/resource_clients/user.js +40 -39
- package/dist/resource_clients/webhook.d.ts +14 -5
- package/dist/resource_clients/webhook.js +30 -22
- package/dist/resource_clients/webhook_collection.d.ts +6 -2
- package/dist/resource_clients/webhook_collection.js +10 -3
- package/dist/resource_clients/webhook_dispatch.d.ts +4 -1
- package/dist/resource_clients/webhook_dispatch.js +8 -2
- package/dist/resource_clients/webhook_dispatch_collection.d.ts +3 -1
- package/dist/resource_clients/webhook_dispatch_collection.js +5 -1
- package/dist/response_validation_error.d.ts +26 -0
- package/dist/response_validation_error.js +37 -0
- package/dist/schemas.d.ts +15 -0
- package/dist/schemas.js +15 -0
- package/dist/statistics.d.ts +1 -6
- package/dist/statistics.js +2 -2
- package/dist/timeouts.d.ts +72 -0
- package/dist/timeouts.js +31 -0
- package/dist/utils.d.ts +57 -41
- package/dist/utils.js +155 -121
- package/package.json +36 -35
- package/dist/argument_validation_error.d.ts +0 -17
- package/dist/argument_validation_error.js +0 -153
package/dist/timeouts.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** Default duration of the `short` timeout tier, in seconds. */
|
|
3
|
+
export const DEFAULT_TIMEOUT_SHORT_SECS = 5;
|
|
4
|
+
/** Default duration of the `medium` timeout tier, in seconds. */
|
|
5
|
+
export const DEFAULT_TIMEOUT_MEDIUM_SECS = 30;
|
|
6
|
+
/** Default duration of the `long` timeout tier, in seconds. */
|
|
7
|
+
export const DEFAULT_TIMEOUT_LONG_SECS = 360;
|
|
8
|
+
/** Default cap on the timeout of a single request attempt, in seconds. */
|
|
9
|
+
export const DEFAULT_TIMEOUT_MAX_SECS = 360;
|
|
10
|
+
/**
|
|
11
|
+
* Schema of {@link Timeout}. Zero is rejected on purpose - `'noTimeout'` is the explicit way to run without one.
|
|
12
|
+
*/
|
|
13
|
+
const timeoutSchema = z.union([z.enum(['short', 'medium', 'long', 'noTimeout']), z.number().positive()]);
|
|
14
|
+
/**
|
|
15
|
+
* Schema of {@link Timeout}, optional, for the methods that validate `timeoutSecs` on its own.
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export const optionalTimeoutSchema = timeoutSchema.optional();
|
|
19
|
+
/**
|
|
20
|
+
* Schema shape of {@link TimeoutOptions}, to spread into the option schema of every method that sends a
|
|
21
|
+
* request. One copy stops it drifting from the interface.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export const timeoutOptionsShape = {
|
|
25
|
+
timeoutSecs: optionalTimeoutSchema,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Schema of {@link TimeoutOptions}, for the methods that take no other option.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
export const timeoutOptionsSchema = z.strictObject(timeoutOptionsShape);
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
|
-
import type {
|
|
2
|
+
import type { TypedArray } from 'type-fest';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import type { ApifyApiError } from './apify_api_error.js';
|
|
5
|
+
import { parseArgument } from '@apify/validations';
|
|
6
|
+
import type { ApifyResponse } from './http_client.js';
|
|
5
7
|
import type { RequestQueueClientListRequestsOptions, RequestQueueClientListRequestsResult } from './resource_clients/request_queue.js';
|
|
6
8
|
import type { WebhookUpdateData } from './resource_clients/webhook.js';
|
|
7
|
-
|
|
8
|
-
* Parses `value` with `schema`, returning the typed result (with schema defaults applied).
|
|
9
|
-
* Throws {@link ArgumentValidationError} on failure.
|
|
10
|
-
*
|
|
11
|
-
* The optional `label` names the interface being validated and is appended to every error line
|
|
12
|
-
* (e.g. ``... at `memory` in `ActorStartOptions` ``).
|
|
13
|
-
* @internal
|
|
14
|
-
*/
|
|
15
|
-
export declare function parseArgument<TValue, TSchema extends z.ZodType>(value: TValue, schema: TSchema, label?: string): TValue & z.output<TSchema>;
|
|
9
|
+
export { parseArgument };
|
|
16
10
|
/**
|
|
17
11
|
* Accepts any non-null, non-array object as a predicate for `z.custom()`.
|
|
18
12
|
* @internal
|
|
@@ -33,32 +27,34 @@ export declare const anyObjectSchema: z.ZodCustom<Record<string, unknown>, Recor
|
|
|
33
27
|
export interface MaybeData<R> {
|
|
34
28
|
data?: R;
|
|
35
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Turns a JSON API response into the value a resource method returns: unwraps the `data` envelope and validates the
|
|
32
|
+
* result against `schema`, one of the schemas generated from the OpenAPI specification. The validated copy is what
|
|
33
|
+
* callers get, so it is the schema's output -- unknown fields and unknown enum values included, since the schemas let
|
|
34
|
+
* both through, date-time fields turned into `Date` objects, and URL fields normalized, since `z.url()` hands back the
|
|
35
|
+
* parsed URL's serialization.
|
|
36
|
+
*
|
|
37
|
+
* Throws {@link ResponseValidationError} when the response does not match the specification.
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseResponse<R>(response: ApifyResponse, schema: z.ZodType): R;
|
|
36
41
|
/**
|
|
37
42
|
* Returns object's 'data' property or throws if parameter is not an object,
|
|
38
43
|
* or an object without a 'data' property.
|
|
39
44
|
*/
|
|
40
45
|
export declare function pluckData<R>(obj: MaybeData<R>): R;
|
|
41
46
|
/**
|
|
42
|
-
*
|
|
43
|
-
* Otherwise rethrows error.
|
|
47
|
+
* Swallows a 404 Not Found API error and rethrows anything else.
|
|
44
48
|
*/
|
|
45
49
|
export declare function catchNotFoundOrThrow(err: ApifyApiError): void;
|
|
46
|
-
type ReturnJsonValue = string | number | boolean | null | Date | ReturnJsonObject | ReturnJsonArray;
|
|
47
|
-
type ReturnJsonObject = {
|
|
48
|
-
[Key in string]?: ReturnJsonValue;
|
|
49
|
-
};
|
|
50
|
-
type ReturnJsonArray = ReturnJsonValue[];
|
|
51
50
|
/**
|
|
52
|
-
*
|
|
53
|
-
* "createdAt").
|
|
51
|
+
* Like `catchNotFoundOrThrow()`, but swallows the 404 only when the client names its resource by ID.
|
|
54
52
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* If the field cannot be converted to Date, it is left as is.
|
|
53
|
+
* A chained client without an ID, such as `run.dataset()` or `run.log()`, requests a path where a 404 can mean either
|
|
54
|
+
* the parent or the default sub-resource is missing. The response cannot tell the two apart, so the error propagates.
|
|
55
|
+
* @internal
|
|
60
56
|
*/
|
|
61
|
-
export declare function
|
|
57
|
+
export declare function catchNotFoundForResourceOrThrow(err: ApifyApiError, resourceId: string | undefined): void;
|
|
62
58
|
/**
|
|
63
59
|
* Helper function that converts array of webhooks to base64 string
|
|
64
60
|
*/
|
|
@@ -67,15 +63,37 @@ export interface CompressedValue {
|
|
|
67
63
|
data: Buffer;
|
|
68
64
|
encoding: 'br' | 'gzip';
|
|
69
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Decides whether a request body with the given content type is worth compressing.
|
|
68
|
+
*
|
|
69
|
+
* Images, audio, video and archives already carry their own compression. Running them through brotli or gzip
|
|
70
|
+
* burns CPU, holds a second full copy of the body in memory, and usually produces output slightly larger than
|
|
71
|
+
* the input. Formats that are raw despite such a media type, for example `image/bmp` or `audio/wav`, are still
|
|
72
|
+
* compressed. A body with no content type is assumed to be compressible.
|
|
73
|
+
* @internal
|
|
74
|
+
*/
|
|
75
|
+
export declare function isCompressibleContentType(contentType?: string): boolean;
|
|
70
76
|
/**
|
|
71
77
|
* Compress the passed value using brotli, falling back to gzip. Returns undefined if the data is
|
|
72
78
|
* too small / wrong type, or if neither algorithm is available.
|
|
73
79
|
*/
|
|
74
80
|
export declare function maybeCompressValue(value: unknown): Promise<CompressedValue | undefined>;
|
|
75
81
|
/**
|
|
76
|
-
*
|
|
82
|
+
* Returns the UTF-8 byte length of a string.
|
|
77
83
|
*/
|
|
78
|
-
export declare function
|
|
84
|
+
export declare function utf8ByteLength(value: string): number;
|
|
85
|
+
/**
|
|
86
|
+
* Splits JSON-serialized items into consecutive batches of at most `maxCount` items, each of which fits into a JSON
|
|
87
|
+
* array body - the items joined by commas between brackets - of at most `maxByteLength` bytes. The `byteLength` of an
|
|
88
|
+
* item is the UTF-8 byte length of its serialization. An item too large for a body of its own still gets one, so a
|
|
89
|
+
* caller that cannot send such an item has to reject it beforehand.
|
|
90
|
+
*/
|
|
91
|
+
export declare function splitIntoJsonArrayBatches<T extends {
|
|
92
|
+
byteLength: number;
|
|
93
|
+
}>(items: readonly T[], { maxCount, maxByteLength }: {
|
|
94
|
+
maxCount: number;
|
|
95
|
+
maxByteLength: number;
|
|
96
|
+
}): T[][];
|
|
79
97
|
export declare function isNode(): boolean;
|
|
80
98
|
export declare function isBuffer(value: unknown): value is Buffer | ArrayBuffer | TypedArray;
|
|
81
99
|
export declare function isStream(value: unknown): value is Readable;
|
|
@@ -83,14 +101,10 @@ export declare function getVersionData(): {
|
|
|
83
101
|
version: string;
|
|
84
102
|
};
|
|
85
103
|
/**
|
|
86
|
-
* Helper class to create async iterators from paginated list endpoints
|
|
104
|
+
* Helper class to create async iterators from paginated list endpoints.
|
|
87
105
|
*/
|
|
88
106
|
export declare class RequestQueuePaginationIterator {
|
|
89
|
-
private
|
|
90
|
-
private readonly getPage;
|
|
91
|
-
private readonly limit?;
|
|
92
|
-
private readonly exclusiveStartId?;
|
|
93
|
-
private readonly cursor?;
|
|
107
|
+
#private;
|
|
94
108
|
constructor(options: RequestQueuePaginationIteratorOptions);
|
|
95
109
|
[Symbol.asyncIterator](): AsyncIterator<RequestQueueClientListRequestsResult>;
|
|
96
110
|
}
|
|
@@ -105,7 +119,6 @@ export interface RequestQueuePaginationIteratorOptions {
|
|
|
105
119
|
maxPageLimit: number;
|
|
106
120
|
getPage: (opts: RequestQueueClientListRequestsOptions) => Promise<RequestQueueClientListRequestsResult>;
|
|
107
121
|
limit?: number;
|
|
108
|
-
exclusiveStartId?: string;
|
|
109
122
|
cursor?: string;
|
|
110
123
|
}
|
|
111
124
|
/**
|
|
@@ -164,6 +177,16 @@ export interface PaginatedList<Data> extends PaginatedResponse<Data> {
|
|
|
164
177
|
/** Should the results be in descending order. */
|
|
165
178
|
desc: boolean;
|
|
166
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Key under which a page of dataset items carries the number of rows the API scanned to produce it, as a
|
|
182
|
+
* non-enumerable property that stays out of the page's public shape. The API applies `offset` and `limit` to the
|
|
183
|
+
* dataset's rows first and shapes the result afterwards: filters (`clean`, `skipEmpty`, `skipHidden`) drop items and
|
|
184
|
+
* `unwind` splits or drops them, so `items.length` can land on either side of the rows scanned. The
|
|
185
|
+
* `x-apify-pagination-count` header reports the scanned number, and the offset iterator advances and terminates by it
|
|
186
|
+
* alone. A page without the property falls back to `items.length`.
|
|
187
|
+
* @internal
|
|
188
|
+
*/
|
|
189
|
+
export declare const SCANNED_COUNT: unique symbol;
|
|
167
190
|
/**
|
|
168
191
|
* Type representing both a Promise of a paginated list and an async iterable.
|
|
169
192
|
*
|
|
@@ -192,12 +215,6 @@ export type DistributiveOptional<T, K extends keyof T> = T extends any ? Omit<T,
|
|
|
192
215
|
* Adds query parameters to a given URL based on the provided options object.
|
|
193
216
|
*/
|
|
194
217
|
export declare function applyQueryParamsToUrl(url: URL, options?: Record<string, string | number | boolean | string[] | undefined>): URL;
|
|
195
|
-
/**
|
|
196
|
-
* Builds a `[check, message]` pair to spread into `.refine()`, asserting that at most one of `keys`
|
|
197
|
-
* is present. Pass the options interface as `T`, so that a misspelled key is a type error.
|
|
198
|
-
* @internal
|
|
199
|
-
*/
|
|
200
|
-
export declare const mutuallyExclusive: <T extends object>(...keys: (keyof T & string)[]) => [(value: T) => boolean, string];
|
|
201
218
|
/**
|
|
202
219
|
* Percent-encodes a caller-supplied URL path segment so it cannot restructure the request path.
|
|
203
220
|
*
|
|
@@ -210,4 +227,3 @@ export declare function toPathSegment(value: string): string;
|
|
|
210
227
|
* `requests/batch` keep their separators; an array has each of its segments encoded individually.
|
|
211
228
|
*/
|
|
212
229
|
export declare function toPath(path: string | string[]): string;
|
|
213
|
-
export {};
|
package/dist/utils.js
CHANGED
|
@@ -1,30 +1,56 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { NotFoundError } from './apify_api_error.js';
|
|
3
|
+
import { parseArgument } from '@apify/validations';
|
|
4
|
+
import { ResponseValidationError } from './response_validation_error.js';
|
|
3
5
|
// @ts-ignore if we enable `resolveJsonModule`, we end up with a `src` folder in `dist`
|
|
4
6
|
import packageJson from '../package.json' with { type: 'json' };
|
|
5
|
-
const NOT_FOUND_STATUS_CODE = 404;
|
|
6
|
-
const RECORD_NOT_FOUND_TYPE = 'record-not-found';
|
|
7
|
-
const RECORD_OR_TOKEN_NOT_FOUND_TYPE = 'record-or-token-not-found';
|
|
8
7
|
const MIN_COMPRESS_BYTES = 1024;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
/** Media type prefixes whose payloads carry their own compression, so compressing the request body is wasted work. */
|
|
9
|
+
const ALREADY_COMPRESSED_MEDIA_TYPE_PREFIXES = ['audio/', 'image/', 'video/'];
|
|
10
|
+
/** Exact media types whose payloads carry their own compression. */
|
|
11
|
+
const ALREADY_COMPRESSED_MEDIA_TYPES = new Set([
|
|
12
|
+
'application/epub+zip',
|
|
13
|
+
'application/gzip',
|
|
14
|
+
'application/java-archive',
|
|
15
|
+
'application/vnd.android.package-archive',
|
|
16
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
17
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
18
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
19
|
+
'application/vnd.rar',
|
|
20
|
+
'application/x-7z-compressed',
|
|
21
|
+
'application/x-bzip',
|
|
22
|
+
'application/x-bzip2',
|
|
23
|
+
'application/x-gzip',
|
|
24
|
+
'application/x-rar-compressed',
|
|
25
|
+
'application/x-xz',
|
|
26
|
+
'application/x-zip-compressed',
|
|
27
|
+
'application/zip',
|
|
28
|
+
'application/zstd',
|
|
29
|
+
'font/woff',
|
|
30
|
+
'font/woff2',
|
|
31
|
+
]);
|
|
32
|
+
/** Uncompressed media types that sit under an already-compressed prefix, so compressing them still pays off. */
|
|
33
|
+
const COMPRESSIBLE_MEDIA_TYPES = new Set([
|
|
34
|
+
'audio/aiff',
|
|
35
|
+
'audio/basic',
|
|
36
|
+
'audio/l16',
|
|
37
|
+
'audio/l24',
|
|
38
|
+
'audio/midi',
|
|
39
|
+
'audio/vnd.wave',
|
|
40
|
+
'audio/wav',
|
|
41
|
+
'audio/wave',
|
|
42
|
+
'audio/x-aiff',
|
|
43
|
+
'audio/x-wav',
|
|
44
|
+
'image/bmp',
|
|
45
|
+
'image/tiff',
|
|
46
|
+
'image/vnd.adobe.photoshop',
|
|
47
|
+
'image/vnd.microsoft.icon',
|
|
48
|
+
'image/x-icon',
|
|
49
|
+
'image/x-ms-bmp',
|
|
50
|
+
]);
|
|
51
|
+
/** Structured syntax suffixes marking a media type as text even under an already-compressed prefix (`image/svg+xml`). */
|
|
52
|
+
const COMPRESSIBLE_MEDIA_TYPE_SUFFIXES = ['+json', '+xml'];
|
|
53
|
+
export { parseArgument };
|
|
28
54
|
/**
|
|
29
55
|
* Accepts any non-null, non-array object as a predicate for `z.custom()`.
|
|
30
56
|
* @internal
|
|
@@ -41,6 +67,29 @@ export function isNonArrayObject(value) {
|
|
|
41
67
|
export const anyObjectSchema = z.custom(isNonArrayObject, {
|
|
42
68
|
error: 'Invalid input: expected an object',
|
|
43
69
|
});
|
|
70
|
+
// Zod installs its English locale as a module-level side effect but ships `"sideEffects": false`, so
|
|
71
|
+
// any tree-shaking bundler drops it and every message degrades to a bare "Invalid input". Passing it
|
|
72
|
+
// in per parse keeps them intact without reaching into the zod config the whole process shares.
|
|
73
|
+
const { localeError } = z.locales.en();
|
|
74
|
+
/**
|
|
75
|
+
* Turns a JSON API response into the value a resource method returns: unwraps the `data` envelope and validates the
|
|
76
|
+
* result against `schema`, one of the schemas generated from the OpenAPI specification. The validated copy is what
|
|
77
|
+
* callers get, so it is the schema's output -- unknown fields and unknown enum values included, since the schemas let
|
|
78
|
+
* both through, date-time fields turned into `Date` objects, and URL fields normalized, since `z.url()` hands back the
|
|
79
|
+
* parsed URL's serialization.
|
|
80
|
+
*
|
|
81
|
+
* Throws {@link ResponseValidationError} when the response does not match the specification.
|
|
82
|
+
* @internal
|
|
83
|
+
*/
|
|
84
|
+
export function parseResponse(response, schema) {
|
|
85
|
+
const data = pluckData(response.data);
|
|
86
|
+
const result = schema.safeParse(data, { error: localeError });
|
|
87
|
+
if (!result.success) {
|
|
88
|
+
const { method = 'GET', url = '' } = response.config;
|
|
89
|
+
throw new ResponseValidationError(result.error, data, { method, url });
|
|
90
|
+
}
|
|
91
|
+
return result.data;
|
|
92
|
+
}
|
|
44
93
|
/**
|
|
45
94
|
* Returns object's 'data' property or throws if parameter is not an object,
|
|
46
95
|
* or an object without a 'data' property.
|
|
@@ -53,62 +102,23 @@ export function pluckData(obj) {
|
|
|
53
102
|
throw new Error(`Expected response object with a "data" property, but received: ${obj}`);
|
|
54
103
|
}
|
|
55
104
|
/**
|
|
56
|
-
*
|
|
57
|
-
* Otherwise rethrows error.
|
|
105
|
+
* Swallows a 404 Not Found API error and rethrows anything else.
|
|
58
106
|
*/
|
|
59
107
|
export function catchNotFoundOrThrow(err) {
|
|
60
|
-
|
|
61
|
-
const isNotFoundMessage = err.type === RECORD_NOT_FOUND_TYPE || err.type === RECORD_OR_TOKEN_NOT_FOUND_TYPE || err.httpMethod === 'head';
|
|
62
|
-
const isNotFoundError = isNotFoundStatus && isNotFoundMessage;
|
|
63
|
-
if (!isNotFoundError)
|
|
108
|
+
if (!(err instanceof NotFoundError))
|
|
64
109
|
throw err;
|
|
65
110
|
}
|
|
66
111
|
/**
|
|
67
|
-
*
|
|
68
|
-
* "createdAt").
|
|
69
|
-
*
|
|
70
|
-
* If you want parse other fields as well, you can provide a custom matcher function shouldParseField(). This
|
|
71
|
-
* admittedly awkward approach allows this function to be reused for various purposes without introducing potential
|
|
72
|
-
* breaking changes.
|
|
112
|
+
* Like `catchNotFoundOrThrow()`, but swallows the 404 only when the client names its resource by ID.
|
|
73
113
|
*
|
|
74
|
-
*
|
|
114
|
+
* A chained client without an ID, such as `run.dataset()` or `run.log()`, requests a path where a 404 can mean either
|
|
115
|
+
* the parent or the default sub-resource is missing. The response cannot tell the two apart, so the error propagates.
|
|
116
|
+
* @internal
|
|
75
117
|
*/
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// array spend a level.
|
|
81
|
-
//
|
|
82
|
-
// In a list response it also reaches one level into caller-owned blobs the API stores verbatim, so a
|
|
83
|
-
// listed request's `userData.foo.somethingAt` comes back as a `Date` rather than the string it was
|
|
84
|
-
// written as.
|
|
85
|
-
// TODO: Consider removing this limitation. It might came across as an annoying surprise as it's not communicated.
|
|
86
|
-
if (depth > 4) {
|
|
87
|
-
return input;
|
|
88
|
-
}
|
|
89
|
-
if (Array.isArray(input))
|
|
90
|
-
return input.map((child) => parseDateFields(child, shouldParseField, depth + 1));
|
|
91
|
-
if (!input || typeof input !== 'object')
|
|
92
|
-
return input;
|
|
93
|
-
return Object.entries(input).reduce((output, [k, v]) => {
|
|
94
|
-
const isValObject = !!v && typeof v === 'object';
|
|
95
|
-
if (k.endsWith('At') || (shouldParseField && shouldParseField(k))) {
|
|
96
|
-
if (v) {
|
|
97
|
-
const d = new Date(v);
|
|
98
|
-
output[k] = Number.isNaN(d.getTime()) ? v : d;
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
output[k] = v;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
else if (isValObject || Array.isArray(v)) {
|
|
105
|
-
output[k] = parseDateFields(v, shouldParseField, depth + 1);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
output[k] = v;
|
|
109
|
-
}
|
|
110
|
-
return output;
|
|
111
|
-
}, {});
|
|
118
|
+
export function catchNotFoundForResourceOrThrow(err, resourceId) {
|
|
119
|
+
if (!resourceId)
|
|
120
|
+
throw err;
|
|
121
|
+
catchNotFoundOrThrow(err);
|
|
112
122
|
}
|
|
113
123
|
/**
|
|
114
124
|
* Helper function that converts array of webhooks to base64 string
|
|
@@ -150,6 +160,28 @@ async function gzipValue(value) {
|
|
|
150
160
|
}
|
|
151
161
|
return gzipPromisified(value);
|
|
152
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Decides whether a request body with the given content type is worth compressing.
|
|
165
|
+
*
|
|
166
|
+
* Images, audio, video and archives already carry their own compression. Running them through brotli or gzip
|
|
167
|
+
* burns CPU, holds a second full copy of the body in memory, and usually produces output slightly larger than
|
|
168
|
+
* the input. Formats that are raw despite such a media type, for example `image/bmp` or `audio/wav`, are still
|
|
169
|
+
* compressed. A body with no content type is assumed to be compressible.
|
|
170
|
+
* @internal
|
|
171
|
+
*/
|
|
172
|
+
export function isCompressibleContentType(contentType) {
|
|
173
|
+
if (!contentType)
|
|
174
|
+
return true;
|
|
175
|
+
// `Content-Type` is case-insensitive and may carry parameters, for example `text/plain; charset=utf-8`.
|
|
176
|
+
const mediaType = contentType.split(';', 1)[0].trim().toLowerCase();
|
|
177
|
+
if (COMPRESSIBLE_MEDIA_TYPES.has(mediaType))
|
|
178
|
+
return true;
|
|
179
|
+
if (COMPRESSIBLE_MEDIA_TYPE_SUFFIXES.some((suffix) => mediaType.endsWith(suffix)))
|
|
180
|
+
return true;
|
|
181
|
+
if (ALREADY_COMPRESSED_MEDIA_TYPES.has(mediaType))
|
|
182
|
+
return false;
|
|
183
|
+
return !ALREADY_COMPRESSED_MEDIA_TYPE_PREFIXES.some((prefix) => mediaType.startsWith(prefix));
|
|
184
|
+
}
|
|
153
185
|
/**
|
|
154
186
|
* Compress the passed value using brotli, falling back to gzip. Returns undefined if the data is
|
|
155
187
|
* too small / wrong type, or if neither algorithm is available.
|
|
@@ -181,28 +213,35 @@ export async function maybeCompressValue(value) {
|
|
|
181
213
|
}
|
|
182
214
|
}
|
|
183
215
|
/**
|
|
184
|
-
*
|
|
216
|
+
* Returns the UTF-8 byte length of a string.
|
|
217
|
+
*/
|
|
218
|
+
export function utf8ByteLength(value) {
|
|
219
|
+
return isNode() ? Buffer.byteLength(value) : new Blob([value]).size;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Splits JSON-serialized items into consecutive batches of at most `maxCount` items, each of which fits into a JSON
|
|
223
|
+
* array body - the items joined by commas between brackets - of at most `maxByteLength` bytes. The `byteLength` of an
|
|
224
|
+
* item is the UTF-8 byte length of its serialization. An item too large for a body of its own still gets one, so a
|
|
225
|
+
* caller that cannot send such an item has to reject it beforehand.
|
|
185
226
|
*/
|
|
186
|
-
export function
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
throw new Error(`RequestQueueClient.batchAddRequests: The size of the request with index: ${startIndex + i} ` +
|
|
198
|
-
`exceeds the maximum allowed size (${maxByteLength} bytes).`);
|
|
227
|
+
export function splitIntoJsonArrayBatches(items, { maxCount, maxByteLength }) {
|
|
228
|
+
const batches = [];
|
|
229
|
+
let batch = [];
|
|
230
|
+
// One byte for the opening bracket; each item then adds its own bytes plus one for the comma or the closing
|
|
231
|
+
// bracket that follows it.
|
|
232
|
+
let byteLength = 1;
|
|
233
|
+
for (const item of items) {
|
|
234
|
+
if (batch.length > 0 && (batch.length >= maxCount || byteLength + item.byteLength + 1 > maxByteLength)) {
|
|
235
|
+
batches.push(batch);
|
|
236
|
+
batch = [];
|
|
237
|
+
byteLength = 1;
|
|
199
238
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
byteLength += itemByteSize;
|
|
203
|
-
slicedArray.push(item);
|
|
239
|
+
batch.push(item);
|
|
240
|
+
byteLength += item.byteLength + 1;
|
|
204
241
|
}
|
|
205
|
-
|
|
242
|
+
if (batch.length > 0)
|
|
243
|
+
batches.push(batch);
|
|
244
|
+
return batches;
|
|
206
245
|
}
|
|
207
246
|
export function isNode() {
|
|
208
247
|
if (typeof BROWSER_BUILD !== 'undefined')
|
|
@@ -232,34 +271,29 @@ export function getVersionData() {
|
|
|
232
271
|
return packageJson;
|
|
233
272
|
}
|
|
234
273
|
/**
|
|
235
|
-
* Helper class to create async iterators from paginated list endpoints
|
|
274
|
+
* Helper class to create async iterators from paginated list endpoints.
|
|
236
275
|
*/
|
|
237
276
|
export class RequestQueuePaginationIterator {
|
|
238
|
-
maxPageLimit;
|
|
239
|
-
getPage;
|
|
240
|
-
limit;
|
|
241
|
-
|
|
242
|
-
cursor;
|
|
277
|
+
#maxPageLimit;
|
|
278
|
+
#getPage;
|
|
279
|
+
#limit;
|
|
280
|
+
#cursor;
|
|
243
281
|
constructor(options) {
|
|
244
|
-
this
|
|
245
|
-
this
|
|
246
|
-
this
|
|
247
|
-
this
|
|
248
|
-
this.getPage = options.getPage;
|
|
282
|
+
this.#maxPageLimit = options.maxPageLimit;
|
|
283
|
+
this.#limit = options.limit;
|
|
284
|
+
this.#cursor = options.cursor;
|
|
285
|
+
this.#getPage = options.getPage;
|
|
249
286
|
}
|
|
250
287
|
async *[Symbol.asyncIterator]() {
|
|
251
|
-
let nextCursor = this
|
|
252
|
-
// allow using exclusiveStartId for the first page, but then we'll delete it to avoid using it for any later page
|
|
253
|
-
let nextExclusiveStartId = this.exclusiveStartId;
|
|
288
|
+
let nextCursor = this.#cursor;
|
|
254
289
|
let iterateItemCount = 0;
|
|
255
290
|
while (true) {
|
|
256
|
-
const pageLimit = this
|
|
257
|
-
? Math.min(this
|
|
258
|
-
: this
|
|
259
|
-
const page = await this
|
|
291
|
+
const pageLimit = this.#limit
|
|
292
|
+
? Math.min(this.#maxPageLimit, this.#limit - iterateItemCount)
|
|
293
|
+
: this.#maxPageLimit;
|
|
294
|
+
const page = await this.#getPage({
|
|
260
295
|
limit: pageLimit,
|
|
261
296
|
cursor: nextCursor,
|
|
262
|
-
exclusiveStartId: nextExclusiveStartId,
|
|
263
297
|
});
|
|
264
298
|
// There are no more pages to iterate
|
|
265
299
|
if (page.items.length === 0)
|
|
@@ -267,10 +301,9 @@ export class RequestQueuePaginationIterator {
|
|
|
267
301
|
yield page;
|
|
268
302
|
iterateItemCount += page.items.length;
|
|
269
303
|
// Limit reached stopping to iterate
|
|
270
|
-
if ((this
|
|
304
|
+
if ((this.#limit && iterateItemCount >= this.#limit) || !page.nextCursor)
|
|
271
305
|
return;
|
|
272
306
|
nextCursor = page.nextCursor;
|
|
273
|
-
nextExclusiveStartId = undefined; // see comment above - delete it for any page after the first one, and paginate with cursor
|
|
274
307
|
}
|
|
275
308
|
}
|
|
276
309
|
}
|
|
@@ -284,6 +317,16 @@ export const paginationOptionsShape = {
|
|
|
284
317
|
offset: z.number().min(0).optional(),
|
|
285
318
|
chunkSize: z.number().positive().optional(),
|
|
286
319
|
};
|
|
320
|
+
/**
|
|
321
|
+
* Key under which a page of dataset items carries the number of rows the API scanned to produce it, as a
|
|
322
|
+
* non-enumerable property that stays out of the page's public shape. The API applies `offset` and `limit` to the
|
|
323
|
+
* dataset's rows first and shapes the result afterwards: filters (`clean`, `skipEmpty`, `skipHidden`) drop items and
|
|
324
|
+
* `unwind` splits or drops them, so `items.length` can land on either side of the rows scanned. The
|
|
325
|
+
* `x-apify-pagination-count` header reports the scanned number, and the offset iterator advances and terminates by it
|
|
326
|
+
* alone. A page without the property falls back to `items.length`.
|
|
327
|
+
* @internal
|
|
328
|
+
*/
|
|
329
|
+
export const SCANNED_COUNT = Symbol('scannedCount');
|
|
287
330
|
export function cast(input) {
|
|
288
331
|
return input;
|
|
289
332
|
}
|
|
@@ -310,15 +353,6 @@ export function applyQueryParamsToUrl(url, options) {
|
|
|
310
353
|
}
|
|
311
354
|
return url;
|
|
312
355
|
}
|
|
313
|
-
/**
|
|
314
|
-
* Builds a `[check, message]` pair to spread into `.refine()`, asserting that at most one of `keys`
|
|
315
|
-
* is present. Pass the options interface as `T`, so that a misspelled key is a type error.
|
|
316
|
-
* @internal
|
|
317
|
-
*/
|
|
318
|
-
export const mutuallyExclusive = (...keys) => [
|
|
319
|
-
(value) => keys.filter((key) => typeof value[key] !== 'undefined').length <= 1,
|
|
320
|
-
`At most one of the following fields is allowed: ${keys.join(', ')}`,
|
|
321
|
-
];
|
|
322
356
|
const pathSegmentSchema = z
|
|
323
357
|
.string()
|
|
324
358
|
.nonempty()
|