apify-client 2.25.1-beta.8 → 3.0.0-beta.0

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.
Files changed (41) hide show
  1. package/dist/apify_client.js +34 -30
  2. package/dist/argument_validation_error.d.ts +17 -0
  3. package/dist/argument_validation_error.js +157 -0
  4. package/dist/base/api_client.d.ts +2 -2
  5. package/dist/base/api_client.js +13 -10
  6. package/dist/bundle.js +66 -28147
  7. package/dist/bundle.js.LICENSE.txt +33 -0
  8. package/dist/bundle.js.map +1 -1
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +1 -0
  11. package/dist/index.mjs +1 -0
  12. package/dist/resource_clients/actor.js +60 -55
  13. package/dist/resource_clients/actor_collection.js +13 -11
  14. package/dist/resource_clients/actor_env_var.js +2 -3
  15. package/dist/resource_clients/actor_env_var_collection.js +3 -3
  16. package/dist/resource_clients/actor_version.js +5 -4
  17. package/dist/resource_clients/actor_version_collection.js +3 -3
  18. package/dist/resource_clients/build.js +7 -10
  19. package/dist/resource_clients/build_collection.js +8 -8
  20. package/dist/resource_clients/dataset.d.ts +6 -4
  21. package/dist/resource_clients/dataset.js +66 -57
  22. package/dist/resource_clients/dataset_collection.js +14 -12
  23. package/dist/resource_clients/key_value_store.d.ts +3 -2
  24. package/dist/resource_clients/key_value_store.js +66 -50
  25. package/dist/resource_clients/key_value_store_collection.js +14 -12
  26. package/dist/resource_clients/request_queue.js +83 -89
  27. package/dist/resource_clients/request_queue_collection.js +12 -11
  28. package/dist/resource_clients/run.js +39 -40
  29. package/dist/resource_clients/run_collection.js +12 -11
  30. package/dist/resource_clients/schedule.js +1 -3
  31. package/dist/resource_clients/schedule_collection.js +10 -9
  32. package/dist/resource_clients/store_collection.js +13 -13
  33. package/dist/resource_clients/task.js +36 -33
  34. package/dist/resource_clients/task_collection.js +9 -9
  35. package/dist/resource_clients/webhook.js +1 -3
  36. package/dist/resource_clients/webhook_collection.js +10 -9
  37. package/dist/resource_clients/webhook_dispatch_collection.js +8 -8
  38. package/dist/statistics.js +4 -3
  39. package/dist/utils.d.ts +51 -5
  40. package/dist/utils.js +94 -12
  41. package/package.json +10 -5
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApifyClient = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const ow_1 = tslib_1.__importDefault(require("ow"));
5
+ const zod_1 = require("zod");
6
6
  const consts_1 = require("@apify/consts");
7
7
  const log_1 = tslib_1.__importDefault(require("@apify/log"));
8
8
  const http_client_1 = require("./http_client");
@@ -30,7 +30,23 @@ const webhook_collection_1 = require("./resource_clients/webhook_collection");
30
30
  const webhook_dispatch_1 = require("./resource_clients/webhook_dispatch");
31
31
  const webhook_dispatch_collection_1 = require("./resource_clients/webhook_dispatch_collection");
32
32
  const statistics_1 = require("./statistics");
33
+ const utils_1 = require("./utils");
33
34
  const DEFAULT_TIMEOUT_SECS = 360;
