Skip to content

Replace zod with valibot; misc fixes - #48

Merged
baronunread merged 10 commits into
mainfrom
chore/misc-fixes
Jul 28, 2026
Merged

Replace zod with valibot; misc fixes#48
baronunread merged 10 commits into
mainfrom
chore/misc-fixes

Conversation

@baronunread

@baronunread baronunread commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replace zod with valibot for form schemas across the app
  • Skip re-sending a verification OTP to already-verified accounts
  • Remove outdated docs (click ingestion, rate limiting, storage recovery)
  • Add CodeGraph data files to .gitignore
  • Minor AGENTS.md / README updates

Test plan

  • bun run check
  • bunx tsc -p tsconfig.worker.json --noEmit
  • bun run test

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements

    • Migrated form validation from Zod to Valibot across auth, links, domains, settings, members, and dashboards.
    • Prevented sending verification OTP emails to already verified accounts.
    • Updated local Cloudflare “fake mode” behavior to be driven by CF_API_TOKEN.
  • Documentation

    • Updated configuration guidance and the worker module layout list.
    • Removed outdated operational guides (rate limiting, click ingestion, storage recovery).
  • Tests

    • Added e2e coverage for invalid forgot-password submissions and incomplete verification codes.
    • Updated schema and domain worker tests for the new validation and fake mode behavior.

baronunread and others added 5 commits July 28, 2026 00:33
The z namespace import is not tree-shakeable (importing it pulls in
roughly the whole builder API), costing ~29 KB gzip in the shared
schemas chunk for five small form schemas. Valibot's per-function
imports tree-shake to actual usage, cutting that chunk to ~14 KB gzip.

Also fills e2e/unit test gaps found while verifying the resolver swap:
forgotSchema had no test at all, and the forgot-password/OTP forms'
valibotResolver wiring had no e2e coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
send-verification-otp is unauthenticated and never checked the
target's emailVerified state before mailing a fresh code. Combined
with autoSignInAfterVerification, this meant anyone who could read a
single OTP email for an existing, already-verified user got a full
session as that user, indefinitely, not just during the signup
window most apps restrict this shortcut to. Brute-forcing the code
itself isn't practical (3 attempts per code, 10-minute expiry, fresh
code per resend), but reading one email is enough on its own.

Verified against the real dev server: with the guard removed, a
second send-verification-otp call for an already-verified address
mails another code (proving the exploit); restoring the guard blocks
it. An automated e2e regression was attempted but dropped: the
endpoint shares a tight 3-per-60s rate-limit bucket with every real
signup in the suite, and adding another live call to it destabilized
unrelated tests under full parallelism.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both docs' layout sections had drifted from src/worker/'s actual
contents (missing better-auth.ts, guards.ts, org-role.ts,
rate-limit.ts, session.ts, clicks.ts, workflows.ts).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit dc8950f.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@baronunread, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dba0fd68-bff3-45fe-802b-709f35a28038

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc23a9 and cef3946.

📒 Files selected for processing (1)
  • README.md
📝 Walkthrough

Walkthrough

The pull request replaces Zod with Valibot across schemas and form resolvers, adds client and worker handling for verified accounts, changes local Cloudflare simulation to use an unset token, updates repository metadata, and removes several operational documents.

Changes

Valibot validation migration

Layer / File(s) Summary
Schema contracts and validation tests
package.json, src/app/lib/schemas.ts, tests/schemas.test.ts
Migrates schemas and parsing tests from Zod to Valibot while preserving constraints and updating defaults, nullable fields, URL checks, and forgot-password coverage.
Form resolver integration
src/app/components/*, src/app/routes/*, tests/e2e/auth.pw.ts
Switches forms to valibotResolver and adds end-to-end checks for invalid forgot-password and OTP submissions.

Verified-account OTP guard

Layer / File(s) Summary
Verified-account OTP handling
src/worker/better-auth.ts, src/app/routes/auth.tsx
Checks account verification before sending OTPs and returns authentication flows to the form when EMAIL_VERIFIED is reported.

Cloudflare local-mode configuration

Layer / File(s) Summary
Token-driven simulation
.dev.vars*, src/worker/env.ts, src/worker/routes/domains.ts, tests/worker/domains.worker.ts, README.md
Uses an unset CF_API_TOKEN for simulated hostname operations and updates timing, tests, and local-development guidance.

Repository maintenance

Layer / File(s) Summary
Metadata, tooling, and documentation
.codegraph/.gitignore, AGENTS.md, package.json, docs/*.md
Adds CodeGraph ignore rules, updates worker listings and development tooling versions, and removes click-ingestion, rate-limiting, and storage-recovery documentation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • baronunread/rdyrct#13: Updates overlapping form components and routes that previously used Zod-based React Hook Form resolvers.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main schema migration and acknowledges the smaller accompanying fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/misc-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/worker/better-auth.ts`:
- Around line 111-120: Move the existing emailVerified lookup out of the
sendVerificationOTP callback and perform it before dispatching the OTP. Update
the relevant authenticated and unauthenticated route/client flows around
sendVerificationOTP to return and handle an explicit EMAIL_VERIFIED result,
avoiding the code screen for verified accounts while preserving OTP sending for
unverified emails.
- Around line 111-120: Update the unauthenticated email-verification flow around
the existing emailVerified lookup and early return to use waitUntil() so
verified and unverified requests have indistinguishable response timing. For
verified accounts, discard the usable OTP while still publishing the appropriate
non-sign-in confirmation event; preserve normal OTP delivery for unverified
accounts.
- Around line 116-120: Close the race between the emailVerified check and OTP
issuance in the verification flow around sendEmail. Coordinate code
creation/sending with the verification-state update so concurrent verification
cannot issue a stale OTP, or record the verification completion boundary and
make autoSignInAfterVerification reject codes issued before it. Preserve the
existing early return for already-verified users.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6dc032a5-8b0d-4d7c-ae6c-743acf969f56

📥 Commits

Reviewing files that changed from the base of the PR and between 5ffff64 and c4c9856.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .codegraph/.gitignore
  • AGENTS.md
  • README.md
  • docs/click-ingestion.md
  • docs/rate-limiting.md
  • docs/storage-recovery.md
  • package.json
  • src/app/components/link-editor.tsx
  • src/app/components/no-org.tsx
  • src/app/lib/schemas.ts
  • src/app/routes/auth.tsx
  • src/app/routes/dashboard.tsx
  • src/app/routes/domains.tsx
  • src/app/routes/members.tsx
  • src/app/routes/settings.tsx
  • src/worker/better-auth.ts
  • tests/e2e/auth.pw.ts
  • tests/schemas.test.ts
💤 Files with no reviewable changes (3)
  • docs/click-ingestion.md
  • docs/storage-recovery.md
  • docs/rate-limiting.md

Comment thread src/worker/better-auth.ts Outdated
Comment thread src/worker/better-auth.ts Outdated
baronunread and others added 3 commits July 28, 2026 02:22
Also widens the fake's issuing_tls window (5s-20s, was 5s-8s) so it's
visible in a browser or e2e run, and fixes a stale comment claiming
domain reads advance the activation pipeline (a background Workflow
does that now).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…llback

better-auth swallows whatever sendVerificationOTP returns or throws and
always answers the client with a generic success, so skipping the send
for an already-verified email there left resend/verify flows stuck on a
code screen waiting for a code that would never arrive. Move the check
into hooks.before (same pattern as the sign-up duplicate-email guard),
where a thrown APIError actually reaches the client as EMAIL_VERIFIED,
and have the client bounce back to the login form on that code. Split
the before hook into guardVerificationOTPSend/guardSignUp to keep its
complexity within the fallow health baseline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Line 82: Update the README statement about unset CF_API_TOKEN to remove the
inaccurate “activate instantly on Check status” claim. Describe that local
custom domains use stubbed Cloudflare status and become ready after the
simulated DNS and TLS delays, using short, direct wording.

In `@src/worker/routes/domains.ts`:
- Line 63: Update the domain workflow around the Cloudflare credential check to
fail closed when CF_API_TOKEN or CF_ZONE_ID is missing, preventing fake domain
IDs from reaching activation, deletion, or re-add paths. If local fake behavior
is required, gate it behind an explicit test-only flag rather than the absence
of credentials; otherwise reject the operation before activation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 06347ffa-1e91-4f95-90fe-2d857cef7d7a

📥 Commits

Reviewing files that changed from the base of the PR and between c4c9856 and 6dc23a9.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • .dev.vars.example
  • .dev.vars.playwright
  • AGENTS.md
  • README.md
  • package.json
  • src/app/lib/hooks.ts
  • src/app/routes/auth.tsx
  • src/worker/better-auth.ts
  • src/worker/env.ts
  • src/worker/routes/domains.ts
  • tests/worker/domains.worker.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json

Comment thread README.md Outdated
Comment thread src/worker/routes/domains.ts Outdated
baronunread and others added 2 commits July 28, 2026 11:58
It doesn't activate "instantly": the stub delays DNS ~5s and TLS ~20s
so the checking_dns/issuing_tls states are visible in a browser or e2e run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@baronunread

Copy link
Copy Markdown
Owner Author

@coderabbitai

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@baronunread How can I help with PR #48?

@baronunread
baronunread merged commit 80bfbff into main Jul 28, 2026
3 checks passed
@baronunread
baronunread deleted the chore/misc-fixes branch July 28, 2026 14:13
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