Skip to content

chore: migrate props to zod v4#752

Draft
DPS0340 wants to merge 2 commits into
tscircuit:mainfrom
DPS0340:chore/zod-v4-compat
Draft

chore: migrate props to zod v4#752
DPS0340 wants to merge 2 commits into
tscircuit:mainfrom
DPS0340:chore/zod-v4-compat

Conversation

@DPS0340

@DPS0340 DPS0340 commented Jul 24, 2026

Copy link
Copy Markdown

Summary

  • require zod@^4 and circuit-json@^0.0.455 as peers so composed schemas use one Zod major
  • migrate record, transform-default, and Zod generic APIs while preserving existing parsed prop behavior
  • replace the removed Zod 3 function-schema chain with boundary-validating wrappers for platform callbacks
  • add regression coverage for optional-default absence and platform callback input/output validation
  • regenerate Props documentation and clarify the affected optional-field contract

Dependency order

This is intentionally a draft until circuit-json PR #661 merges and its release workflow publishes circuit-json@0.0.455. The coordinated rollout is circuit-json, footprinter #726, @tscircuit/props, then core. The exact 0.0.455 range prevents a Zod 3 circuit-json schema from being composed with this package.

Verification

  • bun test: 390 passing
  • bun run typecheck and bun run build with the packed circuit-json PR Add krt autorouter preset #661 artifact installed into a clean worktree
  • bunx biome format on changed files
  • required documentation generators
  • npm pack --dry-run

DPS0340 added 2 commits July 25, 2026 01:23
Preserve public prop and platform function validation contracts under the Zod v4 APIs.
@DPS0340

DPS0340 commented Jul 24, 2026

Copy link
Copy Markdown
Author

The prior type-check failure was reproduced in a clean install: npm circuit-json@0.0.454 still carries Zod 3, so its exported schemas cannot compose with this Zod 4 branch. I then installed the packed artifact from circuit-json PR #661 into a separate clean worktree; bunx tsc --noEmit passed there. Commit b13decc now requires circuit-json@^0.0.455 in both dependency contracts. CI can be rerun after #661 merges and its release workflow publishes that package.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Author

Status update: this one is blocked on circuit-json, not on this diff.

circuit-json@0.0.455 is now published, so bun install resolves and bun test passes (390 pass / 0 fail). However tsc --noEmit still fails with ~122 errors, and the root cause is upstream: the published 0.0.455 build is still compiled against Zod v3 and references ZodEffects (2800 occurrences in dist/index.d.mts), which does not exist in Zod v4:

node_modules/circuit-json/dist/index.d.mts(4,29): error TS2694:
Namespace '.../zod/v4/classic/external' has no exported member 'ZodEffects'.

Because props asserts expectTypesMatch<XProps, InferredXProps>, the broken upstream inference surfaces as missing props errors across the component schemas.

So the ordering is: merge and release tscircuit/circuit-json#661 (the Zod v4 migration) → re-run CI here → this should go green with no changes needed. Happy to rebase and re-verify as soon as that lands. Tracking issue: tscircuit/core#2810.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Author

Note on the red checks here: this is blocked upstream, not by anything in this PR.

The published circuit-json@0.0.455 still ships Zod v3-shaped types (z.ZodEffects, 5-parameter ZodObject), which don't exist in Zod v4. Any package composing those schemas under v4 fails type-check no matter what this PR does.

Unblocked by tscircuit/circuit-json#661, which is green and ready — once that merges and publishes, I'll refresh this branch and the checks should go green.

Happy to close this and reopen after the circuit-json release if you'd rather not have a red PR sitting in the queue.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Author

Additional evidence that this PR matters beyond its own type-check.

While testing whether @tscircuit/circuit-json-util could relax its zod: "3" peer pin, I found that widening it breaks that repo — and the failure traces back here:

TypeError: z.function().args is not a function
  at node_modules/@tscircuit/props/dist/index.js:18259
  var pathToCircuitJsonFn = z.function().args(z.string()).returns(...)

circuit-json-util goes 91 pass / 0 fail on zod 3 → 88 pass / 3 fail on zod 4, and all three failures are that one line. z.function().args() was removed in Zod v4, and it's still present in the published @tscircuit/props@0.0.494.

This PR already fixes itlib/platformConfig.ts replaces that construct with createValidatedAsyncFunctionSchema, which works under both majors.

