From f46c7e63742ea32d57949f74d4971b2f31d5a7e4 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 1 Oct 2026 12:40:00 +0200 Subject: [PATCH 1/4] feat!: type Actor input as plain objects, arrays of them, or raw bytes BREAKING CHANGE: `ActorInput` is `Dictionary | Dictionary[] | Uint8Array`. Input declared as an `interface`, and values such as `Date`, `Map`, `Set` or functions, no longer compile. --- docs/04_upgrading/upgrading_to_v3.mdx | 12 +++++++++++- docs/public-api/apify-client.api.md | 2 +- src/resource_clients/actor.ts | 8 +++++--- test/actors.test.ts | 24 ++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/docs/04_upgrading/upgrading_to_v3.mdx b/docs/04_upgrading/upgrading_to_v3.mdx index dcaaa0d72..cd70168b7 100644 --- a/docs/04_upgrading/upgrading_to_v3.mdx +++ b/docs/04_upgrading/upgrading_to_v3.mdx @@ -391,13 +391,23 @@ Two options that carried a `@deprecated` marker throughout v2 have been removed. `ActorClient.start()`, `call()`, `validateInput()` and `RunClient.metamorph()` took their `input` as `unknown`, so any value compiled, including ones the client cannot send. -The input is now typed `ActorInput`, an alias for `object`, so it's an object or an array that the client serializes into the request body. Any other value stops compiling, including a value typed `unknown`, which has to be narrowed or cast first. To run an Actor without input, omit the argument or pass `undefined`. +The input is now typed `ActorInput`, which accepts a plain object, an array of plain objects, or raw bytes as a `Uint8Array` such as a `Buffer`. The client serializes objects and arrays into the request body and sends bytes as they are. Any other value stops compiling, including a `Date`, `Map`, `Set` or function, and a value typed `unknown`, which has to be narrowed or cast first. To run an Actor without input, omit the argument or pass `undefined`. ```diff - await client.actor('my-actor').call(null, { memory: 1024 }); // v2 + await client.actor('my-actor').call(undefined, { memory: 1024 }); // v3 ``` +An input declared as an `interface` stops compiling as well, since `ActorInput` needs an index signature and interfaces don't get one implicitly. Declare the input type as a `type` alias, or spread the value into a new object: + +```diff +- interface MyInput { url: string } // v2 ++ type MyInput = { url: string }; // v3 + +- await client.actor('my-actor').call(input); // v2, with an interface-typed `input` ++ await client.actor('my-actor').call({ ...input }); // v3 +``` + A raw `string` body stops compiling too, with or without a `contentType`. Pass the input as an object and let the client serialize it: ```diff diff --git a/docs/public-api/apify-client.api.md b/docs/public-api/apify-client.api.md index bf4c82a5f..b851499e7 100644 --- a/docs/public-api/apify-client.api.md +++ b/docs/public-api/apify-client.api.md @@ -201,7 +201,7 @@ export interface ActorExampleRunInput extends GeneratedExampleRunInput { } // @public -export type ActorInput = object; +export type ActorInput = Dictionary | Dictionary[] | Uint8Array; // @public export interface ActorLastRunOptions { diff --git a/src/resource_clients/actor.ts b/src/resource_clients/actor.ts index e67f7877b..47801eeae 100644 --- a/src/resource_clients/actor.ts +++ b/src/resource_clients/actor.ts @@ -8,6 +8,7 @@ import { ResourceClient } from '../base/resource_client.js'; import type { ApifyRequestConfig } from '../http_clients/index.js'; import type { Actor, ActorRun } from '../models.js'; import type { TimeoutOptions } from '../timeouts.js'; +import type { Dictionary } from '../utils.js'; import * as schemas from '../schemas.js'; import { timeoutOptionsSchema, timeoutOptionsShape } from '../timeouts.js'; import { anyObjectSchema, parseArgument, parseResponse, stringifyWebhooksToBase64 } from '../utils.js'; @@ -588,10 +589,11 @@ export type ActorUpdateOptions = Partial< * Input for an Actor run, as taken by {@link ActorClient.start}, {@link ActorClient.call}, * {@link ActorClient.validateInput} and {@link RunClient.metamorph}. * - * An object or an array. Declared as `object` rather than an index-signature type such as - * `Dictionary`, which would reject a caller's own `interface`. + * A plain object or an array of plain objects, serialized into the request body, or raw bytes sent + * as they are, typically with a `contentType`. An input typed as an `interface` needs a `type` alias + * or a spread (`{ ...input }`) to compile. */ -export type ActorInput = object; +export type ActorInput = Dictionary | Dictionary[] | Uint8Array; export interface ActorStartOptions extends TimeoutOptions { /** diff --git a/test/actors.test.ts b/test/actors.test.ts index 7b251808a..752e96d01 100644 --- a/test/actors.test.ts +++ b/test/actors.test.ts @@ -3,7 +3,7 @@ import type { AddressInfo } from 'node:net'; import { setTimeout } from 'node:timers/promises'; import c from 'ansi-colors'; -import type { ActorCollectionCreateOptions, ActorCollectionListOptions, ActorVersion } from 'apify-client'; +import type { ActorCollectionCreateOptions, ActorCollectionListOptions, ActorInput, ActorVersion } from 'apify-client'; import { ActorListSortBy, ActorSourceType, @@ -14,7 +14,7 @@ import { } from 'apify-client'; import express from 'express'; import type { Page } from 'puppeteer'; -import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, expectTypeOf, test, vi } from 'vitest'; import { META_ORIGINS, WEBHOOK_EVENT_TYPES } from '@apify/consts'; import { LEVELS, Log } from '@apify/log'; @@ -239,6 +239,26 @@ describe('Actor methods', () => { }); }); + test('ActorInput accepts plain objects, arrays of them and raw bytes, but no other objects', () => { + interface InterfaceInput { + url: string; + } + type AliasInput = { url: string }; + + expectTypeOf<{ url: string }>().toExtend(); + expectTypeOf().toExtend(); + expectTypeOf().toExtend(); + expectTypeOf().toExtend(); + expectTypeOf().toExtend(); + + expectTypeOf().not.toExtend(); + expectTypeOf().not.toExtend(); + expectTypeOf>().not.toExtend(); + expectTypeOf>().not.toExtend(); + expectTypeOf<() => void>().not.toExtend(); + expectTypeOf().not.toExtend(); + }); + test('start() works with functions in input', async () => { const actorId = 'some-id'; const input = { From f773bb8b41d6171fecc512e19fb1e0e091a5ec84 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 1 Oct 2026 16:01:15 +0200 Subject: [PATCH 2/4] feat: accept ArrayBuffer and readonly arrays as Actor input --- docs/04_upgrading/upgrading_to_v3.mdx | 2 +- docs/public-api/apify-client.api.md | 2 +- src/resource_clients/actor.ts | 9 +++++---- test/actors.test.ts | 3 +++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/04_upgrading/upgrading_to_v3.mdx b/docs/04_upgrading/upgrading_to_v3.mdx index cd70168b7..111f35c33 100644 --- a/docs/04_upgrading/upgrading_to_v3.mdx +++ b/docs/04_upgrading/upgrading_to_v3.mdx @@ -391,7 +391,7 @@ Two options that carried a `@deprecated` marker throughout v2 have been removed. `ActorClient.start()`, `call()`, `validateInput()` and `RunClient.metamorph()` took their `input` as `unknown`, so any value compiled, including ones the client cannot send. -The input is now typed `ActorInput`, which accepts a plain object, an array of plain objects, or raw bytes as a `Uint8Array` such as a `Buffer`. The client serializes objects and arrays into the request body and sends bytes as they are. Any other value stops compiling, including a `Date`, `Map`, `Set` or function, and a value typed `unknown`, which has to be narrowed or cast first. To run an Actor without input, omit the argument or pass `undefined`. +The input is now typed `ActorInput`, which accepts a plain object, an array of plain objects, or raw bytes as a `Uint8Array` (such as a `Buffer`) or an `ArrayBuffer`. The client serializes objects and arrays into the request body and sends bytes as they are. Any other value stops compiling, including a `Date`, `Map`, `Set` or function, and a value typed `unknown`, which has to be narrowed or cast first. To run an Actor without input, omit the argument or pass `undefined`. ```diff - await client.actor('my-actor').call(null, { memory: 1024 }); // v2 diff --git a/docs/public-api/apify-client.api.md b/docs/public-api/apify-client.api.md index b851499e7..32ffbaf7b 100644 --- a/docs/public-api/apify-client.api.md +++ b/docs/public-api/apify-client.api.md @@ -201,7 +201,7 @@ export interface ActorExampleRunInput extends GeneratedExampleRunInput { } // @public -export type ActorInput = Dictionary | Dictionary[] | Uint8Array; +export type ActorInput = Dictionary | readonly Dictionary[] | Uint8Array | ArrayBuffer; // @public export interface ActorLastRunOptions { diff --git a/src/resource_clients/actor.ts b/src/resource_clients/actor.ts index 47801eeae..4454cbb36 100644 --- a/src/resource_clients/actor.ts +++ b/src/resource_clients/actor.ts @@ -589,11 +589,12 @@ export type ActorUpdateOptions = Partial< * Input for an Actor run, as taken by {@link ActorClient.start}, {@link ActorClient.call}, * {@link ActorClient.validateInput} and {@link RunClient.metamorph}. * - * A plain object or an array of plain objects, serialized into the request body, or raw bytes sent - * as they are, typically with a `contentType`. An input typed as an `interface` needs a `type` alias - * or a spread (`{ ...input }`) to compile. + * A plain object or an array of plain objects, serialized into the request body, or raw bytes + * (a `Uint8Array` such as a `Buffer`, or an `ArrayBuffer`) sent as they are, typically with a + * `contentType`. An input typed as an `interface` needs a `type` alias or a spread (`{ ...input }`) + * to compile. */ -export type ActorInput = Dictionary | Dictionary[] | Uint8Array; +export type ActorInput = Dictionary | readonly Dictionary[] | Uint8Array | ArrayBuffer; export interface ActorStartOptions extends TimeoutOptions { /** diff --git a/test/actors.test.ts b/test/actors.test.ts index 752e96d01..107603362 100644 --- a/test/actors.test.ts +++ b/test/actors.test.ts @@ -248,8 +248,10 @@ describe('Actor methods', () => { expectTypeOf<{ url: string }>().toExtend(); expectTypeOf().toExtend(); expectTypeOf().toExtend(); + expectTypeOf().toExtend(); expectTypeOf().toExtend(); expectTypeOf().toExtend(); + expectTypeOf().toExtend(); expectTypeOf().not.toExtend(); expectTypeOf().not.toExtend(); @@ -257,6 +259,7 @@ describe('Actor methods', () => { expectTypeOf>().not.toExtend(); expectTypeOf<() => void>().not.toExtend(); expectTypeOf().not.toExtend(); + expectTypeOf().not.toExtend(); }); test('start() works with functions in input', async () => { From aa3c729c7335297e2dc4852c4e94c2a273ef0703 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 1 Oct 2026 16:01:16 +0200 Subject: [PATCH 3/4] docs: describe raw-bytes Actor input in the method docs --- src/resource_clients/actor.ts | 9 ++++++--- src/resource_clients/run.ts | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/resource_clients/actor.ts b/src/resource_clients/actor.ts index 4454cbb36..b877a6f52 100644 --- a/src/resource_clients/actor.ts +++ b/src/resource_clients/actor.ts @@ -180,7 +180,8 @@ export class ActorClient extends ResourceClient { * asynchronously and this method returns immediately without waiting for completion. * Use the {@link call} method if you want to wait for the Actor to finish. * - * @param input - Input for the Actor, serialized to JSON. Omit it to run the Actor without input. + * @param input - Input for the Actor: an object or array serialized to JSON, or raw bytes sent as they are. + * Omit it to run the Actor without input. * @param options - Run configuration options * @param options.build - Tag or number of the build to run (e.g., `'beta'` or `'1.2.345'`). If not provided, uses the default build. * @param options.memory - Memory in megabytes allocated for the run. If not provided, uses the Actor's default memory setting. @@ -263,7 +264,8 @@ export class ActorClient extends ResourceClient { * by polling the run status. It optionally streams logs to the console or a custom Log instance. * By default, it waits indefinitely unless the `waitSecs` option is provided. * - * @param input - Input for the Actor, serialized to JSON. Omit it to run the Actor without input. + * @param input - Input for the Actor: an object or array serialized to JSON, or raw bytes sent as they are. + * Omit it to run the Actor without input. * @param options - Run configuration options (extends all options from {@link start}) * @param options.waitSecs - Maximum time to wait for the run to finish, in seconds. If omitted, waits indefinitely. * @param options.log - Log instance for streaming run logs. Use `'default'` for console output, `null` to disable logging, or provide a custom Log instance. @@ -322,7 +324,8 @@ export class ActorClient extends ResourceClient { * invalid, the API responds with an error that is thrown as an `ApifyApiError` describing the * validation problem. * - * @param input - Input to validate against the Actor's input schema, serialized to JSON. + * @param input - Input to validate against the Actor's input schema: an object or array serialized to JSON, or + * raw bytes sent as they are. * @param options - Validation options * @param options.build - Tag or number of the build whose input schema the input is validated against * (e.g., `'latest'` or `'1.2.345'`). If not provided, uses the default build. diff --git a/src/resource_clients/run.ts b/src/resource_clients/run.ts index 349d70374..2413eb1d3 100644 --- a/src/resource_clients/run.ts +++ b/src/resource_clients/run.ts @@ -167,7 +167,8 @@ export class RunClient extends ResourceClient { * This is useful for chaining Actor executions or implementing complex workflows. * * @param targetActorId - ID or username/name of the target Actor - * @param input - Input for the target Actor, serialized to JSON. Omit it to metamorph without input. + * @param input - Input for the target Actor: an object or array serialized to JSON, or raw bytes sent as they + * are. Omit it to metamorph without input. * @param options - Metamorph options * @param options.build - Tag or number of the target Actor's build to run. Default is the target Actor's default build. * @param options.timeoutSecs - Timeout for the API request. Default is `'medium'`. From a57928d9f2a6ab6e493a27d165bc6d658f103061 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Thu, 1 Oct 2026 16:01:16 +0200 Subject: [PATCH 4/4] docs: note that class-typed Actor input stops compiling too --- docs/04_upgrading/upgrading_to_v3.mdx | 2 +- src/resource_clients/actor.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/04_upgrading/upgrading_to_v3.mdx b/docs/04_upgrading/upgrading_to_v3.mdx index 111f35c33..ea1398070 100644 --- a/docs/04_upgrading/upgrading_to_v3.mdx +++ b/docs/04_upgrading/upgrading_to_v3.mdx @@ -398,7 +398,7 @@ The input is now typed `ActorInput`, which accepts a plain object, an array of p + await client.actor('my-actor').call(undefined, { memory: 1024 }); // v3 ``` -An input declared as an `interface` stops compiling as well, since `ActorInput` needs an index signature and interfaces don't get one implicitly. Declare the input type as a `type` alias, or spread the value into a new object: +An input declared as an `interface` or a class stops compiling as well, since `ActorInput` needs an index signature and neither gets one implicitly. Declare the input type as a `type` alias, or spread the value into a new object: ```diff - interface MyInput { url: string } // v2 diff --git a/src/resource_clients/actor.ts b/src/resource_clients/actor.ts index b877a6f52..f35129047 100644 --- a/src/resource_clients/actor.ts +++ b/src/resource_clients/actor.ts @@ -594,8 +594,8 @@ export type ActorUpdateOptions = Partial< * * A plain object or an array of plain objects, serialized into the request body, or raw bytes * (a `Uint8Array` such as a `Buffer`, or an `ArrayBuffer`) sent as they are, typically with a - * `contentType`. An input typed as an `interface` needs a `type` alias or a spread (`{ ...input }`) - * to compile. + * `contentType`. An input typed as an `interface` or a class needs a spread (`{ ...input }`), or for + * an interface a `type` alias, to compile. */ export type ActorInput = Dictionary | readonly Dictionary[] | Uint8Array | ArrayBuffer;