35
+ const clientOptionsSchema = zod_1.z.strictObject({
36
+ baseUrl: zod_1.z.string().default('https://api.apify.com'),
37
+ publicBaseUrl: zod_1.z.string().default('https://api.apify.com'),
38
+ maxRetries: zod_1.z.number().default(8),
39
+ minDelayBetweenRetriesMillis: zod_1.z.number().default(500),
40
+ requestInterceptors: zod_1.z.array(zod_1.z.unknown()).default([]),
41
+ timeoutSecs: zod_1.z.number().default(DEFAULT_TIMEOUT_SECS),
42
+ token: zod_1.z.string().optional(),
43
+ userAgentSuffix: zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())]).optional(),
44
+ });
45
+ const resourceIdSchema = zod_1.z.string().min(1);
46
+ const requestQueueOptionsSchema = zod_1.z.strictObject({
47
+ clientKey: zod_1.z.string().min(1).optional(),
48
+ timeoutSecs: zod_1.z.number().optional(),
49
+ });
34
50
  /**
35
51
  * The official JavaScript client for the Apify API.
36
52
  *
@@ -63,17 +79,8 @@ class ApifyClient {
63
79
  logger;
64
80
  httpClient;
65
81
  constructor(options = {}) {
66
- (0, ow_1.default)(options, ow_1.default.object.exactShape({
67
- baseUrl: ow_1.default.optional.string,
68
- publicBaseUrl: ow_1.default.optional.string,
69
- maxRetries: ow_1.default.optional.number,
70
- minDelayBetweenRetriesMillis: ow_1.default.optional.number,
71
- requestInterceptors: ow_1.default.optional.array,
72
- timeoutSecs: ow_1.default.optional.number,
73
- token: ow_1.default.optional.string,
74
- userAgentSuffix: ow_1.default.optional.any(ow_1.default.string, ow_1.default.array.ofType(ow_1.default.string)),
75
- }));
76
- const { baseUrl = 'https://api.apify.com', publicBaseUrl = 'https://api.apify.com', maxRetries = 8, minDelayBetweenRetriesMillis = 500, requestInterceptors = [], timeoutSecs = DEFAULT_TIMEOUT_SECS, token, } = options;
82
+ const parsed = (0, utils_1.parseArgument)(options, clientOptionsSchema, 'ApifyClientOptions');
83
+ const { baseUrl, publicBaseUrl, maxRetries, minDelayBetweenRetriesMillis, requestInterceptors, timeoutSecs, token, } = parsed;
77
84
  const tempBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, baseUrl.length - 1) : baseUrl;
78
85
  this.baseUrl = `${tempBaseUrl}/v2`;
79
86
  const tempPublicBaseUrl = publicBaseUrl.endsWith('/')
@@ -91,7 +98,7 @@ class ApifyClient {
91
98
  timeoutSecs,
92
99
  logger: this.logger,
93
100
  token: this.token,
94
- userAgentSuffix: options.userAgentSuffix,
101
+ userAgentSuffix: parsed.userAgentSuffix,
95
102
  });
96
103
  }
97
104
  _options() {
@@ -130,7 +137,7 @@ class ApifyClient {
130
137
  * ```
131
138
  */