So this isn't only a props-internal migration: it's the change that unblocks circuit-json-util from ever accepting Zod v4, which in turn is what makes footprinter's npm-based job installable. Ordering is circuit-json #661 → this → circuit-json-util peer widening → footprinter #726.

Status here is unchanged and still upstream-bound: bun test passes 390/390, and the remaining type errors are all from circuit-json@0.0.455 still shipping Zod v3-shaped declarations (ZodEffects). Those clear the moment #661 is released — no changes needed in this diff.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Author

@imrishabh18 — tagging you since you merge most PRs here, but this one is not ready to merge yet and I want to be upfront about why rather than have it sit red in your queue.

It's blocked on publish order, not on its own diff:

  1. circuit-json#661 (Zod v4 migration) must merge and publish first. The currently published circuit-json@0.0.455 still ships Zod v3-shaped types (z.ZodEffects, 5-parameter ZodObject) in dist/index.d.mts — neither exists in Zod v4, so anything composing those schemas fails type-check here regardless of what this PR does.
  2. Then this one goes green with no code changes.

bun test already passes 390/390 here; only tsc is red, and every error traces to that upstream .d.mts.

Worth knowing this PR isn't only props-internal: it removes the last z.function().args() call in the org (lib/platformConfig.ts), which is what currently prevents @tscircuit/circuit-json-util from relaxing its zod: "3" peer pin. I verified that widening the pin without this change takes circuit-json-util from 91 pass/0 fail to 88/3, all three failing on that one line.

Full ordering is on core#2810. Happy to close and reopen this once #661 publishes if you'd rather keep the queue clean.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Author

Heads-up on a process issue rather than the code: this repo's stalebot will close this PR while it waits on its upstream.

.github/workflows/stalebot.yml here runs daily at 01:30 UTC with:

days-before-pr-stale: 2
days-before-pr-close: 1
exempt-pr-labels: 'pinned,security,help-wanted'

This PR is deliberately parked — it can't go green until circuit-json#661 merges and publishes, because the published circuit-json@0.0.455 still ships Zod v3-shaped types (z.ZodEffects) that don't exist in v4. "Waiting on an upstream release" is exactly the no-activity state stalebot closes.

This isn't hypothetical: core#2726 (a fix for the same issue as my core#2813, by a different contributor) got zero reviews for 4 days and was closed by github-actions[bot] on 07-24. It wasn't rejected on merit, it just aged out.

Three options, whichever you prefer:

  1. Add one of the exempt labels (pinned is the natural one) and leave it parked.
  2. Tell me to close it, and I'll reopen a fresh PR once Add krt autorouter preset #661 publishes.
  3. I keep it alive with periodic rebases — works, but it's noise in your notifications and I'd rather not.

@imrishabh18 — a one-word reply is enough; I don't want to keep bumping this just to outrun a bot.

@DPS0340

DPS0340 commented Jul 25, 2026

Copy link
Copy Markdown
Author

Re-verified the blocker today, since the failing CI run here is stale and misleading.

The recorded failure is no longer the real one. That run executed at 16:33 UTC and died on:

error: No version matching "^0.0.455" found for specifier "circuit-json"

circuit-json@0.0.455 was published at 21:23 UTC, ~5 hours later. So install now succeeds — I confirmed locally:

$ bun install
+ zod@4.4.3
345 packages installed
$ node -e "console.log(require('circuit-json/package.json').version)"
0.0.455

But the underlying blocker is unchanged. With 0.0.455 actually installed, tsc --noEmit still fails:

lib/common/cadModel.ts(63,62): error TS2345: Argument of type 'true' is not assignable to parameter of type '`missing props ${any}`'
lib/common/commonShape.ts(50,70): error TS2345: Argument of type 'true' is not assignable to parameter of type 'false'
lib/common/kicadFootprintMetadata.ts(18,44): ...

Because the published build still ships v3-shaped types:

$ grep -c "ZodEffects" node_modules/circuit-json/dist/index.d.mts
2813

ZodEffects doesn't exist in Zod v4, so anything composing these schemas under v4 fails type-check regardless of its own diff.

Order is still: tscircuit/circuit-json#661 must merge and publish before this PR can go green. #661 is MERGEABLE/CLEAN and has been since it was opened; it's self-contained and doesn't depend on anything here.

I've left this PR open rather than closing it, since it's ready apart from the upstream dependency — but I'm happy to close and reopen after #661 publishes if a red check in the queue is more trouble than it's worth. Just say which you'd prefer.

Same situation applies to tscircuit/footprinter#726.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant