Skip to content

refactor!: validate arguments with zod instead of ow - #986

Open
vdusek wants to merge 20 commits into
v3from
feat/replace-ow-with-zod
Open

refactor!: validate arguments with zod instead of ow#986
vdusek wants to merge 20 commits into
v3from
feat/replace-ow-with-zod

Conversation

@vdusek

@vdusek vdusek commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Replaces ow with zod for runtime argument validation, mirroring apify/apify-sdk-js#636 and apify/crawlee#3716 so all three packages share one error type and message format. Input validation only — response validation is a separate PR.

How it works

  • ArgumentValidationError and validate() are a hand-synced copy of the @crawlee/core / SDK ones — apify-client sits below both in the dependency graph.
  • zod ^4.0.0 matches the merged SDK PR; crawlee's open PR allows ^3 || ^4, so all three dedupe onto one copy.
  • The local formatter recurses into invalid_union, so a failed union lists every arm the way ow.any() did, and it replaces zod's self-contradictory expected number, received number / expected date, received Date — both worth upstreaming.
  • Schemas use z.strictObject / z.looseObject / z.enum, not the deprecated .strict() / .passthrough() / z.nativeEnum().
  • chunkSize now works wherever the options interface extends PaginationOptions, via a shared paginationOptionsShape the schemas spread. It used to type-check but throw; ow's exactShape had the same gap.

Browser bundle

  • Tree-shaking and minification are back on in rsbuild.config.ts, off since the webpack-to-rsbuild migration in chore: update eslint, adopt prettier and rsbuild #671. Now 288 kB raw / 87 kB gzip, from 1439 kB / 273 kB — below the 946 kB / 203 kB before this PR. A 320 kB budget fails the build, so it cannot grow unnoticed again.
  • Class names stay unmangled: ApifyApiError takes its name from constructor.name, and ResourceClient.waitForFinish() parses the resource name out of it.
  • Zod installs its English locale as a module-level side effect but ships "sideEffects": false, so tree-shaking drops it and messages degrade to a bare Invalid input — for anyone bundling apify-client, not just us. The locale is now passed in per parse, which also keeps it out of the zod config shared with the rest of the process. Covered, with the class names, by test/browser_bundle.test.ts.

Breaking changes

  • Invalid arguments throw ArgumentValidationError (exported from apify-client), not ow's ArgumentError — different messages, the zod issues on issues, the original ZodError on cause.
  • Arrays and functions no longer pass where a plain object is expected: update() / create() fields, TaskClient.start() / call() input, the storage schema option, DatasetClient.pushItems() items.
  • Infinity no longer passes on numeric options such as waitSecs, timeoutSecs or memory, and an invalid Date no longer passes on startedBefore / startedAfterz.number() requires a finite number and z.date() a valid date, where ow only checked the type.
  • Options that were declared but always rejected at runtime are gone from the types: chunkSize on downloadItems() and createItemsPublicUrl(), signature on createItemsPublicUrl() and createKeysPublicUrl() — a compile error now instead of a throw.
  • Date, Map, Set and other class instances still pass as objects, as under ow.

✍️ Drafted by Claude Code

@vdusek vdusek added adhoc Ad-hoc unplanned task added during the sprint. t-tooling Issues with this label are in the ownership of the tooling team. labels Jul 30, 2026
@vdusek vdusek self-assigned this Jul 30, 2026
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

⚠️ There are broken links in the documentation.

See more at https://github.com/apify/apify-client-js/actions/runs/31005542533#summary-92304422147

@vdusek vdusek changed the title refactor!: replace ow with zod for argument validation refactor!: validate arguments with zod instead of ow Jul 30, 2026
@vdusek
vdusek requested a review from B4nan July 31, 2026 09:40
@vdusek
vdusek marked this pull request as ready for review July 31, 2026 09:40
@vdusek
vdusek requested a review from szaganek as a code owner July 31, 2026 09:40
vdusek added 8 commits August 3, 2026 15:48
BREAKING CHANGE: runtime argument validation switched from `ow` to `zod`, so
every invalid-argument error message changed, and the thrown error is now an
`ArgumentValidationError` (newly exported from `apify-client`) instead of `ow`'s
`ArgumentError`. It exposes the structured zod issues on `issues` and keeps the
original `ZodError` on `cause`, so you can branch on them instead of parsing the
message. Values that `ow.object` accepted only incidentally are now rejected:
arrays no longer pass as objects for `update()` / `create()` fields, for
`TaskClient.start()` / `call()` input, for the storage `schema` option, or as
`DatasetClient.pushItems()` array items (which must be objects or strings).
@vdusek
vdusek force-pushed the feat/replace-ow-with-zod branch from 4e8b5c1 to b74a66e Compare August 3, 2026 13:48
B4nan added 4 commits August 3, 2026 17:19
The ow-based validation rejected symbol and bigint values loudly, but the
zod replacement only checked for undefined. A symbol value would then pass
validation, serialize to undefined, and silently PUT an empty record body.
Also fixes a pre-existing "validatioon" typo carried through two comments.
`describeReceived('')` used to produce bare backticks with nothing
between them, e.g. for `client.actor('')`.
@B4nan

B4nan commented Aug 3, 2026

Copy link
Copy Markdown
Member

Follow-ups: the browser bundle grows from 946 kB to 1446 kB raw (203 kB -> 272 kB gzip), because rsbuild.config.ts disables tree-shaking and minification; .strict(), .passthrough() and z.nativeEnum() are deprecated in zod 4 and could move to z.strictObject / z.looseObject / z.enum; chunkSize is missing from every .strict() list schema (pre-existing — ow's exactShape had the same gap).

I would rather fix it here before it gets merged. We don't want to use any deprecated methods, and this PR introduces the bundle size issue.

@vdusek

vdusek commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

I would rather fix it here before it gets merged. We don't want to use any deprecated methods, and this PR introduces the bundle size issue.

OK, I'll check it out

@vdusek
vdusek force-pushed the feat/replace-ow-with-zod branch from aa2684d to 41d769b Compare August 4, 2026 08:50
@vdusek

vdusek commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@B4nan it's ready for a re-check

@B4nan
B4nan requested a review from vladfrangu August 4, 2026 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adhoc Ad-hoc unplanned task added during the sprint. t-tooling Issues with this label are in the ownership of the tooling team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants