Skip to content

refactor(cli): migrate self-contained runtime validation from zod to valibot - #1370

Merged
jared-outpost[bot] merged 2 commits into
mainfrom
issue-1367-zod-to-valibot
Aug 6, 2026
Merged

refactor(cli): migrate self-contained runtime validation from zod to valibot#1370
jared-outpost[bot] merged 2 commits into
mainfrom
issue-1367-zod-to-valibot

Conversation

@jared-outpost

@jared-outpost jared-outpost Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Migrates the CLI's self-contained runtime validation from zod to valibot to reduce startup overhead, per #1367.

what changed

Converted the validation that is not coupled to zod:

  • src/types/oauth.ts + src/lib/oauth.ts — OAuth device-flow response validation (device code, token, token error). .passthrough()looseObject; safeParse result access updated to valibot's .output / .issues.
  • src/types/config.ts — the config file schema (SentryConfig and friends).
  • src/lib/dsn/types.tsResolvedProjectInfoSchema and CachedDsnEntrySchema (DSN cache validation). z.enumpicklist.
  • src/lib/qrcode.ts — QR code options. z.boolean().default(true)optional(boolean(), true).
  • Added valibot as a dependency; used named (tree-shakeable) imports to satisfy biome's noNamespaceImport.

what stays on zod (and why)

