diff --git a/src/actor.ts b/src/actor.ts index f7f4b097ff..8fcdc15c75 100644 --- a/src/actor.ts +++ b/src/actor.ts @@ -1497,7 +1497,12 @@ export class Actor { ...storageClientOptions, ...options, // allow overriding the instance configuration }, - this, + { + config: this.config, + // Hand over the raw manager rather than the public getChargingManager(), which + // warns about a missing init() — a standalone client is a supported use case. + getChargingManager: () => this.chargingManager, + }, ); } diff --git a/src/charging.ts b/src/charging.ts index 84d3d28ebb..80090ba3a8 100644 --- a/src/charging.ts +++ b/src/charging.ts @@ -235,6 +235,14 @@ export class ChargingManager { return dataset.id; } + /** + * Whether {@link ChargingManager.init} has run. All charging operations (including + * {@link ChargingManager.getPricingInfo}) require an initialized manager. + */ + get isInitialized(): boolean { + return this.chargingState !== undefined; + } + /** * Get information about the pricing for this Actor. */ diff --git a/src/patched_apify_client.ts b/src/patched_apify_client.ts index 3f0193aaed..2c31bd27d4 100644 --- a/src/patched_apify_client.ts +++ b/src/patched_apify_client.ts @@ -96,8 +96,13 @@ export function createPatchedApifyClient( httpClient: this.httpClient, }; + // Charging is only configured for an initialized Actor (a platform run). A + // standalone client from `Actor.newClient()` without `Actor.init()` has no + // charging state, so skip interception rather than letting getPricingInfo() throw. + const chargingManager = actor.getChargingManager(); const hasDefaultDatasetItemEvent = - DEFAULT_DATASET_ITEM_EVENT in actor.getChargingManager().getPricingInfo().perEventPrices; + chargingManager.isInitialized && + DEFAULT_DATASET_ITEM_EVENT in chargingManager.getPricingInfo().perEventPrices; if (!isDefaultDataset || !hasDefaultDatasetItemEvent) { return new DatasetClient(datasetOptions); diff --git a/test/apify/charging.test.ts b/test/apify/charging.test.ts index 83ed675c67..23a4732b72 100644 --- a/test/apify/charging.test.ts +++ b/test/apify/charging.test.ts @@ -788,3 +788,24 @@ describe('ChargingManager', () => { }); }); }); + +describe('Actor.newClient()', () => { + afterEach(() => { + // @ts-expect-error reset the singleton between tests + Actor._instance = undefined; // eslint-disable-line no-underscore-dangle + }); + + test('dataset() works without calling Actor.init()', () => { + // `Actor.newClient()` is documented as usable standalone (init() is only required on + // the platform). The charging-aware dataset() getter must not require an initialized + // ChargingManager, otherwise it throws "ChargingManager is not initialized". + const warningSpy = vitest.spyOn(log, 'warning'); + const client = Actor.newClient({ token: 'test-token' }); + + expect(() => client.dataset('some-dataset-id')).not.toThrow(); + // ...and it must not nag about a missing init() on this supported path. + expect(warningSpy).not.toHaveBeenCalledWith(expect.stringContaining('Actor.init()')); + + warningSpy.mockRestore(); + }); +});