132
139
  actor(id) {
133
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
140
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
134
141
  return new actor_1.ActorClient({
135
142
  id,
136
143
  ...this._options(),
@@ -157,7 +164,7 @@ class ApifyClient {
157
164
  * @see https://docs.apify.com/api/v2/actor-build-get
158
165
  */
159
166
  build(id) {
160
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
167
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
161
168
  return new build_1.BuildClient({
162
169
  id,
163
170
  ...this._options(),
@@ -198,7 +205,7 @@ class ApifyClient {
198
205
  * ```
199
206
  */
200
207
  dataset(id) {
201
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
208
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
202
209
  return new dataset_1.DatasetClient({
203
210
  id,
204
211
  ...this._options(),
@@ -235,7 +242,7 @@ class ApifyClient {
235
242
  * ```
236
243
  */
237
244
  keyValueStore(id) {
238
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
245
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
239
246
  return new key_value_store_1.KeyValueStoreClient({
240
247
  id,
241
248
  ...this._options(),
@@ -249,7 +256,7 @@ class ApifyClient {
249
256
  * @see https://docs.apify.com/api/v2/log-get
250
257
  */
251
258
  log(buildOrRunId) {
252
- (0, ow_1.default)(buildOrRunId, ow_1.default.string.nonEmpty);
259
+ (0, utils_1.parseArgument)(buildOrRunId, resourceIdSchema);
253
260
  return new log_2.LogClient({
254
261
  id: buildOrRunId,
255
262
  ...this._options(),
@@ -288,16 +295,13 @@ class ApifyClient {
288
295
  * ```
289
296
  */
290
297
  requestQueue(id, options = {}) {
291
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
292
- (0, ow_1.default)(options, ow_1.default.object.exactShape({
293
- clientKey: ow_1.default.optional.string.nonEmpty,
294
- timeoutSecs: ow_1.default.optional.number,
295
- }));
298
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
299
+ const parsed = (0, utils_1.parseArgument)(options, requestQueueOptionsSchema, 'RequestQueueUserOptions');
296
300
  const apiClientOptions = {
297
301
  id,
298
302
  ...this._options(),
299
303
  };
300
- return new request_queue_1.RequestQueueClient(apiClientOptions, options);
304
+ return new request_queue_1.RequestQueueClient(apiClientOptions, parsed);
301
305
  }
302
306
  /**
303
307
  * Returns a client for managing Actor runs in your account.
@@ -333,7 +337,7 @@ class ApifyClient {
333
337
  * ```
334
338
  */
335
339
  run(id) {
336
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
340
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
337
341
  return new run_1.RunClient({
338
342
  id,
339
343
  ...this._options(),
@@ -366,7 +370,7 @@ class ApifyClient {
366
370
  * ```
367
371
  */
368
372
  task(id) {
369
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
373
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
370
374
  return new task_1.TaskClient({
371
375
  id,
372
376
  ...this._options(),
@@ -393,7 +397,7 @@ class ApifyClient {
393
397
  * @see https://docs.apify.com/api/v2/schedule-get
394
398
  */
395
399
  schedule(id) {
396
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
400
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
397
401
  return new schedule_1.ScheduleClient({
398
402
  id,
399
403
  ...this._options(),
@@ -409,7 +413,7 @@ class ApifyClient {
409
413
  * @see https://docs.apify.com/api/v2/user-get
410
414
  */
411
415
  user(id = consts_1.ME_USER_NAME_PLACEHOLDER) {
412
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
416
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
413
417
  return new user_1.UserClient({
414
418
  id,
415
419
  ...this._options(),
@@ -436,7 +440,7 @@ class ApifyClient {
436
440
  * @see https://docs.apify.com/api/v2/webhook-get
437
441
  */
438
442
  webhook(id) {
439
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
443
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
440
444
  return new webhook_1.WebhookClient({
441
445
  id,
442
446
  ...this._options(),
@@ -461,7 +465,7 @@ class ApifyClient {
461
465
  * @see https://docs.apify.com/api/v2/webhook-dispatch-get
462
466
  */
463
467
  webhookDispatch(id) {
464
- (0, ow_1.default)(id, ow_1.default.string.nonEmpty);
468
+ (0, utils_1.parseArgument)(id, resourceIdSchema);
465
469
  return new webhook_dispatch_1.WebhookDispatchClient({
466
470
  id,
467
471
  ...this._options(),
@@ -0,0 +1,17 @@
1
+ import type { z } from 'zod';
2
+ /**
3
+ * Thrown when an argument fails schema validation.
4
+ *
5
+ * Its `message` is a human-readable sentence naming the offending field and the
6
+ * value it received (rather than a raw JSON dump). The structured
7
+ * {@link https://zod.dev | zod} issues are available on `issues`, and the
8
+ * original `ZodError` on `cause`, for programmatic inspection.
9
+ *
10
+ * `apify-client` sits below `@crawlee/core` and the Apify SDK in the dependency
11
+ * graph, so it defines its own error type rather than importing one from them.
12
+ */
13
+ export declare class ArgumentValidationError extends Error {
14
+ /** Structured issues from the underlying schema check. */
15
+ readonly issues: z.ZodError['issues'];
16
+ constructor(error: z.ZodError, value: unknown, label?: string);
17
+ }
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArgumentValidationError = void 0;
4
+ /** Formats a zod issue path like `groups[0]` or `countryCode`. */
5
+ function formatIssuePath(path) {
6
+ let out = '';
7
+ for (const key of path) {
8
+ if (typeof key === 'number')
9
+ out += `[${key}]`;
10
+ else
11
+ out += out ? `.${String(key)}` : String(key);
12
+ }
13
+ return out;
14
+ }
15
+ /** Reads the value at `path` from the validated input, to include in the error. */
16
+ function valueAtPath(root, path) {
17
+ let current = root;
18
+ for (const key of path) {
19
+ if (current === null || typeof current !== 'object')
20
+ return undefined;
21
+ current = current[key];
22
+ }
23
+ return current;
24
+ }
25
+ /**
26
+ * How much of a received string the message renders. A rejected argument can be arbitrarily large - a
27
+ * whole JSON payload passed where an object was expected - and its full text would swamp the message.
28
+ */
29
+ const MAX_RECEIVED_STRING_LENGTH = 80;
30
+ /** Renders a primitive received value for an error; skips objects/Dates (noisy). */
31
+ function describeReceived(value) {
32
+ switch (typeof value) {
33
+ case 'string':
34
+ // An empty string would render as bare backticks - make it visible.
35
+ if (value === '')
36
+ return "''";
37
+ return value.length > MAX_RECEIVED_STRING_LENGTH
38
+ ? `${value.slice(0, MAX_RECEIVED_STRING_LENGTH)}...`
39
+ : value;
40
+ case 'number':
41
+ case 'boolean':
42
+ return String(value);
43
+ case 'bigint':
44
+ // Keep the `n` suffix, so a rejected bigint is not mistaken for a number.
45
+ return `${value}n`;
46
+ default:
47
+ return undefined;
48
+ }
49
+ }
50
+ /**
51
+ * Renders the issue's own sentence, except where zod's contradicts itself: a value of the expected type
52
+ * that fails that type's implicit constraint is still reported as the wrong *type*, giving "expected
53
+ * number, received number" for `Infinity` / `NaN` and "expected date, received Date" for an invalid
54
+ * `Date`. Name the constraint that actually failed instead.
55
+ */
56
+ function describeIssue(issue, value) {
57
+ if (issue.code === 'invalid_type') {
58
+ if (issue.expected === 'number' && typeof value === 'number') {
59
+ return 'Invalid input: expected a finite number';
60
+ }
61
+ // A tag check, not `instanceof`, so a `Date` from another realm is named too.
62
+ if (issue.expected === 'date' && Object.prototype.toString.call(value) === '[object Date]') {
63
+ return 'Invalid input: expected a valid date';
64
+ }
65
+ }
66
+ return issue.message;
67
+ }
68
+ /**
69
+ * How many issue lines the message renders, before a closing "... and N more" line. Validating a
70
+ * large array - a dataset push, a request batch - can fail on every element, and rendering all of
71
+ * them would make the message megabytes long. The full set stays on `issues` either way.
72
+ */
73
+ const MAX_RENDERED_LINES = 10;
74
+ /**
75
+ * How deep into the value the lines for `issue` would sit, as a path length. Computed without
76
+ * rendering anything, so a union can weigh its arms before any string is built.
77
+ */
78
+ function deepestIssueDepth(issue, baseDepth) {
79
+ const depth = baseDepth + issue.path.length;
80
+ if (issue.code === 'invalid_union') {
81
+ let deepest = -1;
82
+ for (const arm of issue.errors) {
83
+ for (const nested of arm)
84
+ deepest = Math.max(deepest, deepestIssueDepth(nested, depth));
85
+ }
86
+ return deepest;
87
+ }
88
+ return depth;
89
+ }
90
+ /** Collects one line per issue into `lines`; a union expands into a line per deepest-failing arm. */
91
+ function collectIssueLines(issue, root, basePath, lines, counter) {
92
+ const path = [...basePath, ...issue.path];
93
+ // A union's own message is a bare "Invalid input" - the useful part is in `errors`,
94
+ // whose paths are relative to the union, hence passing `path` down as the base.
95
+ if (issue.code === 'invalid_union') {
96
+ // Only the arms that reached deepest are reported. An arm that failed nearer the root rejected a
97
+ // shape the value never had - for `[{ ok: 1 }, 2]` against `object | string | array`, the object
98
+ // and string arms fail on the whole array, and only the array arm can point at `[1]`. When every
99
+ // arm fails at the same depth, as for an argument of an outright wrong type, they are all kept.
100
+ const armDepths = issue.errors.map((arm) => arm.reduce((deepest, nested) => Math.max(deepest, deepestIssueDepth(nested, path.length)), -1));
101
+ const deepest = Math.max(...armDepths);
102
+ for (const [index, arm] of issue.errors.entries()) {
103
+ if (armDepths[index] !== deepest)
104
+ continue;
105
+ for (const nested of arm)
106
+ collectIssueLines(nested, root, path, lines, counter);
107
+ }
108
+ return;
109
+ }
110
+ counter.total += 1;
111
+ if (lines.length >= MAX_RENDERED_LINES)
112
+ return;
113
+ const location = path.length ? ` at \`${formatIssuePath(path)}\`` : '';
114
+ const value = valueAtPath(root, path);
115
+ const received = describeReceived(value);
116
+ const got = received === undefined ? '' : `, got \`${received}\``;
117
+ lines.push(`${describeIssue(issue, value)}${location}${got}`);
118
+ }
119
+ /**
120
+ * Formats a `ZodError` as a plain, human-readable message that names the
121
+ * offending field *and* the value it received (e.g. ``must match pattern
122
+ * /^[A-Z]{2}$/ at `countryCode`, got `CZE` ``) - closer to the old `ow` errors
123
+ * than zod's default, which omits the received value.
124
+ */
125
+ function formatZodError(error, root, label) {
126
+ const lines = [];
127
+ const counter = { total: 0 };
128
+ for (const issue of error.issues)
129
+ collectIssueLines(issue, root, [], lines, counter);
130
+ // The label names the validated interface, the way ow's errors ended with "in object `X`".
131
+ const rendered = label ? lines.map((line) => `${line} in \`${label}\``) : [...lines];
132
+ const hidden = counter.total - lines.length;
133
+ if (hidden > 0)
134
+ rendered.push(`... and ${hidden} more problem${hidden === 1 ? '' : 's'}`);
135
+ return rendered.join('\n');
136
+ }
137
+ /**
138
+ * Thrown when an argument fails schema validation.
139
+ *
140
+ * Its `message` is a human-readable sentence naming the offending field and the
141
+ * value it received (rather than a raw JSON dump). The structured
142
+ * {@link https://zod.dev | zod} issues are available on `issues`, and the
143
+ * original `ZodError` on `cause`, for programmatic inspection.
144
+ *
145
+ * `apify-client` sits below `@crawlee/core` and the Apify SDK in the dependency
146
+ * graph, so it defines its own error type rather than importing one from them.
147
+ */
148
+ class ArgumentValidationError extends Error {
149
+ /** Structured issues from the underlying schema check. */
150
+ issues;
151
+ constructor(error, value, label) {
152
+ super(formatZodError(error, value, label), { cause: error });
153
+ this.name = 'ArgumentValidationError';
154
+ this.issues = error.issues;
155
+ }
156
+ }
157
+ exports.ArgumentValidationError = ArgumentValidationError;
@@ -31,8 +31,8 @@ export declare abstract class ApiClient {
31
31
  params?: Record<string, unknown>;
32
32
  constructor(options: ApiClientOptions);
33
33
  protected _subResourceOptions<T>(moreOptions?: T): BaseOptions & T;
34
- protected _url(path?: string): string;
35
- protected _publicUrl(path?: string): string;
34
+ protected _url(path?: string | string[]): string;
35
+ protected _publicUrl(path?: string | string[]): string;
36
36
  protected _params<T>(endpointParams?: T): Record<string, unknown>;
37
37
  protected _toSafeId(id: string): string;
38
38
  /**
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApiClient = void 0;
4
+ const utils_1 = require("../utils");
4
5
  /** @private */
5
6
  class ApiClient {
6
7
  id;
@@ -22,7 +23,7 @@ class ApiClient {
22
23
  this.baseUrl = baseUrl;
23
24
  this.publicBaseUrl = publicBaseUrl;
24
25
  this.resourcePath = resourcePath;
25
- this.url = id ? `${baseUrl}/${resourcePath}/${this.safeId}` : `${baseUrl}/${resourcePath}`;
26
+ this.url = id ? `${baseUrl}/${resourcePath}/${(0, utils_1.toPathSegment)(this.safeId)}` : `${baseUrl}/${resourcePath}`;
26
27
  this.apifyClient = apifyClient;
27
28
  this.httpClient = httpClient;
28
29
  this.params = params;
@@ -38,20 +39,19 @@ class ApiClient {
38
39
  return { ...baseOptions, ...moreOptions };
39
40
  }
40
41
  _url(path) {
41
- return path ? `${this.url}/${path}` : this.url;
42
+ return path ? `${this.url}/${(0, utils_1.toPath)(path)}` : this.url;
42
43
  }
43
44
  _publicUrl(path) {
44
45
  const url = this.id
45
- ? `${this.publicBaseUrl}/${this.resourcePath}/${this.safeId}`
46
+ ? `${this.publicBaseUrl}/${this.resourcePath}/${(0, utils_1.toPathSegment)(this.safeId)}`
46
47
  : `${this.publicBaseUrl}/${this.resourcePath}`;
47
- return path ? `${url}/${path}` : url;
48
+ return path ? `${url}/${(0, utils_1.toPath)(path)}` : url;
48
49
  }
49
50
  _params(endpointParams) {
50
51
  return { ...this.params, ...endpointParams };
51
52
  }
52
53
  _toSafeId(id) {
53
- // The id has the format `username/actor-name`, so we only need to replace the first `/`.
54
- return id.replace('/', '~');
54
+ return id.replaceAll('/', '~');
55
55
  }
56
56
  /**
57
57
  * Returns async iterator to iterate through all items and Promise that can be awaited to get first page of results.
@@ -69,9 +69,12 @@ class ApiClient {
69
69
  return a;
70
70
  return Math.min(a, b);
71
71
  };
72
+ // `chunkSize` only sizes this loop's requests; it is not an API parameter, so it must not reach
73
+ // `_params()` and the query string.
74
+ const { chunkSize, ...listOptions } = options;
72
75
  const paginatedListPromise = getPaginatedList({
73
- ...options,
74
- limit: minForLimitParam(options.limit, options.chunkSize),
76
+ ...listOptions,
77
+ limit: minForLimitParam(options.limit, chunkSize),
75
78
  });
76
79
  async function* asyncGenerator() {
77
80
  let currentPage = await paginatedListPromise;
@@ -83,8 +86,8 @@ class ApiClient {
83
86
  while (currentPage.items.length > 0 && // Continue only if at least some items were returned in the last page.
84
87
  remainingItems > 0) {
85
88
  const newOptions = {
86
- ...options,
87
- limit: minForLimitParam(remainingItems, options.chunkSize),
89
+ ...listOptions,
90
+ limit: minForLimitParam(remainingItems, chunkSize),
88
91
  offset: currentOffset,
89
92
  };
90
93
  currentPage = await getPaginatedList(newOptions);