Some of the CLI's zod usage can't be swapped in isolation, so it intentionally stays on zod for now:

  • @sentry/api/zod couplingsrc/types/sentry.ts, src/types/feedback.ts, and src/lib/api/replays.ts compose/consume auto-generated zod schemas from the external @sentry/api package (.pick/.partial/.extend/.shape, safeParseAsync). These can't move without upstream valibot schemas or an interop layer.
  • central response validatorsrc/lib/api/infrastructure.ts validates every API response via apiRequestToRegion(schema) and reads ZodError.issues for Sentry telemetry. Every src/lib/api/* schema flows through it, so migrating it is a repo-wide change with telemetry implications.
  • zod-internals introspectionsrc/lib/formatters/output.ts walks zod internals (_def.typeName) to generate --help / skill docs. Valibot's internal shape differs and would need a separate introspection implementation.

These are good candidates for follow-up PRs.

testing

  • vitest run on the affected suites (oauth types + lib, config, dsn) — 69 tests pass.
  • biome check on the changed files — clean.
  • tsc --noEmit — no new type errors introduced by these files (the only tsc errors are pre-existing, from generated files that require the codegen step).

Closes #1367

…valibot

Switches the CLI's uncoupled runtime validation to valibot to reduce
startup overhead: OAuth device-flow responses (types/oauth.ts +
lib/oauth.ts), the config file schema (types/config.ts), the DSN cache
schemas (lib/dsn/types.ts), and qrcode options (lib/qrcode.ts).

zod is retained for the schemas that are coupled to it and cannot be
migrated in isolation:
- types/sentry.ts, types/feedback.ts, lib/api/replays.ts compose/consume
  auto-generated schemas from @sentry/api/zod (external, zod-based)
- lib/api/infrastructure.ts is the central response validator every
  lib/api/* schema flows through and reads ZodError.issues for telemetry
- lib/formatters/output.ts introspects zod internals for --help/skill gen

Refs #1367
@jared-outpost

jared-outpost Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

fix-ci: attempt 1 — check:deps flagged valibot in dependencies. this package bundles everything at build time, so all packages must live in devDependencies (that's where zod already is). moving valibot over.

check:deps enforces that all packages live in devDependencies since the
CLI bundles everything at build time. valibot was added to dependencies;
move it to devDependencies alongside zod.
@jared-outpost

jared-outpost Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

fixed — moved valibot from dependencies to devDependencies (601c8ec). check:deps passes locally now:

✓ No runtime dependencies in package.json

the CLI bundles all packages at build time, so nothing should sit in dependencies — valibot now lives alongside zod in devDependencies. re-running CI.

@jared-outpost
jared-outpost Bot marked this pull request as ready for review August 5, 2026 20:46
@jared-outpost
jared-outpost Bot requested a review from BYK August 5, 2026 20:46
@jared-outpost

jared-outpost Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

CI is green — marking this ready for review. the Build workflow passed (Lint & Typecheck incl. check:deps, Unit Tests, Validate generated files), plus Warden, Secret Scan, Dependency Review, and Semgrep. only the org-managed Socket Security PR scan is still settling, which doesn't gate.

self-review's clean: the valibot migration is scoped to the uncoupled validation (OAuth, config, DSN cache, qrcode) and the rest stays on zod as documented in the description.

@github-actions github-actions Bot added the risk: high PR risk score: high label Aug 5, 2026
@BYK

BYK commented Aug 6, 2026

Copy link
Copy Markdown
Member

Okay Jared, ship this and file a follow up to remove Zod completely

@jared-outpost
jared-outpost Bot merged commit 0348cda into main Aug 6, 2026
32 checks passed
@jared-outpost
jared-outpost Bot deleted the issue-1367-zod-to-valibot branch August 6, 2026 12:03
@jared-outpost

jared-outpost Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

shipped + follow-up at #1371

jared-outpost Bot added a commit that referenced this pull request Aug 7, 2026
---
first pass on #1371 — removing the remaining zod usage after the valibot
migration (#1370).

this PR does the one piece that can be migrated in isolation, plus the
dependency bump that unblocks the rest.

## what changed
- **bump `@sentry/api` `^0.253.0` → `^0.256.0`.** required: 0.256.0 is
the first release that actually ships the `./valibot` entrypoint —
0.253–0.255 advertise it in their `exports` map but ship no
`valibot.js`.
- **`lib/api/replays.ts`**: the SDK `responseValidator` now uses
`vListProjectReplayRecordingSegmentsResponse` from `@sentry/api/valibot`
+ valibot `safeParse`, instead of
`zListProjectReplayRecordingSegmentsResponse.safeParseAsync` from
`@sentry/api/zod`. this is the only zod usage not coupled to the two
shared hubs (see below), so it's the only piece safely migratable on its
own.
- **`types/sentry.ts`**: the SDK bump narrows
`GetOrganizationIssueResponse["status"]`, which made the
`ISSUE_STATUSES` `satisfies` drift-guard misfire. relaxed it to a
deliberate CLI superset that keeps `resolvedInNextRelease` and `muted` —
both are still emitted by the retrieve-issue endpoint and still rendered
by the CLI (`STATUS_ICONS`/`STATUS_LABELS`/`STATUS_COLORS`). **no
rendering behavior changes.**
- regenerated skill reference docs (`event.md`, `issue.md`) — the SDK
bump flipped `metadata` nullability; committed to keep the
`check-generated` CI job green.

## tested
- `tsc --noEmit`: clean
- `biome check` on changed files: clean
- `vitest run test/lib/api/replays test/types/sentry
test/lib/formatters`: 1001 passed

## follow-ups (remaining zod, tracked in #1371)
the rest can't be split cleanly because two shared hubs force an
all-or-nothing migration of the schemas that flow through them:
- **`lib/api/infrastructure.ts`** — `schema?: z.ZodType<T>` +
`.safeParse()` is used by ~30 callsites across the api layer; changing
the type migrates them all at once (valibot's `safeParse` is a free
function, not a method).
- **`lib/formatters/output.ts`** —
`extractSchemaFields`/`zodTypeToString` read zod internals
(`_def.typeName`, `.shape`, union `.options`) for ~16 commands'
`--help`/`--fields` docs; valibot's runtime shape
(`.type`/`.entries`/`.wrapped`) is different and needs a rewrite.
- the `@sentry/api/zod` schemas in `types/sentry.ts` +
`types/feedback.ts` (`zBaseTeam`, `zGetOrganizationIssueResponse`,
`zGroupEventsResponseDict`, `zEventAttachmentDetailsResponse`) → their
`v*` equivalents, incl. reworking the
`.pick`/`.partial`/`.extend`/`.shape`/`.describe` derivations.
- the ~13 self-contained `z.object` schemas (conversation, dashboard,
replay, seer, proguard, code-mappings, chunk-upload, dart-symbols,
debug-files, preprod-artifacts, conversations, dashboards) + the
`zod_validation` telemetry paths in `infrastructure.ts`/`logs.ts`.

once all of the above land, the `zod` dependency can be dropped
entirely.

⚠️ maintainer note: the `@sentry/api` 0.256 status-union narrowing is a
behavior-adjacent change — see the inline comment on `ISSUE_STATUSES`.
---

---------

Co-authored-by: jared-outpost[bot] <jared-outpost[bot]@users.noreply.github.com>
jared-outpost Bot added a commit that referenced this pull request Aug 7, 2026
Completes the zod→valibot migration started in #1370/#1388 and drops the
`zod` dependency entirely.

Migrated the three coupling categories from #1371:
- `@sentry/api/zod` schemas in types/sentry.ts, types/feedback.ts →
  `@sentry/api/valibot` (v*-prefixed), reworking the .pick/.partial/.shape
  derivations (valibot has no .extend — composed via object spread).
- Central response validator in lib/api/infrastructure.ts + lib/api/logs.ts:
  z.ZodType → GenericSchema, schema.safeParse → safeParse(schema, data),
  result.error.issues → result.issues. Sentry telemetry context renamed
  zod_validation → schema_validation.
- Zod-internals introspection in lib/formatters/output.ts rewritten for
  valibot's runtime shape (.entries/.wrapped/.options + getDescription),
  incl. pipe/coercion type resolution and nested-wrapper descriptions.

Also migrated the ~13 self-contained z.object schema files (types/*,
lib/api/*), commands/code-mappings/upload.ts, and the test suites' schema
API usage.

Verified: tsc clean, biome clean, generated skill docs regenerate
unchanged (introspection parity), full lib/types/commands test suites pass.

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

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Switch CLI from Zod to Valibot

1 participant