From b6051f50fb053e1fd180b231ae0a3f48bb439b7d Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 10 Sep 2026 18:23:28 +0200 Subject: [PATCH 1/6] refactor!: drop the underscore prefix from non-public members BREAKING CHANGE: the protected helpers on the base clients are renamed. `_url` -> `buildUrl`, `_publicUrl` -> `buildPublicUrl`, `_params` -> `buildParams`, `_subResourceOptions` -> `subResourceOptions`, `_toSafeId` -> `toSafeId`, `_listPaginatedFromCallback` -> `listPaginatedFromCallback`, `_get` -> `getResource`, `_update` -> `updateResource`, `_delete` -> `deleteResource`, `_waitForFinish` -> `waitForJobFinish`, `_list` -> `listResources`, `_listPaginated` -> `listResourcesPaginated`, `_create` -> `createResource`, `_getOrCreate` -> `getOrCreateResource`, `_batchAddRequests` -> `addRequestBatch`, `_batchAddRequestsWithRetries` -> `addRequestBatchWithRetries`. Subclasses that call the old names have to switch to the new ones. --- CONTRIBUTING.md | 14 ++--- docs/public-api/apify-client.api.md | 40 ++++++------ src/apify_api_error.ts | 41 ++++++++---- src/apify_client.ts | 48 +++++++------- src/base/api_client.ts | 20 +++--- src/base/resource_client.ts | 24 +++---- src/base/resource_collection_client.ts | 24 +++---- src/http_client.ts | 42 ++++++------- src/resource_clients/actor.ts | 36 +++++------ src/resource_clients/actor_collection.ts | 4 +- src/resource_clients/actor_env_var.ts | 6 +- .../actor_env_var_collection.ts | 4 +- src/resource_clients/actor_version.ts | 10 +-- .../actor_version_collection.ts | 4 +- src/resource_clients/build.ts | 16 ++--- src/resource_clients/build_collection.ts | 2 +- src/resource_clients/dataset.ts | 30 ++++----- src/resource_clients/dataset_collection.ts | 4 +- src/resource_clients/key_value_store.ts | 30 ++++----- .../key_value_store_collection.ts | 4 +- src/resource_clients/log.ts | 8 +-- src/resource_clients/request_queue.ts | 62 +++++++++---------- .../request_queue_collection.ts | 4 +- src/resource_clients/run.ts | 34 +++++----- src/resource_clients/run_collection.ts | 2 +- src/resource_clients/schedule.ts | 10 +-- src/resource_clients/schedule_collection.ts | 4 +- src/resource_clients/store_collection.ts | 2 +- src/resource_clients/task.ts | 26 ++++---- src/resource_clients/task_collection.ts | 4 +- src/resource_clients/user.ts | 14 ++--- src/resource_clients/webhook.ts | 12 ++-- src/resource_clients/webhook_collection.ts | 4 +- src/resource_clients/webhook_dispatch.ts | 2 +- .../webhook_dispatch_collection.ts | 2 +- src/statistics.ts | 4 +- 36 files changed, 307 insertions(+), 290 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 20588da37..56be0efc4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -316,15 +316,15 @@ export class MyResourceClient extends ResourceClient { } async get(): Promise { - return this._get(); + return this.getResource(); } async update(newFields: MyResourceUpdate): Promise { - return this._update(newFields); + return this.updateResource(newFields); } async delete(): Promise { - return this._delete(); + return this.deleteResource(); } } ``` @@ -346,11 +346,11 @@ export class MyResourceCollectionClient extends ResourceCollectionClient { } async list(options?: MyResourceListOptions): Promise> { - return this._list(options); + return this.listResources(options); } async create(resource: MyResourceCreate): Promise { - return this._create(resource); + return this.createResource(resource); } } ``` @@ -360,11 +360,11 @@ export class MyResourceCollectionClient extends ResourceCollectionClient { ```typescript // In src/apify_client.ts myResource(id: string): MyResourceClient { - return new MyResourceClient(this._subResourceOptions({ id })); + return new MyResourceClient(this.subResourceOptions({ id })); } myResources(): MyResourceCollectionClient { - return new MyResourceCollectionClient(this._options()); + return new MyResourceCollectionClient(this.subClientOptions()); } ``` diff --git a/docs/public-api/apify-client.api.md b/docs/public-api/apify-client.api.md index 5f9a7b356..5bb49d947 100644 --- a/docs/public-api/apify-client.api.md +++ b/docs/public-api/apify-client.api.md @@ -493,29 +493,29 @@ abstract class ApiClient { // (undocumented) baseUrl: string; // (undocumented) + protected buildParams(endpointParams?: T): Record; + // (undocumented) + protected buildPublicUrl(path?: string | string[]): string; + // (undocumented) + protected buildUrl(path?: string | string[]): string; + // (undocumented) httpClient: HttpClient; // (undocumented) id?: string; - protected _listPaginatedFromCallback>(getPaginatedList: (options?: T) => Promise, options?: T): AsyncIterable & Promise; + protected listPaginatedFromCallback>(getPaginatedList: (options?: T) => Promise, options?: T): AsyncIterable & Promise; // (undocumented) params?: Record; - // (undocumented) - protected _params(endpointParams?: T): Record; publicBaseUrl: string; // (undocumented) - protected _publicUrl(path?: string | string[]): string; - // (undocumented) resourcePath: string; // (undocumented) safeId?: string; // (undocumented) - protected _subResourceOptions(moreOptions?: T): BaseOptions & T; + protected subResourceOptions(moreOptions?: T): BaseOptions & T; // (undocumented) - protected _toSafeId(id: string): string; + protected toSafeId(id: string): string; // (undocumented) url: string; - // (undocumented) - protected _url(path?: string | string[]): string; } // Not exported by the entry point; reachable only as a referenced type. @@ -3274,10 +3274,10 @@ export interface RequestQueue extends Omit; - batchAddRequests(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise; - protected _batchAddRequests(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientAddRequestOptions): Promise; + protected addRequestBatch(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientAddRequestOptions): Promise; // (undocumented) - protected _batchAddRequestsWithRetries(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise; + protected addRequestBatchWithRetries(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise; + batchAddRequests(requests: RequestQueueClientRequestToAdd[], options?: RequestQueueClientBatchAddRequestWithRetriesOptions): Promise; batchDeleteRequests(requests: RequestQueueClientRequestToDelete[]): Promise; delete(): Promise; deleteRequest(id: string): Promise; @@ -3512,11 +3512,11 @@ export interface RequestQueueUserOptions { // Not exported by the entry point; reachable only as a referenced type. // @public class ResourceClient extends ApiClient { - protected _delete(timeoutMillis?: number): Promise; - protected _get(schema: z.ZodType, options?: T, timeoutMillis?: number): Promise; + protected deleteResource(timeoutMillis?: number): Promise; + protected getResource(schema: z.ZodType, options?: T, timeoutMillis?: number): Promise; // (undocumented) - protected _update(schema: z.ZodType, newFields: T, timeoutMillis?: number): Promise; - protected _waitForFinish(schema: z.ZodType, newFields: T, timeoutMillis?: number): Promise; + protected waitForJobFinish(schema: z.ZodType, options?: WaitForFinishOptions): Promise; } @@ -3525,12 +3525,12 @@ class ResourceClient extends ApiClient { // @public class ResourceCollectionClient extends ApiClient { // (undocumented) - protected _create(schema: z.ZodType, resource: D): Promise; + protected createResource(schema: z.ZodType, resource: D): Promise; // (undocumented) - protected _getOrCreate(schema: z.ZodType, name?: string, resource?: D): Promise; + protected getOrCreateResource(schema: z.ZodType, name?: string, resource?: D): Promise; // (undocumented) - protected _list(schema: z.ZodType, options?: T): Promise; - protected _listPaginated>(schema: z.ZodType, options?: T): AsyncIterable & Promise; + protected listResources(schema: z.ZodType, options?: T): Promise; + protected listResourcesPaginated>(schema: z.ZodType, options?: T): AsyncIterable & Promise; } // @public diff --git a/src/apify_api_error.ts b/src/apify_api_error.ts index e51c021ff..23403facd 100644 --- a/src/apify_api_error.ts +++ b/src/apify_api_error.ts @@ -8,15 +8,32 @@ import { isBuffer } from './utils.js'; export type { ApifyApiErrorType } from './models.js'; /** - * Examples of capturing groups for "...at ActorCollectionClient._list (/Users/..." - * 0: "at ActorCollectionClient._list (" + * Examples of capturing groups for "...at ActorCollectionClient.listResources (/Users/..." + * 0: "at ActorCollectionClient.listResources (" * 1: undefined * 2: "ActorCollectionClient" * 3: undefined - * 4: "list" + * 4: "listResources" * @private */ -const CLIENT_METHOD_REGEX = /at( async)? ([A-Za-z]+(Collection)?Client)\._?([A-Za-z]+) \(/; +const CLIENT_METHOD_REGEX = /at( async)? ([A-Za-z]+(Collection)?Client)\.([A-Za-z]+) \(/; + +/** + * A public method that returns the promise of a shared helper without awaiting it leaves only the helper on + * the stack, so the helper is reported under the method the caller invoked. + * @private + */ +const PUBLIC_METHOD_BY_HELPER: Record = { + getResource: 'get', + updateResource: 'update', + deleteResource: 'delete', + waitForJobFinish: 'waitForFinish', + listResources: 'list', + createResource: 'create', + getOrCreateResource: 'getOrCreate', + addRequestBatch: 'batchAddRequests', + addRequestBatchWithRetries: 'batchAddRequests', +}; /** * An `ApifyApiError` is thrown for successful HTTP requests that reach the API, @@ -114,17 +131,17 @@ export class ApifyApiError extends Error { super(message); this.name = this.constructor.name; - this.clientMethod = this._extractClientAndMethodFromStack(); + this.clientMethod = this.extractClientAndMethodFromStack(); this.statusCode = response.status; this.type = type; this.attempt = attempt; this.httpMethod = response.config?.method; - this.path = this._safelyParsePathFromResponse(response); + this.path = this.safelyParsePathFromResponse(response); const stack = this.stack!; this.originalStack = stack.slice(stack.indexOf('\n')); - this.stack = this._createApiStack(); + this.stack = this.createApiStack(); this.data = errorData; } @@ -139,7 +156,7 @@ export class ApifyApiError extends Error { return new ErrorClass(response, attempt); } - private _safelyParsePathFromResponse(response: AxiosResponse) { + private safelyParsePathFromResponse(response: AxiosResponse) { const urlString = response.config?.url; let url; try { @@ -150,10 +167,10 @@ export class ApifyApiError extends Error { return url.pathname + url.search; } - private _extractClientAndMethodFromStack() { + private extractClientAndMethodFromStack() { const match = this.stack!.match(CLIENT_METHOD_REGEX); - if (match) return `${match[2]}.${match[4]}`; - return 'unknown'; + if (!match) return 'unknown'; + return `${match[2]}.${PUBLIC_METHOD_BY_HELPER[match[4]] ?? match[4]}`; } /** @@ -170,7 +187,7 @@ export class ApifyApiError extends Error { * httpMethod: post * path: /v2/actor-tasks/user~my-task/runs */ - private _createApiStack() { + private createApiStack() { const { name, ...props } = this; const stack = Object.entries(props) diff --git a/src/apify_client.ts b/src/apify_client.ts index b9536bbaa..30293b39e 100644 --- a/src/apify_client.ts +++ b/src/apify_client.ts @@ -123,7 +123,7 @@ export class ApifyClient { }); } - private _options() { + private subClientOptions() { return { baseUrl: this.baseUrl, publicBaseUrl: this.publicBaseUrl, @@ -141,7 +141,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/acts-get */ actors(): ActorCollectionClient { - return new ActorCollectionClient(this._options()); + return new ActorCollectionClient(this.subClientOptions()); } /** @@ -165,7 +165,7 @@ export class ApifyClient { return new ActorClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -178,7 +178,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/actor-builds-get */ builds(): BuildCollectionClient { - return new BuildCollectionClient(this._options()); + return new BuildCollectionClient(this.subClientOptions()); } /** @@ -195,7 +195,7 @@ export class ApifyClient { return new BuildClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -208,7 +208,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/datasets-get */ datasets(): DatasetCollectionClient { - return new DatasetCollectionClient(this._options()); + return new DatasetCollectionClient(this.subClientOptions()); } /** @@ -241,7 +241,7 @@ export class ApifyClient { return new DatasetClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -254,7 +254,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/key-value-stores-get */ keyValueStores(): KeyValueStoreCollectionClient { - return new KeyValueStoreCollectionClient(this._options()); + return new KeyValueStoreCollectionClient(this.subClientOptions()); } /** @@ -281,7 +281,7 @@ export class ApifyClient { return new KeyValueStoreClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -297,7 +297,7 @@ export class ApifyClient { return new LogClient({ id: buildOrRunId, - ...this._options(), + ...this.subClientOptions(), }); } @@ -310,7 +310,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/request-queues-get */ requestQueues(): RequestQueueCollectionClient { - return new RequestQueueCollectionClient(this._options()); + return new RequestQueueCollectionClient(this.subClientOptions()); } /** @@ -340,7 +340,7 @@ export class ApifyClient { const apiClientOptions = { id, - ...this._options(), + ...this.subClientOptions(), }; return new RequestQueueClient(apiClientOptions, parsed); } @@ -355,7 +355,7 @@ export class ApifyClient { */ runs(): RunCollectionClient { return new RunCollectionClient({ - ...this._options(), + ...this.subClientOptions(), resourcePath: 'actor-runs', }); } @@ -384,7 +384,7 @@ export class ApifyClient { return new RunClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -397,7 +397,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/actor-tasks-get */ tasks(): TaskCollectionClient { - return new TaskCollectionClient(this._options()); + return new TaskCollectionClient(this.subClientOptions()); } /** @@ -420,7 +420,7 @@ export class ApifyClient { return new TaskClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -433,7 +433,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/schedules-get */ schedules(): ScheduleCollectionClient { - return new ScheduleCollectionClient(this._options()); + return new ScheduleCollectionClient(this.subClientOptions()); } /** @@ -450,7 +450,7 @@ export class ApifyClient { return new ScheduleClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -468,7 +468,7 @@ export class ApifyClient { return new UserClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -481,7 +481,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/webhooks-get */ webhooks(): WebhookCollectionClient { - return new WebhookCollectionClient(this._options()); + return new WebhookCollectionClient(this.subClientOptions()); } /** @@ -498,7 +498,7 @@ export class ApifyClient { return new WebhookClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -511,7 +511,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/webhook-dispatches-get */ webhookDispatches(): WebhookDispatchCollectionClient { - return new WebhookDispatchCollectionClient(this._options()); + return new WebhookDispatchCollectionClient(this.subClientOptions()); } /** @@ -526,7 +526,7 @@ export class ApifyClient { return new WebhookDispatchClient({ id, - ...this._options(), + ...this.subClientOptions(), }); } @@ -539,7 +539,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/store-get */ store(): StoreCollectionClient { - return new StoreCollectionClient(this._options()); + return new StoreCollectionClient(this.subClientOptions()); } /** diff --git a/src/base/api_client.ts b/src/base/api_client.ts index 6e5ab88b2..8f82563a0 100644 --- a/src/base/api_client.ts +++ b/src/base/api_client.ts @@ -47,7 +47,7 @@ export abstract class ApiClient { const { baseUrl, publicBaseUrl, apifyClient, httpClient, resourcePath, id, params = {} } = options; this.id = id; - this.safeId = id && this._toSafeId(id); + this.safeId = id && this.toSafeId(id); this.baseUrl = baseUrl; this.publicBaseUrl = publicBaseUrl; this.resourcePath = resourcePath; @@ -57,40 +57,40 @@ export abstract class ApiClient { this.params = params; } - protected _subResourceOptions(moreOptions?: T): BaseOptions & T { + protected subResourceOptions(moreOptions?: T): BaseOptions & T { const baseOptions: BaseOptions = { - baseUrl: this._url(), + baseUrl: this.buildUrl(), publicBaseUrl: this.publicBaseUrl, apifyClient: this.apifyClient, httpClient: this.httpClient, - params: this._params(), + params: this.buildParams(), }; return { ...baseOptions, ...moreOptions } as BaseOptions & T; } - protected _url(path?: string | string[]): string { + protected buildUrl(path?: string | string[]): string { return path ? `${this.url}/${toPath(path)}` : this.url; } - protected _publicUrl(path?: string | string[]): string { + protected buildPublicUrl(path?: string | string[]): string { const url = this.id ? `${this.publicBaseUrl}/${this.resourcePath}/${toPathSegment(this.safeId!)}` : `${this.publicBaseUrl}/${this.resourcePath}`; return path ? `${url}/${toPath(path)}` : url; } - protected _params(endpointParams?: T): Record { + protected buildParams(endpointParams?: T): Record { return { ...this.params, ...endpointParams }; } - protected _toSafeId(id: string): string { + protected toSafeId(id: string): string { return id.replaceAll('/', '~'); } /** * Returns async iterator to iterate through all items and Promise that can be awaited to get first page of results. */ - protected _listPaginatedFromCallback>( + protected listPaginatedFromCallback>( getPaginatedList: (options?: T) => Promise, options: T = {} as T, ): AsyncIterable & Promise { @@ -104,7 +104,7 @@ export abstract class ApiClient { }; // `chunkSize` only sizes this loop's requests; it is not an API parameter, so it must not reach - // `_params()` and the query string. + // `buildParams()` and the query string. const { chunkSize, ...listOptions } = options; const paginatedListPromise = getPaginatedList({ diff --git a/src/base/resource_client.ts b/src/base/resource_client.ts index 7739d45a6..27d6b6ae7 100644 --- a/src/base/resource_client.ts +++ b/src/base/resource_client.ts @@ -27,15 +27,15 @@ export class ResourceClient extends ApiClient { * A 404 resolves to `undefined` only when the client names its resource by ID. A chained client without one, such * as `run.dataset()`, throws it instead (see `catchNotFoundForResourceOrThrow()`). */ - protected async _get( + protected async getResource( schema: z.ZodType, options: T = {} as T, timeoutMillis?: number, ): Promise { const requestOpts: ApifyRequestConfig = { - url: this._url(), + url: this.buildUrl(), method: 'GET', - params: this._params(options), + params: this.buildParams(options), timeout: timeoutMillis, }; try { @@ -48,11 +48,11 @@ export class ResourceClient extends ApiClient { return undefined; } - protected async _update(schema: z.ZodType, newFields: T, timeoutMillis?: number): Promise { + protected async updateResource(schema: z.ZodType, newFields: T, timeoutMillis?: number): Promise { const response = await this.httpClient.call({ - url: this._url(), + url: this.buildUrl(), method: 'PUT', - params: this._params(), + params: this.buildParams(), data: newFields, timeout: timeoutMillis, }); @@ -63,12 +63,12 @@ export class ResourceClient extends ApiClient { * A 404 is swallowed, keeping the DELETE idempotent, only when the client names its resource by ID. A chained client * without one throws it instead (see `catchNotFoundForResourceOrThrow()`). */ - protected async _delete(timeoutMillis?: number): Promise { + protected async deleteResource(timeoutMillis?: number): Promise { try { await this.httpClient.call({ - url: this._url(), + url: this.buildUrl(), method: 'DELETE', - params: this._params(), + params: this.buildParams(), timeout: timeoutMillis, }); } catch (err) { @@ -80,7 +80,7 @@ export class ResourceClient extends ApiClient { * This function is used in Build and Run endpoints so it's kept * here to stay DRY. */ - protected async _waitForFinish( + protected async waitForJobFinish( schema: z.ZodType, options: WaitForFinishOptions = {}, ): Promise { @@ -103,9 +103,9 @@ export class ResourceClient extends ApiClient { const waitForFinish = Math.max(0, remainingWaitSeconds); const requestOpts: ApifyRequestConfig = { - url: this._url(), + url: this.buildUrl(), method: 'GET', - params: this._params({ waitForFinish }), + params: this.buildParams({ waitForFinish }), }; try { const response = await this.httpClient.call(requestOpts); diff --git a/src/base/resource_collection_client.ts b/src/base/resource_collection_client.ts index e0d480abf..a80c48cf8 100644 --- a/src/base/resource_collection_client.ts +++ b/src/base/resource_collection_client.ts @@ -12,11 +12,11 @@ export class ResourceCollectionClient extends ApiClient { /** * @private */ - protected async _list(schema: z.ZodType, options: T = {} as T): Promise { + protected async listResources(schema: z.ZodType, options: T = {} as T): Promise { const response = await this.httpClient.call({ - url: this._url(), + url: this.buildUrl(), method: 'GET', - params: this._params(options), + params: this.buildParams(options), }); return parseResponse(response, schema); } @@ -24,31 +24,31 @@ export class ResourceCollectionClient extends ApiClient { /** * Returns async iterator to iterate through all items and Promise that can be awaited to get first page of results. */ - protected _listPaginated>( + protected listResourcesPaginated>( schema: z.ZodType, options: T = {} as T, ): AsyncIterable & Promise { - return this._listPaginatedFromCallback( - async (listOptions?: T) => this._list(schema, listOptions), + return this.listPaginatedFromCallback( + async (listOptions?: T) => this.listResources(schema, listOptions), options, ); } - protected async _create(schema: z.ZodType, resource: D): Promise { + protected async createResource(schema: z.ZodType, resource: D): Promise { const response = await this.httpClient.call({ - url: this._url(), + url: this.buildUrl(), method: 'POST', - params: this._params(), + params: this.buildParams(), data: resource, }); return parseResponse(response, schema); } - protected async _getOrCreate(schema: z.ZodType, name?: string, resource?: D): Promise { + protected async getOrCreateResource(schema: z.ZodType, name?: string, resource?: D): Promise { const response = await this.httpClient.call({ - url: this._url(), + url: this.buildUrl(), method: 'POST', - params: this._params({ name }), + params: this.buildParams({ name }), data: resource, }); return parseResponse(response, schema); diff --git a/src/http_client.ts b/src/http_client.ts index b76570993..38593badd 100644 --- a/src/http_client.ts +++ b/src/http_client.ts @@ -55,7 +55,7 @@ export class HttpClient { this.logger = options.logger; this.workflowKey = options.workflowKey || process.env[APIFY_ENV_VARS.WORKFLOW_KEY]; this.userAgentSuffix = options.userAgentSuffix; - this._onRequestRetry = this._onRequestRetry.bind(this); + this.onRequestRetry = this.onRequestRetry.bind(this); this.axios = axios.create({ // Disable axios's built-in proxy handling since we're using custom agents @@ -171,16 +171,16 @@ export class HttpClient { async call(config: ApifyRequestConfig): Promise> { await this.ensureNodeInit(); this.stats.calls++; - const makeRequest = this._createRequestHandler(config); + const makeRequest = this.createRequestHandler(config); return retry(makeRequest, { retries: this.maxRetries, minTimeout: this.minDelayBetweenRetriesMillis, - onRetry: this._onRequestRetry, + onRetry: this.onRequestRetry, }); } - private _informAboutStreamNoRetry() { + private informAboutStreamNoRetry() { this.logger.warningOnce( 'Request body was a stream - retrying will not work, as part of it was already consumed.', ); @@ -194,7 +194,7 @@ export class HttpClient { * status codes are retried. See the following functions for the * retrying logic. */ - private _createRequestHandler(config: ApifyRequestConfig) { + private createRequestHandler(config: ApifyRequestConfig) { const makeRequest: RetryFunction = async (stopTrying, attempt) => { this.stats.requests++; let response: ApifyResponse; @@ -215,9 +215,9 @@ export class HttpClient { ); response = await this.axios.request(config); - if (this._isStatusOk(response.status)) return response; + if (this.isStatusOk(response.status)) return response; } catch (err) { - return cast(this._handleRequestError(err as AxiosError, config, stopTrying)); + return cast(this.handleRequestError(err as AxiosError, config, stopTrying)); } if (response.status === RATE_LIMIT_EXCEEDED_STATUS_CODE) { @@ -225,9 +225,9 @@ export class HttpClient { } const apiError = ApifyApiError.fromResponse(response, attempt); - if (this._isStatusCodeRetryable(response.status)) { + if (this.isStatusCodeRetryable(response.status)) { if (requestIsStream) { - this._informAboutStreamNoRetry(); + this.informAboutStreamNoRetry(); } else { // allow a retry throw apiError; @@ -241,7 +241,7 @@ export class HttpClient { return makeRequest; } - private _isStatusOk(statusCode: number) { + private isStatusOk(statusCode: number) { return statusCode < 300; } @@ -249,14 +249,14 @@ export class HttpClient { * Handles all unexpected errors that can happen, but are not * Apify API typed errors. E.g. network errors, timeouts and so on. */ - private _handleRequestError(err: AxiosError, config: ApifyRequestConfig, stopTrying: (e: Error) => void) { - if (this._isTimeoutError(err) && config.doNotRetryTimeouts) { + private handleRequestError(err: AxiosError, config: ApifyRequestConfig, stopTrying: (e: Error) => void) { + if (this.isTimeoutError(err) && config.doNotRetryTimeouts) { return stopTrying(err); } - if (this._isRetryableError(err)) { + if (this.isRetryableError(err)) { if (isStream(config.data)) { - this._informAboutStreamNoRetry(); + this.informAboutStreamNoRetry(); } else { throw err; } @@ -268,7 +268,7 @@ export class HttpClient { * Axios calls req.abort() on timeouts so timeout errors will * have a code ECONNABORTED. */ - private _isTimeoutError(err: AxiosError) { + private isTimeoutError(err: AxiosError) { return err.code === 'ECONNABORTED'; } @@ -278,8 +278,8 @@ export class HttpClient { * @param {Error} err * @private */ - private _isRetryableError(err: AxiosError) { - return this._isNetworkError(err) || this._isResponseBodyInvalid(err); + private isRetryableError(err: AxiosError) { + return this.isNetworkError(err) || this.isResponseBodyInvalid(err); } /** @@ -287,7 +287,7 @@ export class HttpClient { * a response, the request often does not fail, but simply contains * an incomplete response. This can often be fixed by retrying. */ - private _isResponseBodyInvalid(err: Error): err is InvalidResponseBodyError { + private isResponseBodyInvalid(err: Error): err is InvalidResponseBodyError { return err instanceof InvalidResponseBodyError; } @@ -296,7 +296,7 @@ export class HttpClient { * it throws an AxiosError, which will have the request * and config (and other) properties. */ - private _isNetworkError(err: AxiosError) { + private isNetworkError(err: AxiosError) { const hasRequest = err.request && typeof err.request === 'object'; const hasConfig = err.config && typeof err.config === 'object'; return hasRequest && hasConfig; @@ -307,13 +307,13 @@ export class HttpClient { * For status codes 300-499 (except 429) we do not retry the request, * because it's probably caused by invalid url (redirect 3xx) or invalid user input (4xx). */ - private _isStatusCodeRetryable(statusCode: number) { + private isStatusCodeRetryable(statusCode: number) { const isRateLimitError = statusCode === RATE_LIMIT_EXCEEDED_STATUS_CODE; const isInternalError = statusCode >= 500; return isRateLimitError || isInternalError; } - private _onRequestRetry(error: Error, attempt: number) { + private onRequestRetry(error: Error, attempt: number) { if (attempt === Math.round(this.maxRetries / 2)) { this.logger.warning( `API request failed ${attempt} times. Max attempts: ${this.maxRetries + 1}.\nCause:${error.stack}`, diff --git a/src/resource_clients/actor.ts b/src/resource_clients/actor.ts index b79a52a4e..4fde6331e 100644 --- a/src/resource_clients/actor.ts +++ b/src/resource_clients/actor.ts @@ -129,7 +129,7 @@ export class ActorClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-get */ async get(): Promise { - return this._get(schemas.Actor()); + return this.getResource(schemas.Actor()); } /** @@ -142,7 +142,7 @@ export class ActorClient extends ResourceClient { async update(newFields: ActorUpdateOptions): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.Actor(), newFields); + return this.updateResource(schemas.Actor(), newFields); } /** @@ -151,7 +151,7 @@ export class ActorClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-delete */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } /** @@ -213,10 +213,10 @@ export class ActorClient extends ResourceClient { }; const request: ApifyRequestConfig = { - url: this._url('runs'), + url: this.buildUrl('runs'), method: 'POST', data: input, - params: this._params(params), + params: this.buildParams(params), // Apify internal property. Tells the request serialization interceptor // to stringify functions to JSON, instead of omitting them. stringifyFunctions: true, @@ -320,10 +320,10 @@ export class ActorClient extends ResourceClient { const parsed = parseArgument(options, validateInputOptionsSchema, 'ActorValidateInputOptions'); const request: ApifyRequestConfig = { - url: this._url('validate-input'), + url: this.buildUrl('validate-input'), method: 'POST', data: input, - params: this._params({ build: parsed.build }), + params: this.buildParams({ build: parsed.build }), // Apify internal property. Tells the request serialization interceptor // to stringify functions to JSON, instead of omitting them. stringifyFunctions: true, @@ -372,9 +372,9 @@ export class ActorClient extends ResourceClient { const parsed = parseArgument(options, buildOptionsSchema, 'ActorBuildOptions'); const response = await this.httpClient.call({ - url: this._url('builds'), + url: this.buildUrl('builds'), method: 'POST', - params: this._params({ + params: this.buildParams({ version: versionNumber, ...parsed, }), @@ -410,9 +410,9 @@ export class ActorClient extends ResourceClient { */ async defaultBuild(options: BuildClientGetOptions = {}): Promise { const response = await this.httpClient.call({ - url: this._url('builds/default'), + url: this.buildUrl('builds/default'), method: 'GET', - params: this._params(options), + params: this.buildParams(options), }); const { id } = parseResponse(response, schemas.Build()); @@ -447,9 +447,9 @@ export class ActorClient extends ResourceClient { const parsed = parseArgument(options, lastRunOptionsSchema, 'ActorLastRunOptions'); return new RunClient( - this._subResourceOptions({ + this.subResourceOptions({ id: 'last', - params: this._params(parsed), + params: this.buildParams(parsed), resourcePath: 'runs', }), ); @@ -463,7 +463,7 @@ export class ActorClient extends ResourceClient { */ builds(): BuildCollectionClient { return new BuildCollectionClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'builds', }), ); @@ -477,7 +477,7 @@ export class ActorClient extends ResourceClient { */ runs(): RunCollectionClient { return new RunCollectionClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'runs', }), ); @@ -493,7 +493,7 @@ export class ActorClient extends ResourceClient { version(versionNumber: string): ActorVersionClient { parseArgument(versionNumber, versionNumberSchema); return new ActorVersionClient( - this._subResourceOptions({ + this.subResourceOptions({ id: versionNumber, }), ); @@ -506,7 +506,7 @@ export class ActorClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-versions-get */ versions(): ActorVersionCollectionClient { - return new ActorVersionCollectionClient(this._subResourceOptions()); + return new ActorVersionCollectionClient(this.subResourceOptions()); } /** @@ -516,7 +516,7 @@ export class ActorClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-webhooks-get */ webhooks(): WebhookCollectionClient { - return new WebhookCollectionClient(this._subResourceOptions()); + return new WebhookCollectionClient(this.subResourceOptions()); } } diff --git a/src/resource_clients/actor_collection.ts b/src/resource_clients/actor_collection.ts index a72345366..4522c968d 100644 --- a/src/resource_clients/actor_collection.ts +++ b/src/resource_clients/actor_collection.ts @@ -72,7 +72,7 @@ export class ActorCollectionClient extends ResourceCollectionClient { list(options: ActorCollectionListOptions = {}): PaginatedIterator { const parsed = parseArgument(options, listOptionsSchema, 'ActorCollectionListOptions'); - return this._listPaginated(schemas.ListOfActors(), parsed); + return this.listResourcesPaginated(schemas.ListOfActors(), parsed); } /** @@ -85,7 +85,7 @@ export class ActorCollectionClient extends ResourceCollectionClient { async create(actor: ActorCollectionCreateOptions): Promise { parseArgument(actor, actorCreateSchema); - return this._create(schemas.Actor(), actor); + return this.createResource(schemas.Actor(), actor); } } diff --git a/src/resource_clients/actor_env_var.ts b/src/resource_clients/actor_env_var.ts index bdf12c5a7..f7995c3fe 100644 --- a/src/resource_clients/actor_env_var.ts +++ b/src/resource_clients/actor_env_var.ts @@ -45,7 +45,7 @@ export class ActorEnvVarClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-version-env-var-get */ async get(): Promise { - return this._get(schemas.EnvVar()); + return this.getResource(schemas.EnvVar()); } /** @@ -57,7 +57,7 @@ export class ActorEnvVarClient extends ResourceClient { */ async update(actorEnvVar: ActorEnvironmentVariable): Promise { parseArgument(actorEnvVar, anyObjectSchema); - return this._update(schemas.EnvVar(), actorEnvVar); + return this.updateResource(schemas.EnvVar(), actorEnvVar); } /** @@ -66,6 +66,6 @@ export class ActorEnvVarClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-version-env-var-delete */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } } diff --git a/src/resource_clients/actor_env_var_collection.ts b/src/resource_clients/actor_env_var_collection.ts index f698bc853..2edcc9d64 100644 --- a/src/resource_clients/actor_env_var_collection.ts +++ b/src/resource_clients/actor_env_var_collection.ts @@ -64,7 +64,7 @@ export class ActorEnvVarCollectionClient extends ResourceCollectionClient { * @see https://docs.apify.com/api/v2/act-version-env-vars-get */ list(): Promise & AsyncIterable { - return this._listPaginated(schemas.ListOfEnvVars()); + return this.listResourcesPaginated(schemas.ListOfEnvVars()); } /** @@ -76,7 +76,7 @@ export class ActorEnvVarCollectionClient extends ResourceCollectionClient { */ async create(actorEnvVar: ActorEnvironmentVariable): Promise { parseArgument(actorEnvVar, actorEnvVarSchema); - return this._create(schemas.EnvVar(), actorEnvVar); + return this.createResource(schemas.EnvVar(), actorEnvVar); } } diff --git a/src/resource_clients/actor_version.ts b/src/resource_clients/actor_version.ts index d7bd902af..9000772cb 100644 --- a/src/resource_clients/actor_version.ts +++ b/src/resource_clients/actor_version.ts @@ -64,7 +64,7 @@ export class ActorVersionClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-version-get */ async get(): Promise { - return this._get(schemas.Version()); + return this.getResource(schemas.Version()); } /** @@ -77,7 +77,7 @@ export class ActorVersionClient extends ResourceClient { async update(newFields: ActorVersionUpdateData): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.Version(), newFields); + return this.updateResource(schemas.Version(), newFields); } /** @@ -86,7 +86,7 @@ export class ActorVersionClient extends ResourceClient { * @see https://docs.apify.com/api/v2/act-version-delete */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } /** @@ -100,7 +100,7 @@ export class ActorVersionClient extends ResourceClient { envVar(envVarName: string): ActorEnvVarClient { parseArgument(envVarName, envVarNameSchema); return new ActorEnvVarClient( - this._subResourceOptions({ + this.subResourceOptions({ id: envVarName, }), ); @@ -114,7 +114,7 @@ export class ActorVersionClient extends ResourceClient { * @since Added in 2.1.0 */ envVars(): ActorEnvVarCollectionClient { - return new ActorEnvVarCollectionClient(this._subResourceOptions()); + return new ActorEnvVarCollectionClient(this.subResourceOptions()); } } diff --git a/src/resource_clients/actor_version_collection.ts b/src/resource_clients/actor_version_collection.ts index e7f49365f..ce5d3ace0 100644 --- a/src/resource_clients/actor_version_collection.ts +++ b/src/resource_clients/actor_version_collection.ts @@ -63,7 +63,7 @@ export class ActorVersionCollectionClient extends ResourceCollectionClient { * @see https://docs.apify.com/api/v2/act-versions-get */ list(): Promise & AsyncIterable { - return this._listPaginated(schemas.ListOfVersions()); + return this.listResourcesPaginated(schemas.ListOfVersions()); } /** @@ -76,7 +76,7 @@ export class ActorVersionCollectionClient extends ResourceCollectionClient { async create(actorVersion: ActorVersion): Promise { parseArgument(actorVersion, actorVersionSchema); - return this._create(schemas.Version(), actorVersion); + return this.createResource(schemas.Version(), actorVersion); } } diff --git a/src/resource_clients/build.ts b/src/resource_clients/build.ts index fdff0ff34..e63e08554 100644 --- a/src/resource_clients/build.ts +++ b/src/resource_clients/build.ts @@ -67,7 +67,7 @@ export class BuildClient extends ResourceClient { async get(options: BuildClientGetOptions = {}): Promise { const parsed = parseArgument(options, getOptionsSchema, 'BuildClientGetOptions'); - return this._get(schemas.Build(), parsed); + return this.getResource(schemas.Build(), parsed); } /** @@ -85,9 +85,9 @@ export class BuildClient extends ResourceClient { */ async abort(): Promise { const response = await this.httpClient.call({ - url: this._url('abort'), + url: this.buildUrl('abort'), method: 'POST', - params: this._params(), + params: this.buildParams(), }); return parseResponse(response, schemas.Build()); @@ -100,7 +100,7 @@ export class BuildClient extends ResourceClient { * @since Added in 2.8.1 */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } /** @@ -112,9 +112,9 @@ export class BuildClient extends ResourceClient { */ async getOpenApiDefinition(): Promise { const response = await this.httpClient.call({ - url: this._url('openapi.json'), + url: this.buildUrl('openapi.json'), method: 'GET', - params: this._params(), + params: this.buildParams(), }); return response.data; @@ -153,7 +153,7 @@ export class BuildClient extends ResourceClient { async waitForFinish(options: BuildClientWaitForFinishOptions = {}): Promise { const parsed = parseArgument(options, waitForFinishOptionsSchema, 'BuildClientWaitForFinishOptions'); - return this._waitForFinish(schemas.Build(), parsed); + return this.waitForJobFinish(schemas.Build(), parsed); } /** @@ -174,7 +174,7 @@ export class BuildClient extends ResourceClient { */ log(): LogClient { return new LogClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'log', }), ); diff --git a/src/resource_clients/build_collection.ts b/src/resource_clients/build_collection.ts index 93685ecc0..a363237bc 100644 --- a/src/resource_clients/build_collection.ts +++ b/src/resource_clients/build_collection.ts @@ -69,7 +69,7 @@ export class BuildCollectionClient extends ResourceCollectionClient { list(options: BuildCollectionClientListOptions = {}): PaginatedIterator { const parsed = parseArgument(options, listOptionsSchema, 'BuildCollectionClientListOptions'); - return this._listPaginated(schemas.ListOfBuilds(), parsed); + return this.listResourcesPaginated(schemas.ListOfBuilds(), parsed); } } diff --git a/src/resource_clients/dataset.ts b/src/resource_clients/dataset.ts index ec15da699..25284e525 100644 --- a/src/resource_clients/dataset.ts +++ b/src/resource_clients/dataset.ts @@ -130,7 +130,7 @@ export class DatasetClient< * @see https://docs.apify.com/api/v2/dataset-get */ async get(): Promise { - return this._get(schemas.Dataset(), {}, SMALL_TIMEOUT_MILLIS); + return this.getResource(schemas.Dataset(), {}, SMALL_TIMEOUT_MILLIS); } /** @@ -143,7 +143,7 @@ export class DatasetClient< async update(newFields: DatasetClientUpdateOptions): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.Dataset(), newFields, SMALL_TIMEOUT_MILLIS); + return this.updateResource(schemas.Dataset(), newFields, SMALL_TIMEOUT_MILLIS); } /** @@ -152,7 +152,7 @@ export class DatasetClient< * @see https://docs.apify.com/api/v2/dataset-delete */ async delete(): Promise { - return this._delete(SMALL_TIMEOUT_MILLIS); + return this.deleteResource(SMALL_TIMEOUT_MILLIS); } /** @@ -206,16 +206,16 @@ export class DatasetClient< datasetListOptions: DatasetClientListItemOptions = {}, ): Promise> => { const response = await this.httpClient.call({ - url: this._url('items'), + url: this.buildUrl('items'), method: 'GET', - params: this._params(datasetListOptions), + params: this.buildParams(datasetListOptions), timeout: DEFAULT_TIMEOUT_MILLIS, }); - return this._createPaginationList(response, datasetListOptions.desc ?? false); + return this.createPaginationList(response, datasetListOptions.desc ?? false); }; - return this._listPaginatedFromCallback(fetchItems, parsed); + return this.listPaginatedFromCallback(fetchItems, parsed); } /** @@ -266,9 +266,9 @@ export class DatasetClient< const parsed = parseArgument(options, downloadItemsOptionsSchema, 'DatasetClientDownloadItemsOptions'); const { data } = await this.httpClient.call({ - url: this._url('items'), + url: this.buildUrl('items'), method: 'GET', - params: this._params({ + params: this.buildParams({ format, ...parsed, }), @@ -315,13 +315,13 @@ export class DatasetClient< parseArgument(items, pushItemsSchema); await this.httpClient.call({ - url: this._url('items'), + url: this.buildUrl('items'), method: 'POST', headers: { 'content-type': 'application/json; charset=utf-8', }, data: items, - params: this._params(), + params: this.buildParams(), doNotRetryTimeouts: true, // see timeout handling in http-client timeout: MEDIUM_TIMEOUT_MILLIS, }); @@ -339,9 +339,9 @@ export class DatasetClient< */ async getStatistics(): Promise { const response = await this.httpClient.call({ - url: this._url('statistics'), + url: this.buildUrl('statistics'), method: 'GET', - params: this._params(), + params: this.buildParams(), timeout: SMALL_TIMEOUT_MILLIS, }); return parseResponse(response, schemas.DatasetStatistics()); @@ -386,7 +386,7 @@ export class DatasetClient< const { expiresInSecs, ...queryOptions } = parsed; - let createdItemsPublicUrl = new URL(this._publicUrl('items')); + let createdItemsPublicUrl = new URL(this.buildPublicUrl('items')); if (dataset?.urlSigningSecretKey) { const signature = await createStorageContentSignatureAsync({ @@ -402,7 +402,7 @@ export class DatasetClient< return createdItemsPublicUrl.toString(); } - private _createPaginationList(response: ApifyResponse, userProvidedDesc: boolean): PaginatedList { + private createPaginationList(response: ApifyResponse, userProvidedDesc: boolean): PaginatedList { return { items: response.data, total: Number(response.headers['x-apify-pagination-total']), diff --git a/src/resource_clients/dataset_collection.ts b/src/resource_clients/dataset_collection.ts index 525641428..d9847064d 100644 --- a/src/resource_clients/dataset_collection.ts +++ b/src/resource_clients/dataset_collection.ts @@ -74,7 +74,7 @@ export class DatasetCollectionClient extends ResourceCollectionClient { ): Promise & AsyncIterable { const parsed = parseArgument(options, listOptionsSchema, 'DatasetCollectionClientListOptions'); - return this._listPaginated(schemas.ListOfDatasets(), parsed); + return this.listResourcesPaginated(schemas.ListOfDatasets(), parsed); } /** @@ -89,7 +89,7 @@ export class DatasetCollectionClient extends ResourceCollectionClient { parseArgument(name, nameSchema); parseArgument(options?.schema, schemaSchema); // TODO: Add schema validation - return this._getOrCreate(schemas.Dataset(), name, options); + return this.getOrCreateResource(schemas.Dataset(), name, options); } } diff --git a/src/resource_clients/key_value_store.ts b/src/resource_clients/key_value_store.ts index 16b7e8aec..df5da27c1 100644 --- a/src/resource_clients/key_value_store.ts +++ b/src/resource_clients/key_value_store.ts @@ -122,7 +122,7 @@ export class KeyValueStoreClient extends ResourceClient { * @see https://docs.apify.com/api/v2/key-value-store-get */ async get(): Promise { - return this._get(schemas.KeyValueStore(), {}, SMALL_TIMEOUT_MILLIS); + return this.getResource(schemas.KeyValueStore(), {}, SMALL_TIMEOUT_MILLIS); } /** @@ -138,7 +138,7 @@ export class KeyValueStoreClient extends ResourceClient { async update(newFields: KeyValueClientUpdateOptions): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.KeyValueStore(), newFields, DEFAULT_TIMEOUT_MILLIS); + return this.updateResource(schemas.KeyValueStore(), newFields, DEFAULT_TIMEOUT_MILLIS); } /** @@ -147,7 +147,7 @@ export class KeyValueStoreClient extends ResourceClient { * @see https://docs.apify.com/api/v2/key-value-store-delete */ async delete(): Promise { - return this._delete(SMALL_TIMEOUT_MILLIS); + return this.deleteResource(SMALL_TIMEOUT_MILLIS); } /** @@ -194,9 +194,9 @@ export class KeyValueStoreClient extends ResourceClient { kvsListOptions: KeyValueClientListKeysOptions = {}, ): Promise => { const response = await this.httpClient.call({ - url: this._url('keys'), + url: this.buildUrl('keys'), method: 'GET', - params: this._params(kvsListOptions), + params: this.buildParams(kvsListOptions), timeout: MEDIUM_TIMEOUT_MILLIS, }); @@ -258,7 +258,7 @@ export class KeyValueStoreClient extends ResourceClient { const store = await this.get(); - const recordPublicUrl = new URL(this._publicUrl(['records', key])); + const recordPublicUrl = new URL(this.buildPublicUrl(['records', key])); if (store?.urlSigningSecretKey) { const signature = await createHmacSignatureAsync(store.urlSigningSecretKey, key); @@ -298,7 +298,7 @@ export class KeyValueStoreClient extends ResourceClient { const { expiresInSecs, ...queryOptions } = parsed; - let createdPublicKeysUrl = new URL(this._publicUrl('keys')); + let createdPublicKeysUrl = new URL(this.buildPublicUrl('keys')); if (store?.urlSigningSecretKey) { const signature = await createStorageContentSignatureAsync({ @@ -334,9 +334,9 @@ export class KeyValueStoreClient extends ResourceClient { */ async recordExists(key: string): Promise { const requestOpts: Record = { - url: this._url(['records', key]), + url: this.buildUrl(['records', key]), method: 'HEAD', - params: this._params(), + params: this.buildParams(), }; try { @@ -388,9 +388,9 @@ export class KeyValueStoreClient extends ResourceClient { if (parsed.signature) queryParams.signature = parsed.signature; const requestOpts: Record = { - url: this._url(['records', key]), + url: this.buildUrl(['records', key]), method: 'GET', - params: this._params(queryParams), + params: this.buildParams(queryParams), timeout: DEFAULT_TIMEOUT_MILLIS, }; @@ -493,9 +493,9 @@ export class KeyValueStoreClient extends ResourceClient { } const uploadOpts: ApifyRequestConfig = { - url: this._url(['records', key]), + url: this.buildUrl(['records', key]), method: 'PUT', - params: this._params(), + params: this.buildParams(), data: value, headers: contentType ? { 'content-type': contentType } : undefined, doNotRetryTimeouts, @@ -520,9 +520,9 @@ export class KeyValueStoreClient extends ResourceClient { parseArgument(key, keySchema); await this.httpClient.call({ - url: this._url(['records', key]), + url: this.buildUrl(['records', key]), method: 'DELETE', - params: this._params(), + params: this.buildParams(), timeout: SMALL_TIMEOUT_MILLIS, }); } diff --git a/src/resource_clients/key_value_store_collection.ts b/src/resource_clients/key_value_store_collection.ts index fcb87672c..dc63e64a0 100644 --- a/src/resource_clients/key_value_store_collection.ts +++ b/src/resource_clients/key_value_store_collection.ts @@ -74,7 +74,7 @@ export class KeyValueStoreCollectionClient extends ResourceCollectionClient { ): Promise & AsyncIterable { const parsed = parseArgument(options, listOptionsSchema, 'KeyValueStoreCollectionClientListOptions'); - return this._listPaginated(schemas.ListOfKeyValueStores(), parsed); + return this.listResourcesPaginated(schemas.ListOfKeyValueStores(), parsed); } /** @@ -92,7 +92,7 @@ export class KeyValueStoreCollectionClient extends ResourceCollectionClient { parseArgument(name, nameSchema); parseArgument(options?.schema, schemaSchema); // TODO: Add schema validation - return this._getOrCreate(schemas.KeyValueStore(), name, options); + return this.getOrCreateResource(schemas.KeyValueStore(), name, options); } } diff --git a/src/resource_clients/log.ts b/src/resource_clients/log.ts index f2a01f2e2..8445315a6 100644 --- a/src/resource_clients/log.ts +++ b/src/resource_clients/log.ts @@ -56,9 +56,9 @@ export class LogClient extends ResourceClient { */ async get(options: LogOptions = {}): Promise { const requestOpts: ApifyRequestConfig = { - url: this._url(), + url: this.buildUrl(), method: 'GET', - params: this._params(options), + params: this.buildParams(options), }; try { @@ -87,9 +87,9 @@ export class LogClient extends ResourceClient { }; const requestOpts: ApifyRequestConfig = { - url: this._url(), + url: this.buildUrl(), method: 'GET', - params: this._params(params), + params: this.buildParams(params), responseType: 'stream', }; diff --git a/src/resource_clients/request_queue.ts b/src/resource_clients/request_queue.ts index f6399c937..cd8cb884a 100644 --- a/src/resource_clients/request_queue.ts +++ b/src/resource_clients/request_queue.ts @@ -163,7 +163,7 @@ export class RequestQueueClient extends ResourceClient { * @see https://docs.apify.com/api/v2/request-queue-get */ async get(): Promise { - return this._get(schemas.RequestQueue(), {}, SMALL_TIMEOUT_MILLIS); + return this.getResource(schemas.RequestQueue(), {}, SMALL_TIMEOUT_MILLIS); } /** @@ -176,7 +176,7 @@ export class RequestQueueClient extends ResourceClient { async update(newFields: RequestQueueClientUpdateOptions): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.RequestQueue(), newFields, SMALL_TIMEOUT_MILLIS); + return this.updateResource(schemas.RequestQueue(), newFields, SMALL_TIMEOUT_MILLIS); } /** @@ -185,7 +185,7 @@ export class RequestQueueClient extends ResourceClient { * @see https://docs.apify.com/api/v2/request-queue-delete */ async delete(): Promise { - return this._delete(SMALL_TIMEOUT_MILLIS); + return this.deleteResource(SMALL_TIMEOUT_MILLIS); } /** @@ -202,10 +202,10 @@ export class RequestQueueClient extends ResourceClient { const parsed = parseArgument(options, listHeadOptionsSchema, 'RequestQueueClientListHeadOptions'); const response = await this.httpClient.call({ - url: this._url('head'), + url: this.buildUrl('head'), method: 'GET', timeout: Math.min(SMALL_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params({ + params: this.buildParams({ limit: parsed.limit, clientKey: this.clientKey, }), @@ -253,10 +253,10 @@ export class RequestQueueClient extends ResourceClient { const parsed = parseArgument(options, listAndLockHeadOptionsSchema, 'RequestQueueClientListAndLockHeadOptions'); const response = await this.httpClient.call({ - url: this._url('head/lock'), + url: this.buildUrl('head/lock'), method: 'POST', timeout: Math.min(MEDIUM_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params({ + params: this.buildParams({ limit: parsed.limit, lockSecs: parsed.lockSecs, clientKey: this.clientKey, @@ -312,11 +312,11 @@ export class RequestQueueClient extends ResourceClient { const parsed = parseArgument(options, forefrontOptionsSchema, 'RequestQueueClientAddRequestOptions'); const response = await this.httpClient.call({ - url: this._url('requests'), + url: this.buildUrl('requests'), method: 'POST', timeout: Math.min(SMALL_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), data: request, - params: this._params({ + params: this.buildParams({ forefront: parsed.forefront, clientKey: this.clientKey, }), @@ -330,7 +330,7 @@ export class RequestQueueClient extends ResourceClient { * * @private */ - protected async _batchAddRequests( + protected async addRequestBatch( requests: RequestQueueClientRequestToAdd[], options: RequestQueueClientAddRequestOptions = {}, ): Promise { @@ -338,11 +338,11 @@ export class RequestQueueClient extends ResourceClient { const parsed = parseArgument(options, forefrontOptionsSchema, 'RequestQueueClientAddRequestOptions'); const response = await this.httpClient.call({ - url: this._url('requests/batch'), + url: this.buildUrl('requests/batch'), method: 'POST', timeout: Math.min(MEDIUM_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), data: requests, - params: this._params({ + params: this.buildParams({ forefront: parsed.forefront, clientKey: this.clientKey, }), @@ -351,7 +351,7 @@ export class RequestQueueClient extends ResourceClient { return parseResponse(response, schemas.BatchAddResult()); } - protected async _batchAddRequestsWithRetries( + protected async addRequestBatchWithRetries( requests: RequestQueueClientRequestToAdd[], options: RequestQueueClientBatchAddRequestWithRetriesOptions = {}, ): Promise { @@ -369,7 +369,7 @@ export class RequestQueueClient extends ResourceClient { let unprocessedRequests: RequestQueueClientBatchRequestsOperationResult['unprocessedRequests'] = []; for (let i = 0; i < 1 + maxUnprocessedRequestsRetries; i++) { try { - const response = await this._batchAddRequests(remainingRequests, { + const response = await this.addRequestBatch(remainingRequests, { forefront, }); processedRequests.push(...response.processedRequests); @@ -485,7 +485,7 @@ export class RequestQueueClient extends ResourceClient { while (i < requests.length) { const slicedRequests = requests.slice(i, i + REQUEST_QUEUE_MAX_REQUESTS_PER_BATCH_OPERATION); const requestsInBatch = sliceArrayByByteLength(slicedRequests, payloadSizeLimitBytes, i); - const requestPromise = this._batchAddRequestsWithRetries(requestsInBatch, options); + const requestPromise = this.addRequestBatchWithRetries(requestsInBatch, options); executingRequests.add(requestPromise); // A rejection reaches the caller through the awaits below; this bookkeeping chain only has to avoid // turning it into an unhandled one of its own. @@ -532,11 +532,11 @@ export class RequestQueueClient extends ResourceClient { parseArgument(requests, batchDeleteRequestsSchema); const response = await this.httpClient.call({ - url: this._url('requests/batch'), + url: this.buildUrl('requests/batch'), method: 'DELETE', timeout: Math.min(SMALL_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), data: requests, - params: this._params({ + params: this.buildParams({ clientKey: this.clientKey, }), }); @@ -554,10 +554,10 @@ export class RequestQueueClient extends ResourceClient { async getRequest(id: string): Promise { parseArgument(id, requestIdSchema); const requestOpts: ApifyRequestConfig = { - url: this._url(['requests', id]), + url: this.buildUrl(['requests', id]), method: 'GET', timeout: Math.min(SMALL_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params(), + params: this.buildParams(), }; try { const response = await this.httpClient.call(requestOpts); @@ -585,11 +585,11 @@ export class RequestQueueClient extends ResourceClient { const parsed = parseArgument(options, forefrontOptionsSchema, 'RequestQueueClientAddRequestOptions'); const response = await this.httpClient.call({ - url: this._url(['requests', request.id]), + url: this.buildUrl(['requests', request.id]), method: 'PUT', timeout: Math.min(MEDIUM_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), data: request, - params: this._params({ + params: this.buildParams({ forefront: parsed.forefront, clientKey: this.clientKey, }), @@ -607,10 +607,10 @@ export class RequestQueueClient extends ResourceClient { parseArgument(id, requestIdSchema); await this.httpClient.call({ - url: this._url(['requests', id]), + url: this.buildUrl(['requests', id]), method: 'DELETE', timeout: Math.min(SMALL_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params({ + params: this.buildParams({ clientKey: this.clientKey, }), }); @@ -653,10 +653,10 @@ export class RequestQueueClient extends ResourceClient { ); const response = await this.httpClient.call({ - url: this._url(['requests', id, 'lock']), + url: this.buildUrl(['requests', id, 'lock']), method: 'PUT', timeout: Math.min(MEDIUM_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params({ + params: this.buildParams({ forefront: parsed.forefront, lockSecs: parsed.lockSecs, clientKey: this.clientKey, @@ -682,10 +682,10 @@ export class RequestQueueClient extends ResourceClient { const parsed = parseArgument(options, forefrontOptionsSchema, 'RequestQueueClientDeleteRequestLockOptions'); await this.httpClient.call({ - url: this._url(['requests', id, 'lock']), + url: this.buildUrl(['requests', id, 'lock']), method: 'DELETE', timeout: Math.min(SMALL_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params({ + params: this.buildParams({ forefront: parsed.forefront, clientKey: this.clientKey, }), @@ -712,10 +712,10 @@ export class RequestQueueClient extends ResourceClient { rqListOptions: RequestQueueClientListRequestsOptions = {}, ): Promise => { const response = await this.httpClient.call({ - url: this._url('requests'), + url: this.buildUrl('requests'), method: 'GET', timeout: Math.min(MEDIUM_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params({ + params: this.buildParams({ ...rqListOptions, filter: rqListOptions.filter ? rqListOptions.filter.join(',') : undefined, clientKey: this.clientKey, @@ -769,10 +769,10 @@ export class RequestQueueClient extends ResourceClient { */ async unlockRequests(): Promise { const response = await this.httpClient.call({ - url: this._url('requests/unlock'), + url: this.buildUrl('requests/unlock'), method: 'POST', timeout: Math.min(MEDIUM_TIMEOUT_MILLIS, this.timeoutMillis ?? Infinity), - params: this._params({ + params: this.buildParams({ clientKey: this.clientKey, }), }); diff --git a/src/resource_clients/request_queue_collection.ts b/src/resource_clients/request_queue_collection.ts index 8e16d57b9..004cec1ab 100644 --- a/src/resource_clients/request_queue_collection.ts +++ b/src/resource_clients/request_queue_collection.ts @@ -73,7 +73,7 @@ export class RequestQueueCollectionClient extends ResourceCollectionClient { ): Promise & AsyncIterable { const parsed = parseArgument(options, listOptionsSchema, 'RequestQueueCollectionListOptions'); - return this._listPaginated(schemas.ListOfRequestQueues(), parsed); + return this.listResourcesPaginated(schemas.ListOfRequestQueues(), parsed); } /** @@ -86,7 +86,7 @@ export class RequestQueueCollectionClient extends ResourceCollectionClient { async getOrCreate(name?: string): Promise { parseArgument(name, nameSchema); - return this._getOrCreate(schemas.RequestQueue(), name); + return this.getOrCreateResource(schemas.RequestQueue(), name); } } diff --git a/src/resource_clients/run.ts b/src/resource_clients/run.ts index ebff18307..441339e77 100644 --- a/src/resource_clients/run.ts +++ b/src/resource_clients/run.ts @@ -94,7 +94,7 @@ export class RunClient extends ResourceClient { async get(options: RunGetOptions = {}): Promise { const parsed = parseArgument(options, getOptionsSchema, 'RunGetOptions'); - return this._get(schemas.Run(), parsed); + return this.getResource(schemas.Run(), parsed); } /** @@ -118,9 +118,9 @@ export class RunClient extends ResourceClient { const parsed = parseArgument(options, abortOptionsSchema, 'RunAbortOptions'); const response = await this.httpClient.call({ - url: this._url('abort'), + url: this.buildUrl('abort'), method: 'POST', - params: this._params(parsed), + params: this.buildParams(parsed), }); return parseResponse(response, schemas.Run()); @@ -133,7 +133,7 @@ export class RunClient extends ResourceClient { * @since Added in 2.8.1 */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } /** @@ -164,7 +164,7 @@ export class RunClient extends ResourceClient { parseArgument(targetActorId, targetActorIdSchema); const parsed = parseArgument(options, metamorphOptionsSchema, 'RunMetamorphOptions'); - const safeTargetActorId = this._toSafeId(targetActorId); + const safeTargetActorId = this.toSafeId(targetActorId); const params = { targetActorId: safeTargetActorId, @@ -172,10 +172,10 @@ export class RunClient extends ResourceClient { }; const request: AxiosRequestConfig = { - url: this._url('metamorph'), + url: this.buildUrl('metamorph'), method: 'POST', data: input, - params: this._params(params), + params: this.buildParams(params), // Apify internal property. Tells the request serialization interceptor // to stringify functions to JSON, instead of omitting them. // TODO: remove this ts-expect-error once we have defined custom Apify axios configs @@ -211,7 +211,7 @@ export class RunClient extends ResourceClient { */ async reboot(): Promise { const request: AxiosRequestConfig = { - url: this._url('reboot'), + url: this.buildUrl('reboot'), method: 'POST', }; @@ -240,7 +240,7 @@ export class RunClient extends ResourceClient { async update(newFields: RunUpdateOptions): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.Run(), newFields); + return this.updateResource(schemas.Run(), newFields); } /** @@ -270,9 +270,9 @@ export class RunClient extends ResourceClient { const parsed = parseArgument(options, resurrectOptionsSchema, 'RunResurrectOptions'); const response = await this.httpClient.call({ - url: this._url('resurrect'), + url: this.buildUrl('resurrect'), method: 'POST', - params: this._params(parsed), + params: this.buildParams(parsed), }); return parseResponse(response, schemas.Run()); @@ -301,7 +301,7 @@ export class RunClient extends ResourceClient { const idempotencyKey = providedIdempotencyKey ?? `${this.id}-${eventName}-${Date.now()}-${randomSuffix}`; const request: AxiosRequestConfig = { - url: this._url('charge'), + url: this.buildUrl('charge'), method: 'POST', data: { eventName, @@ -346,7 +346,7 @@ export class RunClient extends ResourceClient { async waitForFinish(options: RunWaitForFinishOptions = {}): Promise { const parsed = parseArgument(options, waitForFinishOptionsSchema, 'RunWaitForFinishOptions'); - return this._waitForFinish(schemas.Run(), parsed); + return this.waitForJobFinish(schemas.Run(), parsed); } /** @@ -365,7 +365,7 @@ export class RunClient extends ResourceClient { */ dataset(): DatasetClient { return new DatasetClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'dataset', }), ); @@ -388,7 +388,7 @@ export class RunClient extends ResourceClient { */ keyValueStore(): KeyValueStoreClient { return new KeyValueStoreClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'key-value-store', }), ); @@ -411,7 +411,7 @@ export class RunClient extends ResourceClient { */ requestQueue(): RequestQueueClient { return new RequestQueueClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'request-queue', }), ); @@ -434,7 +434,7 @@ export class RunClient extends ResourceClient { */ log(): LogClient { return new LogClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'log', }), ); diff --git a/src/resource_clients/run_collection.ts b/src/resource_clients/run_collection.ts index 9b051e397..9612b034c 100644 --- a/src/resource_clients/run_collection.ts +++ b/src/resource_clients/run_collection.ts @@ -73,7 +73,7 @@ export class RunCollectionClient extends ResourceCollectionClient { list(options: RunCollectionListOptions = {}): PaginatedIterator { const parsed = parseArgument(options, listOptionsSchema, 'RunCollectionListOptions'); - return this._listPaginated(schemas.ListOfRuns(), parsed); + return this.listResourcesPaginated(schemas.ListOfRuns(), parsed); } } diff --git a/src/resource_clients/schedule.ts b/src/resource_clients/schedule.ts index 759a5f443..cd7ab0030 100644 --- a/src/resource_clients/schedule.ts +++ b/src/resource_clients/schedule.ts @@ -61,7 +61,7 @@ export class ScheduleClient extends ResourceClient { * @see https://docs.apify.com/api/v2/schedule-get */ async get(): Promise { - return this._get(schemas.Schedule()); + return this.getResource(schemas.Schedule()); } /** @@ -73,7 +73,7 @@ export class ScheduleClient extends ResourceClient { */ async update(newFields: ScheduleCreateOrUpdateData): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.Schedule(), newFields); + return this.updateResource(schemas.Schedule(), newFields); } /** @@ -82,7 +82,7 @@ export class ScheduleClient extends ResourceClient { * @see https://docs.apify.com/api/v2/schedule-delete */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } /** @@ -93,9 +93,9 @@ export class ScheduleClient extends ResourceClient { */ async getLog(): Promise { const response = await this.httpClient.call({ - url: this._url('log'), + url: this.buildUrl('log'), method: 'GET', - params: this._params(), + params: this.buildParams(), }); return parseResponse(response, scheduleLogSchema); } diff --git a/src/resource_clients/schedule_collection.ts b/src/resource_clients/schedule_collection.ts index 2c8c70d2c..9de0de73c 100644 --- a/src/resource_clients/schedule_collection.ts +++ b/src/resource_clients/schedule_collection.ts @@ -71,7 +71,7 @@ export class ScheduleCollectionClient extends ResourceCollectionClient { list(options: ScheduleCollectionListOptions = {}): PaginatedIterator { const parsed = parseArgument(options, listOptionsSchema, 'ScheduleCollectionListOptions'); - return this._listPaginated(schemas.ListOfSchedules(), parsed); + return this.listResourcesPaginated(schemas.ListOfSchedules(), parsed); } /** @@ -84,7 +84,7 @@ export class ScheduleCollectionClient extends ResourceCollectionClient { async create(schedule?: ScheduleCreateOrUpdateData): Promise { parseArgument(schedule, scheduleCreateSchema); - return this._create(schemas.Schedule(), schedule); + return this.createResource(schemas.Schedule(), schedule); } } diff --git a/src/resource_clients/store_collection.ts b/src/resource_clients/store_collection.ts index 9244f58b2..7100ca073 100644 --- a/src/resource_clients/store_collection.ts +++ b/src/resource_clients/store_collection.ts @@ -74,7 +74,7 @@ export class StoreCollectionClient extends ResourceCollectionClient { list(options: StoreCollectionListOptions = {}): PaginatedIterator { const parsed = parseArgument(options, listOptionsSchema, 'StoreCollectionListOptions'); - return this._listPaginated(schemas.ListOfStoreActors(), parsed); + return this.listResourcesPaginated(schemas.ListOfStoreActors(), parsed); } } diff --git a/src/resource_clients/task.ts b/src/resource_clients/task.ts index 4ade81ba0..d4336de92 100644 --- a/src/resource_clients/task.ts +++ b/src/resource_clients/task.ts @@ -80,7 +80,7 @@ export class TaskClient extends ResourceClient { * @see https://docs.apify.com/api/v2/actor-task-get */ async get(): Promise { - return this._get(schemas.Task()); + return this.getResource(schemas.Task()); } /** @@ -93,7 +93,7 @@ export class TaskClient extends ResourceClient { async update(newFields: TaskUpdateData): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.Task(), newFields); + return this.updateResource(schemas.Task(), newFields); } /** @@ -134,7 +134,7 @@ export class TaskClient extends ResourceClient { * @see https://docs.apify.com/api/v2/actor-task-delete */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } /** @@ -171,10 +171,10 @@ export class TaskClient extends ResourceClient { }; const request: ApifyRequestConfig = { - url: this._url('runs'), + url: this.buildUrl('runs'), method: 'POST', data: input, - params: this._params(params), + params: this.buildParams(params), // Apify internal property. Tells the request serialization interceptor // to stringify functions to JSON, instead of omitting them. stringifyFunctions: true, @@ -226,9 +226,9 @@ export class TaskClient extends ResourceClient { */ async getInput(): Promise { const response = await this.httpClient.call({ - url: this._url('input'), + url: this.buildUrl('input'), method: 'GET', - params: this._params(), + params: this.buildParams(), }); return cast(response.data); } @@ -242,9 +242,9 @@ export class TaskClient extends ResourceClient { */ async updateInput(newFields: Dictionary | Dictionary[]): Promise { const response = await this.httpClient.call({ - url: this._url('input'), + url: this.buildUrl('input'), method: 'PUT', - params: this._params(), + params: this.buildParams(), data: newFields, }); @@ -264,9 +264,9 @@ export class TaskClient extends ResourceClient { const parsed = parseArgument(options, lastRunOptionsSchema, 'TaskLastRunOptions'); return new RunClient( - this._subResourceOptions({ + this.subResourceOptions({ id: 'last', - params: this._params(parsed), + params: this.buildParams(parsed), resourcePath: 'runs', }), ); @@ -280,7 +280,7 @@ export class TaskClient extends ResourceClient { */ runs(): RunCollectionClient { return new RunCollectionClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'runs', }), ); @@ -293,7 +293,7 @@ export class TaskClient extends ResourceClient { * @see https://docs.apify.com/api/v2/actor-task-webhooks-get */ webhooks(): WebhookCollectionClient { - return new WebhookCollectionClient(this._subResourceOptions()); + return new WebhookCollectionClient(this.subResourceOptions()); } } diff --git a/src/resource_clients/task_collection.ts b/src/resource_clients/task_collection.ts index cb4b2c9bf..058f3b25c 100644 --- a/src/resource_clients/task_collection.ts +++ b/src/resource_clients/task_collection.ts @@ -73,7 +73,7 @@ export class TaskCollectionClient extends ResourceCollectionClient { list(options: TaskCollectionListOptions = {}): PaginatedIterator { const parsed = parseArgument(options, listOptionsSchema, 'TaskCollectionListOptions'); - return this._listPaginated(schemas.ListOfTasks(), parsed); + return this.listResourcesPaginated(schemas.ListOfTasks(), parsed); } /** @@ -86,7 +86,7 @@ export class TaskCollectionClient extends ResourceCollectionClient { async create(task: TaskCreateData): Promise { parseArgument(task, anyObjectSchema); - return this._create(schemas.Task(), task); + return this.createResource(schemas.Task(), task); } } diff --git a/src/resource_clients/user.ts b/src/resource_clients/user.ts index 9d9fc0969..676346326 100644 --- a/src/resource_clients/user.ts +++ b/src/resource_clients/user.ts @@ -71,7 +71,7 @@ export class UserClient extends ResourceClient { * @see https://docs.apify.com/api/v2/user-get */ async get(): Promise { - return this._get(schemas.UserPrivateInfo()); + return this.getResource(schemas.UserPrivateInfo()); } /** @@ -83,9 +83,9 @@ export class UserClient extends ResourceClient { */ async monthlyUsage(): Promise { const response = await this.httpClient.call({ - url: this._url('usage/monthly'), + url: this.buildUrl('usage/monthly'), method: 'GET', - params: this._params(), + params: this.buildParams(), }); // `dailyServiceUsages[].date` does not end in `At`, so it has to be named for `parseDateFields`. return parseResponse(response, schemas.MonthlyUsage(), (key) => key === 'date'); @@ -100,9 +100,9 @@ export class UserClient extends ResourceClient { */ async limits(): Promise { const response = await this.httpClient.call({ - url: this._url('limits'), + url: this.buildUrl('limits'), method: 'GET', - params: this._params(), + params: this.buildParams(), }); return parseResponse(response, schemas.AccountLimits()); } @@ -116,9 +116,9 @@ export class UserClient extends ResourceClient { */ async updateLimits(options: LimitsUpdateOptions): Promise { const requestOpts: ApifyRequestConfig = { - url: this._url('limits'), + url: this.buildUrl('limits'), method: 'PUT', - params: this._params(), + params: this.buildParams(), data: options, }; await this.httpClient.call(requestOpts); diff --git a/src/resource_clients/webhook.ts b/src/resource_clients/webhook.ts index 1a1b7940f..d5b440978 100644 --- a/src/resource_clients/webhook.ts +++ b/src/resource_clients/webhook.ts @@ -63,7 +63,7 @@ export class WebhookClient extends ResourceClient { * @see https://docs.apify.com/api/v2/webhook-get */ async get(): Promise { - return this._get(schemas.Webhook()); + return this.getResource(schemas.Webhook()); } /** @@ -76,7 +76,7 @@ export class WebhookClient extends ResourceClient { async update(newFields: WebhookUpdateData): Promise { parseArgument(newFields, anyObjectSchema); - return this._update(schemas.Webhook(), newFields); + return this.updateResource(schemas.Webhook(), newFields); } /** @@ -85,7 +85,7 @@ export class WebhookClient extends ResourceClient { * @see https://docs.apify.com/api/v2/webhook-delete */ async delete(): Promise { - return this._delete(); + return this.deleteResource(); } /** @@ -96,9 +96,9 @@ export class WebhookClient extends ResourceClient { */ async test(): Promise { const response = await this.httpClient.call({ - url: this._url('test'), + url: this.buildUrl('test'), method: 'POST', - params: this._params(), + params: this.buildParams(), }); return parseResponse(response, schemas.WebhookDispatch()); } @@ -111,7 +111,7 @@ export class WebhookClient extends ResourceClient { */ dispatches(): WebhookDispatchCollectionClient { return new WebhookDispatchCollectionClient( - this._subResourceOptions({ + this.subResourceOptions({ resourcePath: 'dispatches', }), ); diff --git a/src/resource_clients/webhook_collection.ts b/src/resource_clients/webhook_collection.ts index 00d6e27b9..108ee2030 100644 --- a/src/resource_clients/webhook_collection.ts +++ b/src/resource_clients/webhook_collection.ts @@ -73,7 +73,7 @@ export class WebhookCollectionClient extends ResourceCollectionClient { ): PaginatedIterator> { const parsed = parseArgument(options, listOptionsSchema, 'WebhookCollectionListOptions'); - return this._listPaginated(schemas.ListOfWebhooks(), parsed); + return this.listResourcesPaginated(schemas.ListOfWebhooks(), parsed); } /** @@ -86,7 +86,7 @@ export class WebhookCollectionClient extends ResourceCollectionClient { async create(webhook?: WebhookUpdateData): Promise { parseArgument(webhook, webhookCreateSchema); - return this._create(schemas.Webhook(), webhook); + return this.createResource(schemas.Webhook(), webhook); } } diff --git a/src/resource_clients/webhook_dispatch.ts b/src/resource_clients/webhook_dispatch.ts index 34339e29c..35c42df42 100644 --- a/src/resource_clients/webhook_dispatch.ts +++ b/src/resource_clients/webhook_dispatch.ts @@ -47,6 +47,6 @@ export class WebhookDispatchClient extends ResourceClient { * @see https://docs.apify.com/api/v2/webhook-dispatch-get */ async get(): Promise { - return this._get(schemas.WebhookDispatch()); + return this.getResource(schemas.WebhookDispatch()); } } diff --git a/src/resource_clients/webhook_dispatch_collection.ts b/src/resource_clients/webhook_dispatch_collection.ts index 6c9df8eca..a885c545a 100644 --- a/src/resource_clients/webhook_dispatch_collection.ts +++ b/src/resource_clients/webhook_dispatch_collection.ts @@ -64,7 +64,7 @@ export class WebhookDispatchCollectionClient extends ResourceCollectionClient { list(options: WebhookDispatchCollectionListOptions = {}): PaginatedIterator { const parsed = parseArgument(options, listOptionsSchema, 'WebhookDispatchCollectionListOptions'); - return this._listPaginated(schemas.ListOfWebhookDispatches(), parsed); + return this.listResourcesPaginated(schemas.ListOfWebhookDispatches(), parsed); } } diff --git a/src/statistics.ts b/src/statistics.ts index d19923989..32264258a 100644 --- a/src/statistics.ts +++ b/src/statistics.ts @@ -26,7 +26,7 @@ export class Statistics { // attempt is never 0, // but we don't want index 0 empty const index = attempt - 1; - this._fillBlanksWithZeroes(index); + this.fillBlanksWithZeroes(index); this.rateLimitErrors[index]++; } @@ -35,7 +35,7 @@ export class Statistics { * number of zeroes by dynamically filling the empty indexes * when necessary. */ - private _fillBlanksWithZeroes(inclusiveIndex: number) { + private fillBlanksWithZeroes(inclusiveIndex: number) { if (this.rateLimitErrors.length <= inclusiveIndex) { for (let k = 0; k <= inclusiveIndex; k++) { if (typeof this.rateLimitErrors[k] !== 'number') { From ab374d64470722a15d857f58d4a6d7f45e6c5540 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 10 Sep 2026 19:24:02 +0200 Subject: [PATCH 2/6] test: cover the clientMethod mapping for the renamed helpers --- test/apify_api_error.test.ts | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/apify_api_error.test.ts b/test/apify_api_error.test.ts index c9e4ba7cd..8f7acc940 100644 --- a/test/apify_api_error.test.ts +++ b/test/apify_api_error.test.ts @@ -144,6 +144,68 @@ describe('ApifyApiError', () => { }); }); + describe('clientMethod names the public method, not the shared helper', () => { + // `400` as the resource id is what makes the mock server answer with an API error. + const cases: { helper: string; expected: string; call: (client: ApifyClient) => Promise }[] = [ + { helper: 'getResource', expected: 'ActorClient.get', call: (client) => client.actor('400').get() }, + { + helper: 'updateResource', + expected: 'ActorClient.update', + call: (client) => client.actor('400').update({ isPublic: false }), + }, + { + helper: 'deleteResource', + expected: 'ActorClient.delete', + call: (client) => client.actor('400').delete(), + }, + { + helper: 'listResources', + expected: 'ActorVersionCollectionClient.list', + call: (client) => client.actor('400').versions().list(), + }, + { + helper: 'createResource', + expected: 'ActorVersionCollectionClient.create', + call: (client) => + client.actor('400').versions().create({ + versionNumber: '0.0', + sourceType: 'GIT_REPO', + gitRepoUrl: 'https://github.com/user/repo.git', + }), + }, + { + helper: 'waitForJobFinish', + expected: 'RunClient.waitForFinish', + call: (client) => client.run('400').waitForFinish(), + }, + ]; + + test.each(cases)('$helper reports $expected', async ({ expected, call }) => { + const client = new ApifyClient({ baseUrl, maxRetries: 0, ...DEFAULT_OPTIONS }); + + await expect(call(client)).rejects.toMatchObject({ clientMethod: expected }); + }); + + test('addRequestBatch reports RequestQueueClient.batchAddRequests', async () => { + const client = new ApifyClient({ baseUrl, maxRetries: 0, ...DEFAULT_OPTIONS }); + // `batchAddRequests()` answers with unprocessed requests instead of throwing, so the helper is + // called directly to reach the error it builds. + const queue = client.requestQueue('some-queue-id') as any; + mockServer.setResponse({ + statusCode: 400, + body: { error: { type: 'some-type', message: 'Some message' } }, + }); + + try { + await expect( + queue.addRequestBatch([{ url: 'http://example.com', uniqueKey: 'some-key', method: 'GET' }]), + ).rejects.toMatchObject({ clientMethod: 'RequestQueueClient.batchAddRequests' }); + } finally { + mockServer.setResponse(null); + } + }); + }); + describe('subclass by HTTP status', () => { const response = (status: number, data: unknown = { error: { type: 'some-type', message: 'Some message' } }) => ({ status, data, config: { method: 'get', url: 'http://localhost/v2/acts' } }) as any; From 9ea7842c87068306a91483cfdb1e7c982734d0e5 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 10 Sep 2026 19:24:02 +0200 Subject: [PATCH 3/6] docs: add the underscore rename to the v3 upgrading guide --- docs/04_upgrading/upgrading_v3.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/04_upgrading/upgrading_v3.md b/docs/04_upgrading/upgrading_v3.md index f58f58b2f..09c778e4c 100644 --- a/docs/04_upgrading/upgrading_v3.md +++ b/docs/04_upgrading/upgrading_v3.md @@ -316,3 +316,32 @@ export HTTPS_PROXY=http://proxy.example.com:3128 ``` The same `proxy-agent` upgrade removes the `[DEP0169] DeprecationWarning` about `url.parse()` that Node.js 24 and newer printed on the client's first request. + +## Non-public members no longer carry an underscore + +Members declared `private` or `protected` had a leading underscore on top of the keyword in v2, and the underscore is gone in v3. Code that calls only the public methods of a client is unaffected. A class that extends one of the client classes and calls a protected helper has to switch to the new name. The base classes below are not exported, so you reach these members by extending a concrete client such as `ActorClient`. + +| Class | v2 | v3 | +| --- | --- | --- | +| `ApiClient` | `_url()` | `buildUrl()` | +| `ApiClient` | `_publicUrl()` | `buildPublicUrl()` | +| `ApiClient` | `_params()` | `buildParams()` | +| `ApiClient` | `_subResourceOptions()` | `subResourceOptions()` | +| `ApiClient` | `_toSafeId()` | `toSafeId()` | +| `ApiClient` | `_listPaginatedFromCallback()` | `listPaginatedFromCallback()` | +| `ResourceClient` | `_get()` | `getResource()` | +| `ResourceClient` | `_update()` | `updateResource()` | +| `ResourceClient` | `_delete()` | `deleteResource()` | +| `ResourceClient` | `_waitForFinish()` | `waitForJobFinish()` | +| `ResourceCollectionClient` | `_list()` | `listResources()` | +| `ResourceCollectionClient` | `_listPaginated()` | `listResourcesPaginated()` | +| `ResourceCollectionClient` | `_create()` | `createResource()` | +| `ResourceCollectionClient` | `_getOrCreate()` | `getOrCreateResource()` | +| `RequestQueueClient` | `_batchAddRequests()` | `addRequestBatch()` | +| `RequestQueueClient` | `_batchAddRequestsWithRetries()` | `addRequestBatchWithRetries()` | + +A helper got a suffix wherever the bare name would collide with a public method of the same class, which is why `_get()` is now `getResource()` and not `get()`. + +`LoggerActorRedirect` keeps `_log()`, since it overrides the method of that name on the `Logger` base class in `@apify/log`. + +The `clientMethod` field on `ApifyApiError` still names the public method you called, such as `ActorCollectionClient.list`, so what an error reports doesn't change. From 4ce90bfd6a871d358fbe18aaea2c92c0faa32a58 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 10 Sep 2026 19:24:02 +0200 Subject: [PATCH 4/6] docs: fix the new-client template in the contributing guide --- CONTRIBUTING.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 56be0efc4..1f2af85ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -306,6 +306,7 @@ When adding support for a new API resource: // src/resource_clients/my_resource.ts import { ApiClientSubResourceOptions } from '../base/api_client'; import { ResourceClient } from '../base/resource_client'; +import * as schemas from '../schemas'; export class MyResourceClient extends ResourceClient { constructor(options: ApiClientSubResourceOptions) { @@ -316,11 +317,11 @@ export class MyResourceClient extends ResourceClient { } async get(): Promise { - return this.getResource(); + return this.getResource(schemas.MyResource()); } async update(newFields: MyResourceUpdate): Promise { - return this.updateResource(newFields); + return this.updateResource(schemas.MyResource(), newFields); } async delete(): Promise { @@ -335,6 +336,7 @@ export class MyResourceClient extends ResourceClient { // src/resource_clients/my_resource_collection.ts import { ApiClientSubResourceOptions } from '../base/api_client'; import { ResourceCollectionClient } from '../base/resource_collection_client'; +import * as schemas from '../schemas'; import { PaginatedList } from '../utils'; export class MyResourceCollectionClient extends ResourceCollectionClient { @@ -346,11 +348,11 @@ export class MyResourceCollectionClient extends ResourceCollectionClient { } async list(options?: MyResourceListOptions): Promise> { - return this.listResources(options); + return this.listResources(schemas.ListOfMyResources(), options); } async create(resource: MyResourceCreate): Promise { - return this.createResource(resource); + return this.createResource(schemas.MyResource(), resource); } } ``` @@ -360,7 +362,7 @@ export class MyResourceCollectionClient extends ResourceCollectionClient { ```typescript // In src/apify_client.ts myResource(id: string): MyResourceClient { - return new MyResourceClient(this.subResourceOptions({ id })); + return new MyResourceClient({ id, ...this.subClientOptions() }); } myResources(): MyResourceCollectionClient { From cc9d4d96bbf59dc823763d8a2756706737f15976 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Sun, 13 Sep 2026 18:42:46 +0200 Subject: [PATCH 5/6] refactor: declare private members as #private identifiers --- CONTRIBUTING.md | 1 + docs/04_upgrading/upgrading_v3.md | 4 ++ src/apify_api_error.ts | 12 ++--- src/apify_client.ts | 48 +++++++++---------- src/http_client.ts | 65 +++++++++++++------------- src/resource_clients/dataset.ts | 4 +- src/resource_clients/log.ts | 62 ++++++++++++------------- src/resource_clients/request_queue.ts | 66 +++++++++++++-------------- src/statistics.ts | 4 +- src/utils.ts | 30 ++++++------ 10 files changed, 149 insertions(+), 147 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f2af85ce..18d70d0e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -159,6 +159,7 @@ This project uses [oxlint](https://oxc.rs/docs/guide/usage/linter) and [oxfmt](h - Use single quotes for strings - Add trailing commas in multiline structures - Export types and interfaces alongside implementations +- Declare private members as `#private` identifiers, and use `protected` only for helpers that subclasses call - Avoid `any` types where possible (though the oxlint rule is disabled) ### Before Committing diff --git a/docs/04_upgrading/upgrading_v3.md b/docs/04_upgrading/upgrading_v3.md index 2d3da73f1..5e8000c7d 100644 --- a/docs/04_upgrading/upgrading_v3.md +++ b/docs/04_upgrading/upgrading_v3.md @@ -397,3 +397,7 @@ A helper got a suffix wherever the bare name would collide with a public method `LoggerActorRedirect` keeps `_log()`, since it overrides the method of that name on the `Logger` base class in `@apify/log`. The `clientMethod` field on `ApifyApiError` still names the public method you called, such as `ActorCollectionClient.list`, so what an error reports doesn't change. + +### Private members are private at runtime + +Members that v2 declared `private` are declared with a `#` in v3, so the JavaScript runtime enforces the boundary, where v2 relied on the type checker. Code that reached one of them through a cast, such as `(client.httpClient as any).nodeInitPromise`, throws a `TypeError` in v3. They also don't appear when you spread an instance, iterate `Object.keys()` on it, or pass it to `JSON.stringify()`. The `protected` helpers in the table keep the `protected` keyword, so a subclass can call them. diff --git a/src/apify_api_error.ts b/src/apify_api_error.ts index 23403facd..a9ba97ff9 100644 --- a/src/apify_api_error.ts +++ b/src/apify_api_error.ts @@ -131,17 +131,17 @@ export class ApifyApiError extends Error { super(message); this.name = this.constructor.name; - this.clientMethod = this.extractClientAndMethodFromStack(); + this.clientMethod = this.#extractClientAndMethodFromStack(); this.statusCode = response.status; this.type = type; this.attempt = attempt; this.httpMethod = response.config?.method; - this.path = this.safelyParsePathFromResponse(response); + this.path = this.#safelyParsePathFromResponse(response); const stack = this.stack!; this.originalStack = stack.slice(stack.indexOf('\n')); - this.stack = this.createApiStack(); + this.stack = this.#createApiStack(); this.data = errorData; } @@ -156,7 +156,7 @@ export class ApifyApiError extends Error { return new ErrorClass(response, attempt); } - private safelyParsePathFromResponse(response: AxiosResponse) { + #safelyParsePathFromResponse(response: AxiosResponse) { const urlString = response.config?.url; let url; try { @@ -167,7 +167,7 @@ export class ApifyApiError extends Error { return url.pathname + url.search; } - private extractClientAndMethodFromStack() { + #extractClientAndMethodFromStack() { const match = this.stack!.match(CLIENT_METHOD_REGEX); if (!match) return 'unknown'; return `${match[2]}.${PUBLIC_METHOD_BY_HELPER[match[4]] ?? match[4]}`; @@ -187,7 +187,7 @@ export class ApifyApiError extends Error { * httpMethod: post * path: /v2/actor-tasks/user~my-task/runs */ - private createApiStack() { + #createApiStack() { const { name, ...props } = this; const stack = Object.entries(props) diff --git a/src/apify_client.ts b/src/apify_client.ts index 9b3240242..fe1b92842 100644 --- a/src/apify_client.ts +++ b/src/apify_client.ts @@ -136,7 +136,7 @@ export class ApifyClient { }); } - private subClientOptions() { + #subClientOptions() { return { baseUrl: this.baseUrl, publicBaseUrl: this.publicBaseUrl, @@ -154,7 +154,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/acts-get */ actors(): ActorCollectionClient { - return new ActorCollectionClient(this.subClientOptions()); + return new ActorCollectionClient(this.#subClientOptions()); } /** @@ -178,7 +178,7 @@ export class ApifyClient { return new ActorClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -191,7 +191,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/actor-builds-get */ builds(): BuildCollectionClient { - return new BuildCollectionClient(this.subClientOptions()); + return new BuildCollectionClient(this.#subClientOptions()); } /** @@ -208,7 +208,7 @@ export class ApifyClient { return new BuildClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -221,7 +221,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/datasets-get */ datasets(): DatasetCollectionClient { - return new DatasetCollectionClient(this.subClientOptions()); + return new DatasetCollectionClient(this.#subClientOptions()); } /** @@ -254,7 +254,7 @@ export class ApifyClient { return new DatasetClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -267,7 +267,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/key-value-stores-get */ keyValueStores(): KeyValueStoreCollectionClient { - return new KeyValueStoreCollectionClient(this.subClientOptions()); + return new KeyValueStoreCollectionClient(this.#subClientOptions()); } /** @@ -294,7 +294,7 @@ export class ApifyClient { return new KeyValueStoreClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -310,7 +310,7 @@ export class ApifyClient { return new LogClient({ id: buildOrRunId, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -323,7 +323,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/request-queues-get */ requestQueues(): RequestQueueCollectionClient { - return new RequestQueueCollectionClient(this.subClientOptions()); + return new RequestQueueCollectionClient(this.#subClientOptions()); } /** @@ -353,7 +353,7 @@ export class ApifyClient { const apiClientOptions = { id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }; return new RequestQueueClient(apiClientOptions, parsed); } @@ -368,7 +368,7 @@ export class ApifyClient { */ runs(): RunCollectionClient { return new RunCollectionClient({ - ...this.subClientOptions(), + ...this.#subClientOptions(), resourcePath: 'actor-runs', }); } @@ -397,7 +397,7 @@ export class ApifyClient { return new RunClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -410,7 +410,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/actor-tasks-get */ tasks(): TaskCollectionClient { - return new TaskCollectionClient(this.subClientOptions()); + return new TaskCollectionClient(this.#subClientOptions()); } /** @@ -433,7 +433,7 @@ export class ApifyClient { return new TaskClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -446,7 +446,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/schedules-get */ schedules(): ScheduleCollectionClient { - return new ScheduleCollectionClient(this.subClientOptions()); + return new ScheduleCollectionClient(this.#subClientOptions()); } /** @@ -463,7 +463,7 @@ export class ApifyClient { return new ScheduleClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -481,7 +481,7 @@ export class ApifyClient { return new UserClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -494,7 +494,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/webhooks-get */ webhooks(): WebhookCollectionClient { - return new WebhookCollectionClient(this.subClientOptions()); + return new WebhookCollectionClient(this.#subClientOptions()); } /** @@ -511,7 +511,7 @@ export class ApifyClient { return new WebhookClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -524,7 +524,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/webhook-dispatches-get */ webhookDispatches(): WebhookDispatchCollectionClient { - return new WebhookDispatchCollectionClient(this.subClientOptions()); + return new WebhookDispatchCollectionClient(this.#subClientOptions()); } /** @@ -539,7 +539,7 @@ export class ApifyClient { return new WebhookDispatchClient({ id, - ...this.subClientOptions(), + ...this.#subClientOptions(), }); } @@ -552,7 +552,7 @@ export class ApifyClient { * @see https://docs.apify.com/api/v2/store-get */ store(): StoreCollectionClient { - return new StoreCollectionClient(this.subClientOptions()); + return new StoreCollectionClient(this.#subClientOptions()); } /** diff --git a/src/http_client.ts b/src/http_client.ts index b032744ed..bcf2c0e87 100644 --- a/src/http_client.ts +++ b/src/http_client.ts @@ -46,9 +46,9 @@ export class HttpClient { workflowKey?: string; - private nodeInitPromise?: Promise; + #nodeInitPromise?: Promise; - private userAgentSuffix?: string | string[]; + #userAgentSuffix?: string | string[]; constructor(options: HttpClientOptions) { const { token } = options; @@ -64,8 +64,7 @@ export class HttpClient { this.timeoutMaxMillis = options.timeoutMaxSecs * 1000; this.logger = options.logger; this.workflowKey = options.workflowKey || process.env[APIFY_ENV_VARS.WORKFLOW_KEY]; - this.userAgentSuffix = options.userAgentSuffix; - this.onRequestRetry = this.onRequestRetry.bind(this); + this.#userAgentSuffix = options.userAgentSuffix; this.axios = axios.create({ // Disable axios's built-in proxy handling since we're using custom agents @@ -117,15 +116,15 @@ export class HttpClient { responseInterceptors.forEach((i) => this.axios.interceptors.response.use(i as any)); } - private async ensureNodeInit(): Promise { + async #ensureNodeInit(): Promise { if (!isNode()) return; - this.nodeInitPromise ??= this.initNode(); + this.#nodeInitPromise ??= this.#initNode(); - return this.nodeInitPromise; + return this.#nodeInitPromise; } - private async initNode(): Promise { + async #initNode(): Promise { if (!isNode()) return; const [{ ProxyAgent }, os] = await Promise.all([import('proxy-agent'), import('node:os')]); @@ -173,26 +172,26 @@ export class HttpClient { const isAtHome = !!process.env[APIFY_ENV_VARS.IS_AT_HOME]; let userAgent = `ApifyClient/${version} (${os.platform()}; Node/${process.version}); isAtHome/${isAtHome}`; - if (this.userAgentSuffix) { - userAgent += `; ${asArray(this.userAgentSuffix).join('; ')}`; + if (this.#userAgentSuffix) { + userAgent += `; ${asArray(this.#userAgentSuffix).join('; ')}`; } this.axios.defaults.headers['User-Agent'] = userAgent; } async call(config: ApifyRequestConfig): Promise> { - await this.ensureNodeInit(); + await this.#ensureNodeInit(); this.stats.calls++; - const makeRequest = this.createRequestHandler(config); + const makeRequest = this.#createRequestHandler(config); return retry(makeRequest, { retries: this.maxRetries, minTimeout: this.minDelayBetweenRetriesMillis, - onRetry: this.onRequestRetry, + onRetry: (error, attempt) => this.#onRequestRetry(error, attempt), }); } - private informAboutStreamNoRetry() { + #informAboutStreamNoRetry() { this.logger.warningOnce( 'Request body was a stream - retrying will not work, as part of it was already consumed.', ); @@ -206,7 +205,7 @@ export class HttpClient { * status codes are retried. See the following functions for the * retrying logic. */ - private createRequestHandler(config: ApifyRequestConfig) { + #createRequestHandler(config: ApifyRequestConfig) { const { timeoutSecs = 'medium', ...axiosConfig } = config; const makeRequest: RetryFunction = async (stopTrying, attempt) => { @@ -224,11 +223,11 @@ export class HttpClient { response = await this.axios.request({ ...axiosConfig, - timeout: this.computeTimeoutMillis(timeoutSecs, attempt), + timeout: this.#computeTimeoutMillis(timeoutSecs, attempt), }); - if (this.isStatusOk(response.status)) return response; + if (this.#isStatusOk(response.status)) return response; } catch (err) { - return cast(this.handleRequestError(err as AxiosError, config, stopTrying)); + return cast(this.#handleRequestError(err as AxiosError, config, stopTrying)); } if (response.status === RATE_LIMIT_EXCEEDED_STATUS_CODE) { @@ -236,9 +235,9 @@ export class HttpClient { } const apiError = ApifyApiError.fromResponse(response, attempt); - if (this.isStatusCodeRetryable(response.status)) { + if (this.#isStatusCodeRetryable(response.status)) { if (requestIsStream) { - this.informAboutStreamNoRetry(); + this.#informAboutStreamNoRetry(); } else { // allow a retry throw apiError; @@ -252,7 +251,7 @@ export class HttpClient { return makeRequest; } - private isStatusOk(statusCode: number) { + #isStatusOk(statusCode: number) { return statusCode < 300; } @@ -262,7 +261,7 @@ export class HttpClient { * as no timeout. The result doubles with each attempt and is capped at `timeoutMaxMillis`. A requested * value above the cap is capped too, which warns once, since it does not take effect in full. */ - private computeTimeoutMillis(timeoutSecs: Timeout, attempt: number): number { + #computeTimeoutMillis(timeoutSecs: Timeout, attempt: number): number { if (timeoutSecs === 'noTimeout') return 0; const requestedMillis = typeof timeoutSecs === 'number' ? timeoutSecs * 1000 : this.timeoutMillis[timeoutSecs]; @@ -283,14 +282,14 @@ export class HttpClient { * Handles all unexpected errors that can happen, but are not * Apify API typed errors. E.g. network errors, timeouts and so on. */ - private handleRequestError(err: AxiosError, config: ApifyRequestConfig, stopTrying: (e: Error) => void) { - if (this.isTimeoutError(err) && config.doNotRetryTimeouts) { + #handleRequestError(err: AxiosError, config: ApifyRequestConfig, stopTrying: (e: Error) => void) { + if (this.#isTimeoutError(err) && config.doNotRetryTimeouts) { return stopTrying(err); } - if (this.isRetryableError(err)) { + if (this.#isRetryableError(err)) { if (isStream(config.data)) { - this.informAboutStreamNoRetry(); + this.#informAboutStreamNoRetry(); } else { throw err; } @@ -302,7 +301,7 @@ export class HttpClient { * Axios calls req.abort() on timeouts so timeout errors will * have a code ECONNABORTED. */ - private isTimeoutError(err: AxiosError) { + #isTimeoutError(err: AxiosError) { return err.code === 'ECONNABORTED'; } @@ -312,8 +311,8 @@ export class HttpClient { * @param {Error} err * @private */ - private isRetryableError(err: AxiosError) { - return this.isNetworkError(err) || this.isResponseBodyInvalid(err); + #isRetryableError(err: AxiosError) { + return this.#isNetworkError(err) || this.#isResponseBodyInvalid(err); } /** @@ -321,7 +320,7 @@ export class HttpClient { * a response, the request often does not fail, but simply contains * an incomplete response. This can often be fixed by retrying. */ - private isResponseBodyInvalid(err: Error): err is InvalidResponseBodyError { + #isResponseBodyInvalid(err: Error): err is InvalidResponseBodyError { return err instanceof InvalidResponseBodyError; } @@ -330,7 +329,7 @@ export class HttpClient { * it throws an AxiosError, which will have the request * and config (and other) properties. */ - private isNetworkError(err: AxiosError) { + #isNetworkError(err: AxiosError) { const hasRequest = err.request && typeof err.request === 'object'; const hasConfig = err.config && typeof err.config === 'object'; return hasRequest && hasConfig; @@ -341,13 +340,13 @@ export class HttpClient { * For status codes 300-499 (except 429) we do not retry the request, * because it's probably caused by invalid url (redirect 3xx) or invalid user input (4xx). */ - private isStatusCodeRetryable(statusCode: number) { + #isStatusCodeRetryable(statusCode: number) { const isRateLimitError = statusCode === RATE_LIMIT_EXCEEDED_STATUS_CODE; const isInternalError = statusCode >= 500; return isRateLimitError || isInternalError; } - private onRequestRetry(error: Error, attempt: number) { + #onRequestRetry(error: Error, attempt: number) { if (attempt === Math.round(this.maxRetries / 2)) { this.logger.warning( `API request failed ${attempt} times. Max attempts: ${this.maxRetries + 1}.\nCause:${error.stack}`, diff --git a/src/resource_clients/dataset.ts b/src/resource_clients/dataset.ts index a38121178..c3528afbc 100644 --- a/src/resource_clients/dataset.ts +++ b/src/resource_clients/dataset.ts @@ -230,7 +230,7 @@ export class DatasetClient< timeoutSecs, }); - return this.createPaginationList(response, datasetListOptions.desc ?? false); + return this.#createPaginationList(response, datasetListOptions.desc ?? false); }; return this.listPaginatedFromCallback(fetchItems, listOptions); @@ -435,7 +435,7 @@ export class DatasetClient< return createdItemsPublicUrl.toString(); } - private createPaginationList(response: ApifyResponse, userProvidedDesc: boolean): PaginatedList { + #createPaginationList(response: ApifyResponse, userProvidedDesc: boolean): PaginatedList { const page: PaginatedList = { items: response.data, total: Number(response.headers['x-apify-pagination-total']), diff --git a/src/resource_clients/log.ts b/src/resource_clients/log.ts index bb8d1b94a..e523014c0 100644 --- a/src/resource_clients/log.ts +++ b/src/resource_clients/log.ts @@ -163,73 +163,73 @@ export class LoggerActorRedirect extends Logger { * @since Added in 2.20.0 */ export class StreamedLog { - private destinationLog: Log; - private streamBuffer: Buffer[] = []; - private splitMarker = /(?:\n|^)(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)/g; - private relevancyTimeLimit: Date | null; + #destinationLog: Log; + #streamBuffer: Buffer[] = []; + #splitMarker = /(?:\n|^)(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)/g; + #relevancyTimeLimit: Date | null; - private logClient: LogClient; - private streamingTask: Promise | null = null; - private stopLogging = false; + #logClient: LogClient; + #streamingTask: Promise | null = null; + #stopLogging = false; constructor(options: StreamedLogOptions) { const { toLog, logClient, fromStart = true } = options; - this.destinationLog = toLog; - this.logClient = logClient; - this.relevancyTimeLimit = fromStart ? null : new Date(); + this.#destinationLog = toLog; + this.#logClient = logClient; + this.#relevancyTimeLimit = fromStart ? null : new Date(); } /** * Start log redirection. */ public start(): void { - if (this.streamingTask) { + if (this.#streamingTask) { throw new Error('Streaming task already active'); } - this.stopLogging = false; - this.streamingTask = this.streamLog(); + this.#stopLogging = false; + this.#streamingTask = this.#streamLog(); } /** * Stop log redirection. */ public async stop(): Promise { - if (!this.streamingTask) { + if (!this.#streamingTask) { throw new Error('Streaming task is not active'); } - this.stopLogging = true; + this.#stopLogging = true; try { - await this.streamingTask; + await this.#streamingTask; } catch (err) { if (!(err instanceof Error && err.name === 'AbortError')) { throw err; } } finally { - this.streamingTask = null; + this.#streamingTask = null; } } /** * Get log stream from response and redirect it to another log. */ - private async streamLog(): Promise { + async #streamLog(): Promise { try { - const logStream = await this.logClient.stream({ raw: true }); + const logStream = await this.#logClient.stream({ raw: true }); if (!logStream) { return; } - const lastChunkRemainder = await this.logStreamChunks(logStream); + const lastChunkRemainder = await this.#logStreamChunks(logStream); // Process whatever is left when exiting. Maybe it is incomplete, maybe it is last log without EOL. const lastMessage = Buffer.from(lastChunkRemainder).toString().trim(); if (lastMessage.length) { - this.destinationLog.info(lastMessage); + this.#destinationLog.info(lastMessage); } } catch (err) { log.warning(`Log redirection stopped due to error`, err as Error); } } - private async logStreamChunks(logStream: Readable): Promise { + async #logStreamChunks(logStream: Readable): Promise { // Chunk may be incomplete. Keep remainder for next chunk. let previousChunkRemainder: Uint8Array = new Uint8Array(); @@ -244,11 +244,11 @@ export class StreamedLog { previousChunkRemainder = chunkWithPreviousRemainder.slice(lastCompleteMessageIndex); // Push complete part of the chunk to the buffer - this.streamBuffer.push(Buffer.from(chunkWithPreviousRemainder.slice(0, lastCompleteMessageIndex))); - this.logBufferContent(); + this.#streamBuffer.push(Buffer.from(chunkWithPreviousRemainder.slice(0, lastCompleteMessageIndex))); + this.#logBufferContent(); // Keep processing the new data until stopped - if (this.stopLogging) { + if (this.#stopLogging) { break; } } @@ -258,20 +258,20 @@ export class StreamedLog { /** * Parse the buffer and log complete messages. */ - private logBufferContent(): void { - const allParts = Buffer.concat(this.streamBuffer).toString().split(this.splitMarker).slice(1); + #logBufferContent(): void { + const allParts = Buffer.concat(this.#streamBuffer).toString().split(this.#splitMarker).slice(1); // Parse the buffer parts into complete messages const messageMarkers = allParts.filter((_, i) => i % 2 === 0); const messageContents = allParts.filter((_, i) => i % 2 !== 0); - this.streamBuffer = []; + this.#streamBuffer = []; messageMarkers.forEach((marker, index) => { const decodedMarker = marker; const decodedContent = messageContents[index]; - if (this.relevancyTimeLimit) { + if (this.#relevancyTimeLimit) { // Log only relevant messages. Ignore too old log messages. const logTime = new Date(decodedMarker); - if (logTime < this.relevancyTimeLimit) { + if (logTime < this.#relevancyTimeLimit) { return; } } @@ -279,7 +279,7 @@ export class StreamedLog { // Original log level information is not available. Log all on info level. Log level could be guessed for // some logs, but for any multiline logs such guess would be probably correct only for the first line. - this.destinationLog.info(message.trim()); + this.#destinationLog.info(message.trim()); }); } } diff --git a/src/resource_clients/request_queue.ts b/src/resource_clients/request_queue.ts index 54e06ac69..78c5f74c9 100644 --- a/src/resource_clients/request_queue.ts +++ b/src/resource_clients/request_queue.ts @@ -145,9 +145,9 @@ export type { * @see https://docs.apify.com/platform/storage/request-queue */ export class RequestQueueClient extends ResourceClient { - private clientKey?: string; + #clientKey?: string; - private timeoutSecs?: number; + #timeoutSecs?: number; /** * @hidden @@ -158,19 +158,19 @@ export class RequestQueueClient extends ResourceClient { ...options, }); - this.clientKey = userOptions.clientKey; - this.timeoutSecs = userOptions.timeoutSecs; + this.#clientKey = userOptions.clientKey; + this.#timeoutSecs = userOptions.timeoutSecs; } /** * Picks the timeout of a request: an explicit per-call `timeoutSecs` as is, otherwise the method's default * tier, capped at the queue-wide `timeoutSecs` when one was given. */ - private resolveTimeout(timeoutSecs: Timeout | undefined, defaultTier: TimeoutTier): Timeout { + #resolveTimeout(timeoutSecs: Timeout | undefined, defaultTier: TimeoutTier): Timeout { if (timeoutSecs !== undefined) return timeoutSecs; - if (this.timeoutSecs === undefined) return defaultTier; + if (this.#timeoutSecs === undefined) return defaultTier; const tierSecs = this.httpClient.timeoutMillis[defaultTier] / 1000; - return tierSecs <= this.timeoutSecs ? defaultTier : this.timeoutSecs; + return tierSecs <= this.#timeoutSecs ? defaultTier : this.#timeoutSecs; } /** @@ -184,7 +184,7 @@ export class RequestQueueClient extends ResourceClient { async get(options: TimeoutOptions = {}): Promise { const { timeoutSecs } = parseArgument(options, timeoutOptionsSchema, 'TimeoutOptions'); - return this.getResource(schemas.RequestQueue(), {}, this.resolveTimeout(timeoutSecs, 'short')); + return this.getResource(schemas.RequestQueue(), {}, this.#resolveTimeout(timeoutSecs, 'short')); } /** @@ -200,7 +200,7 @@ export class RequestQueueClient extends ResourceClient { parseArgument(newFields, anyObjectSchema); const { timeoutSecs } = parseArgument(options, timeoutOptionsSchema, 'TimeoutOptions'); - return this.updateResource(schemas.RequestQueue(), newFields, this.resolveTimeout(timeoutSecs, 'short')); + return this.updateResource(schemas.RequestQueue(), newFields, this.#resolveTimeout(timeoutSecs, 'short')); } /** @@ -213,7 +213,7 @@ export class RequestQueueClient extends ResourceClient { async delete(options: TimeoutOptions = {}): Promise { const { timeoutSecs } = parseArgument(options, timeoutOptionsSchema, 'TimeoutOptions'); - return this.deleteResource(this.resolveTimeout(timeoutSecs, 'short')); + return this.deleteResource(this.#resolveTimeout(timeoutSecs, 'short')); } /** @@ -234,10 +234,10 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl('head'), method: 'GET', - timeoutSecs: this.resolveTimeout(parsed.timeoutSecs, 'short'), + timeoutSecs: this.#resolveTimeout(parsed.timeoutSecs, 'short'), params: this.buildParams({ limit: parsed.limit, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -286,11 +286,11 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl('head/lock'), method: 'POST', - timeoutSecs: this.resolveTimeout(parsed.timeoutSecs, 'medium'), + timeoutSecs: this.#resolveTimeout(parsed.timeoutSecs, 'medium'), params: this.buildParams({ limit: parsed.limit, lockSecs: parsed.lockSecs, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -346,11 +346,11 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl('requests'), method: 'POST', - timeoutSecs: this.resolveTimeout(parsed.timeoutSecs, 'short'), + timeoutSecs: this.#resolveTimeout(parsed.timeoutSecs, 'short'), data: request, params: this.buildParams({ forefront: parsed.forefront, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -372,11 +372,11 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl('requests/batch'), method: 'POST', - timeoutSecs: this.resolveTimeout(parsed.timeoutSecs, 'medium'), + timeoutSecs: this.#resolveTimeout(parsed.timeoutSecs, 'medium'), data: requests, params: this.buildParams({ forefront: parsed.forefront, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -575,10 +575,10 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl('requests/batch'), method: 'DELETE', - timeoutSecs: this.resolveTimeout(timeoutSecs, 'short'), + timeoutSecs: this.#resolveTimeout(timeoutSecs, 'short'), data: requests, params: this.buildParams({ - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -604,7 +604,7 @@ export class RequestQueueClient extends ResourceClient { const requestOpts: ApifyRequestConfig = { url: this.buildUrl(['requests', id]), method: 'GET', - timeoutSecs: this.resolveTimeout(timeoutSecs, 'short'), + timeoutSecs: this.#resolveTimeout(timeoutSecs, 'short'), params: this.buildParams(), }; try { @@ -637,11 +637,11 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl(['requests', request.id]), method: 'PUT', - timeoutSecs: this.resolveTimeout(parsed.timeoutSecs, 'medium'), + timeoutSecs: this.#resolveTimeout(parsed.timeoutSecs, 'medium'), data: request, params: this.buildParams({ forefront: parsed.forefront, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -662,9 +662,9 @@ export class RequestQueueClient extends ResourceClient { await this.httpClient.call({ url: this.buildUrl(['requests', id]), method: 'DELETE', - timeoutSecs: this.resolveTimeout(timeoutSecs, 'short'), + timeoutSecs: this.#resolveTimeout(timeoutSecs, 'short'), params: this.buildParams({ - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); } @@ -709,11 +709,11 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl(['requests', id, 'lock']), method: 'PUT', - timeoutSecs: this.resolveTimeout(parsed.timeoutSecs, 'medium'), + timeoutSecs: this.#resolveTimeout(parsed.timeoutSecs, 'medium'), params: this.buildParams({ forefront: parsed.forefront, lockSecs: parsed.lockSecs, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -740,10 +740,10 @@ export class RequestQueueClient extends ResourceClient { await this.httpClient.call({ url: this.buildUrl(['requests', id, 'lock']), method: 'DELETE', - timeoutSecs: this.resolveTimeout(parsed.timeoutSecs, 'short'), + timeoutSecs: this.#resolveTimeout(parsed.timeoutSecs, 'short'), params: this.buildParams({ forefront: parsed.forefront, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); } @@ -769,7 +769,7 @@ export class RequestQueueClient extends ResourceClient { listRequestsOptionsSchema, 'RequestQueueClientListRequestsOptions', ); - const pageTimeout = this.resolveTimeout(timeoutSecs, 'medium'); + const pageTimeout = this.#resolveTimeout(timeoutSecs, 'medium'); const getPaginatedList = async ( rqListOptions: RequestQueueClientListRequestsOptions = {}, @@ -781,7 +781,7 @@ export class RequestQueueClient extends ResourceClient { params: this.buildParams({ ...rqListOptions, filter: rqListOptions.filter ? rqListOptions.filter.join(',') : undefined, - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); @@ -838,9 +838,9 @@ export class RequestQueueClient extends ResourceClient { const response = await this.httpClient.call({ url: this.buildUrl('requests/unlock'), method: 'POST', - timeoutSecs: this.resolveTimeout(timeoutSecs, 'long'), + timeoutSecs: this.#resolveTimeout(timeoutSecs, 'long'), params: this.buildParams({ - clientKey: this.clientKey, + clientKey: this.#clientKey, }), }); diff --git a/src/statistics.ts b/src/statistics.ts index 32264258a..d5c7a00c5 100644 --- a/src/statistics.ts +++ b/src/statistics.ts @@ -26,7 +26,7 @@ export class Statistics { // attempt is never 0, // but we don't want index 0 empty const index = attempt - 1; - this.fillBlanksWithZeroes(index); + this.#fillBlanksWithZeroes(index); this.rateLimitErrors[index]++; } @@ -35,7 +35,7 @@ export class Statistics { * number of zeroes by dynamically filling the empty indexes * when necessary. */ - private fillBlanksWithZeroes(inclusiveIndex: number) { + #fillBlanksWithZeroes(inclusiveIndex: number) { if (this.rateLimitErrors.length <= inclusiveIndex) { for (let k = 0; k <= inclusiveIndex; k++) { if (typeof this.rateLimitErrors[k] !== 'number') { diff --git a/src/utils.ts b/src/utils.ts index 25e623edb..4b6abdc26 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -374,32 +374,30 @@ export function getVersionData(): { version: string } { * Helper class to create async iterators from paginated list endpoints. */ export class RequestQueuePaginationIterator { - private readonly maxPageLimit: number; + readonly #maxPageLimit: number; - private readonly getPage: ( - opts: RequestQueueClientListRequestsOptions, - ) => Promise; + readonly #getPage: (opts: RequestQueueClientListRequestsOptions) => Promise; - private readonly limit?: number; + readonly #limit?: number; - private readonly cursor?: string; + readonly #cursor?: string; constructor(options: RequestQueuePaginationIteratorOptions) { - this.maxPageLimit = options.maxPageLimit; - this.limit = options.limit; - this.cursor = options.cursor; - this.getPage = options.getPage; + this.#maxPageLimit = options.maxPageLimit; + this.#limit = options.limit; + this.#cursor = options.cursor; + this.#getPage = options.getPage; } async *[Symbol.asyncIterator](): AsyncIterator { - let nextCursor = this.cursor; + let nextCursor = this.#cursor; let iterateItemCount = 0; while (true) { - const pageLimit = this.limit - ? Math.min(this.maxPageLimit, this.limit - iterateItemCount) - : this.maxPageLimit; + const pageLimit = this.#limit + ? Math.min(this.#maxPageLimit, this.#limit - iterateItemCount) + : this.#maxPageLimit; - const page: RequestQueueClientListRequestsResult = await this.getPage({ + const page: RequestQueueClientListRequestsResult = await this.#getPage({ limit: pageLimit, cursor: nextCursor, }); @@ -408,7 +406,7 @@ export class RequestQueuePaginationIterator { yield page; iterateItemCount += page.items.length; // Limit reached stopping to iterate - if ((this.limit && iterateItemCount >= this.limit) || !page.nextCursor) return; + if ((this.#limit && iterateItemCount >= this.#limit) || !page.nextCursor) return; nextCursor = page.nextCursor; } From 4535755de62b9da79cf424bf71987abc89a90004 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Mon, 14 Sep 2026 09:16:46 +0200 Subject: [PATCH 6/6] refactor!: let ApifyApiError.clientMethod name the shared helper --- docs/04_upgrading/upgrading_v3.md | 2 +- src/apify_api_error.ts | 24 ++--------- test/apify_api_error.test.ts | 67 ++----------------------------- 3 files changed, 8 insertions(+), 85 deletions(-) diff --git a/docs/04_upgrading/upgrading_v3.md b/docs/04_upgrading/upgrading_v3.md index 5e8000c7d..2195ca48e 100644 --- a/docs/04_upgrading/upgrading_v3.md +++ b/docs/04_upgrading/upgrading_v3.md @@ -396,7 +396,7 @@ A helper got a suffix wherever the bare name would collide with a public method `LoggerActorRedirect` keeps `_log()`, since it overrides the method of that name on the `Logger` base class in `@apify/log`. -The `clientMethod` field on `ApifyApiError` still names the public method you called, such as `ActorCollectionClient.list`, so what an error reports doesn't change. +The `clientMethod` field on `ApifyApiError` is parsed from the stack trace, and a public method that delegates to one of these helpers is reported under the helper's name. An error from `client.actor(id).get()` says `ActorClient.getResource`, where v2 said `ActorClient.get`. Adjust anything that matches on these values in your logs. ### Private members are private at runtime diff --git a/src/apify_api_error.ts b/src/apify_api_error.ts index a9ba97ff9..b0c3bae0c 100644 --- a/src/apify_api_error.ts +++ b/src/apify_api_error.ts @@ -18,23 +18,6 @@ export type { ApifyApiErrorType } from './models.js'; */ const CLIENT_METHOD_REGEX = /at( async)? ([A-Za-z]+(Collection)?Client)\.([A-Za-z]+) \(/; -/** - * A public method that returns the promise of a shared helper without awaiting it leaves only the helper on - * the stack, so the helper is reported under the method the caller invoked. - * @private - */ -const PUBLIC_METHOD_BY_HELPER: Record = { - getResource: 'get', - updateResource: 'update', - deleteResource: 'delete', - waitForJobFinish: 'waitForFinish', - listResources: 'list', - createResource: 'create', - getOrCreateResource: 'getOrCreate', - addRequestBatch: 'batchAddRequests', - addRequestBatchWithRetries: 'batchAddRequests', -}; - /** * An `ApifyApiError` is thrown for successful HTTP requests that reach the API, * but the API responds with an error response. Typically, those are rate limit @@ -53,8 +36,9 @@ export class ApifyApiError extends Error { override name: string; /** - * The invoked resource client and the method. Known issue: Sometimes it displays - * as `unknown` because it can't be parsed from a stack trace. + * The resource client and the method parsed from the stack trace, such as `ActorClient.getResource`. A public + * method that delegates to a shared helper of its class is reported under the helper. The value is `unknown` + * when the stack trace cannot be parsed. */ clientMethod: string; @@ -170,7 +154,7 @@ export class ApifyApiError extends Error { #extractClientAndMethodFromStack() { const match = this.stack!.match(CLIENT_METHOD_REGEX); if (!match) return 'unknown'; - return `${match[2]}.${PUBLIC_METHOD_BY_HELPER[match[4]] ?? match[4]}`; + return `${match[2]}.${match[4]}`; } /** diff --git a/test/apify_api_error.test.ts b/test/apify_api_error.test.ts index 8f7acc940..bbaf85460 100644 --- a/test/apify_api_error.test.ts +++ b/test/apify_api_error.test.ts @@ -46,7 +46,8 @@ describe('ApifyApiError', () => { // This does not work in v10 and lower, but we want to be able to run tests for v10, // because some people might still use it. They will just see clientMethod: undefined. if (!process.version.startsWith('v10')) { - expect(err.clientMethod).toBe(`${actorCollectionClient.constructor.name}.${method}`); + // `list()` returns the promise of the `listResources` helper, so the helper is the frame on the stack. + expect(err.clientMethod).toBe(`${actorCollectionClient.constructor.name}.listResources`); } expect(err.type).toEqual('token-not-provided'); expect(err.message).toEqual('Authentication token was not provided'); @@ -77,7 +78,7 @@ describe('ApifyApiError', () => { }, method); expect(error.name).toEqual('UnauthorizedError'); - expect(error.clientMethod).toBe(`ActorCollectionClient.${method}`); + expect(error.clientMethod).toBe('ActorCollectionClient.listResources'); expect(error.type).toEqual('token-not-provided'); expect(error.message).toEqual('Authentication token was not provided'); expect(error.statusCode).toEqual(401); @@ -144,68 +145,6 @@ describe('ApifyApiError', () => { }); }); - describe('clientMethod names the public method, not the shared helper', () => { - // `400` as the resource id is what makes the mock server answer with an API error. - const cases: { helper: string; expected: string; call: (client: ApifyClient) => Promise }[] = [ - { helper: 'getResource', expected: 'ActorClient.get', call: (client) => client.actor('400').get() }, - { - helper: 'updateResource', - expected: 'ActorClient.update', - call: (client) => client.actor('400').update({ isPublic: false }), - }, - { - helper: 'deleteResource', - expected: 'ActorClient.delete', - call: (client) => client.actor('400').delete(), - }, - { - helper: 'listResources', - expected: 'ActorVersionCollectionClient.list', - call: (client) => client.actor('400').versions().list(), - }, - { - helper: 'createResource', - expected: 'ActorVersionCollectionClient.create', - call: (client) => - client.actor('400').versions().create({ - versionNumber: '0.0', - sourceType: 'GIT_REPO', - gitRepoUrl: 'https://github.com/user/repo.git', - }), - }, - { - helper: 'waitForJobFinish', - expected: 'RunClient.waitForFinish', - call: (client) => client.run('400').waitForFinish(), - }, - ]; - - test.each(cases)('$helper reports $expected', async ({ expected, call }) => { - const client = new ApifyClient({ baseUrl, maxRetries: 0, ...DEFAULT_OPTIONS }); - - await expect(call(client)).rejects.toMatchObject({ clientMethod: expected }); - }); - - test('addRequestBatch reports RequestQueueClient.batchAddRequests', async () => { - const client = new ApifyClient({ baseUrl, maxRetries: 0, ...DEFAULT_OPTIONS }); - // `batchAddRequests()` answers with unprocessed requests instead of throwing, so the helper is - // called directly to reach the error it builds. - const queue = client.requestQueue('some-queue-id') as any; - mockServer.setResponse({ - statusCode: 400, - body: { error: { type: 'some-type', message: 'Some message' } }, - }); - - try { - await expect( - queue.addRequestBatch([{ url: 'http://example.com', uniqueKey: 'some-key', method: 'GET' }]), - ).rejects.toMatchObject({ clientMethod: 'RequestQueueClient.batchAddRequests' }); - } finally { - mockServer.setResponse(null); - } - }); - }); - describe('subclass by HTTP status', () => { const response = (status: number, data: unknown = { error: { type: 'some-type', message: 'Some message' } }) => ({ status, data, config: { method: 'get', url: 'http://localhost/v2/acts' } }) as any;