refactor(cli): migrate self-contained runtime validation from zod to valibot - #1370
Conversation
…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
|
fix-ci: attempt 1 — |
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.
|
fixed — moved the CLI bundles all packages at build time, so nothing should sit in |
|
CI is green — marking this ready for review. the Build workflow passed (Lint & Typecheck incl. 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. |
|
Okay Jared, ship this and file a follow up to remove Zod completely |
|
shipped + follow-up at #1371 |
--- 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>
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
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;safeParseresult access updated to valibot's.output/.issues.src/types/config.ts— the config file schema (SentryConfigand friends).src/lib/dsn/types.ts—ResolvedProjectInfoSchemaandCachedDsnEntrySchema(DSN cache validation).z.enum→picklist.src/lib/qrcode.ts— QR code options.z.boolean().default(true)→optional(boolean(), true).valibotas a dependency; used named (tree-shakeable) imports to satisfy biome'snoNamespaceImport.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/zodcoupling —src/types/sentry.ts,src/types/feedback.ts, andsrc/lib/api/replays.tscompose/consume auto-generated zod schemas from the external@sentry/apipackage (.pick/.partial/.extend/.shape,safeParseAsync). These can't move without upstream valibot schemas or an interop layer.src/lib/api/infrastructure.tsvalidates every API response viaapiRequestToRegion(schema)and readsZodError.issuesfor Sentry telemetry. Everysrc/lib/api/*schema flows through it, so migrating it is a repo-wide change with telemetry implications.src/lib/formatters/output.tswalks 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 runon the affected suites (oauth types + lib, config, dsn) — 69 tests pass.biome checkon 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