Feature: rebase/deps-minor to dev - #416
Conversation
Move every workspace package to TypeScript 7.0.2, including sites/hacklytics2027 (was 5.8.3). No tsconfig or source changes were needed; tsc --noEmit and next build type checking both pass on 7. tooling/eslint stays on TypeScript 6.0.2 on purpose: TS 7 no longer ships the classic JS API, and typescript-eslint (latest 8.70.1) still requires typescript <6.1.0. Every lint config resolves typescript-eslint through @query/eslint-config, so pinning it there keeps lint working while the rest of the repo compiles with TS 7.
Brings every dependency with a same-major update to its latest release, including Next.js 16.3.6 (latest stable), tRPC 11.19, TanStack Query 5.103, drizzle, Tailwind 4.3, React 19.3, eslint 10.11 and typescript-eslint 8.70.1. No source changes needed.
- zod 3 -> 4. `.default({})` on the createPaymentIntent input now uses
`.prefault({})`, which keeps zod 3's behaviour of running the inner
field defaults. zod 4's `.uuid()` is RFC-strict; the database only
generates v4 ids (defaultRandom), so the stricter check stays and the
router test fixtures now use valid UUIDs instead of 0000...0001.
- nodemailer 9 -> 10: `SentMessageInfo.pending` is optional; guard it.
- vitest 4 -> 5: vite is now a peer dependency, so it is installed
explicitly at the root and in @query/api.
- @stripe/stripe-js 5 -> 9, @stripe/react-stripe-js 3 -> 6, dotenv 16 -> 18,
qrcode.react 3 -> 4, cross-env 7 -> 10: no code changes needed.
- Drop @types/minimatch and @types/qrcode.react (deprecated; both
packages ship their own types).
- @types/node stays on 22.x to match engines (node <24).
- vitest 5 requires Node ^22.12. test.yml ran on Node 20; move it to 22, matching pnpm-ci.yml. vitest is dev-only, so the App Hosting runtime (nodejs20) and the engines range are unaffected. - package.json asked for vite ^7.3.6 while the pnpm override pins ^7.3.5, so the lockfile recorded a different specifier than the manifest. Both now say ^7.3.5; pnpm install --frozen-lockfile passes.
Upgrade major dependencies and migrate (zod 4, vitest 5, nodemailer 10, Stripe)
Upgrades @eslint/js 10, @eslint/compat 2, eslint-config-prettier 10, eslint-plugin-react-hooks 7 and globals 17, and drops the deprecated @types/eslint__js. react-hooks 7's recommended set adds the React Compiler rules. Both sites build with reactCompiler: true, so these are real findings, not style. Fixes for the 31 new errors: - Mounted flags (`useEffect(() => setMounted(true), [])`) become `useIsClient()`, a useSyncExternalStore hook in lib/use-is-client.ts. - Resize listeners that mirror window.innerWidth into state become `useWindowWidth()` in lib/use-window-width.ts. Hero reads its prop directly instead of copying it into state. - Forms seeded from query data, and state reset when an input changes, use the "adjust state during render" pattern guarded by the previous value, so the stale value never paints. - AdminLayout derives `loading` instead of storing it; the Stripe modal memoizes loadStripe; EventFormModal seeds the date in its initial state; verify uses window.location.assign; the judging page updates its ref in an effect instead of during render. - LazySection drops the IntersectionObserver-missing fallback (every supported browser has it) and the hacklytics countdown seeds its first tick from the initial state. - AttendeesTab: drop a useless initial assignment (no-useless-assignment).
- Admin judging: move the selection ref and prep generation in the click handler instead of a post-render effect. Between render and that effect, a prep run for the previous edition still passed stillThisRun and could land its result on the newly selected edition's panel. Re-selecting the current edition is now a no-op so it cannot orphan its own run. - ResumePreview: remount per src via key instead of keying state by src. Going A -> B -> A before B loaded matched A's retained blob URL, which the A -> B cleanup had already revoked, and showed a broken PDF.
Upgrade eslint stack and fix React Compiler lint errors
Dependency ReviewThe following issues were found:
|
|
Visit the preview URL for this PR (updated for commit f125b60): https://hacklytics2027--pr-416-k3clc3ft.web.app (expires Thu, 01 Oct 2026 19:48:07 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: c48ba34db61581e25fe2978355160b5eefe0e83f |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f125b60. Configure here.
| const [timeLeft, setTimeLeft] = useState(getTimeLeft); | ||
| // False for the static HTML and hydration, true after: the build-time | ||
| // countdown would be stale by the time anyone loads the page. | ||
| const mounted = useSyncExternalStore(subscribeNoop, () => true, () => false); |
There was a problem hiding this comment.
Stale countdown after hydration
Medium Severity
After hydration, mounted flips true immediately while timeLeft still holds the server/build snapshot. The interval never ticks right away, so the hero countdown can show a stale value (possibly days off on the static export) for up to a second before jumping to the real remaining time.
Reviewed by Cursor Bugbot for commit f125b60. Configure here.


Automated PR tracking changes from
rebase/deps-minorintodev.Note
Medium Risk
Wide dependency upgrades (especially Zod 4 and payment/Stripe input parsing) plus many React state-timing refactors could surface subtle validation or race regressions despite mostly mechanical changes.
Overview
This PR rolls minor/patch dependency upgrades across the monorepo—Node 22 in CI, TypeScript 7, Next/React 19.3, Vitest 5 + Vite 7, Zod 4, and refreshed tRPC, Stripe, Drizzle, ESLint, and Tailwind stacks—and adjusts code where those bumps require it.
Backend / validation: Shared schemas move to Zod 4 (
createPaymentIntentuses.prefault({})instead of.default({})). Router tests use RFC4122-style UUIDs so stricter UUID parsing still passes. Email sign-in tolerates Nodemailer 10 by treatingresult.pendingas optional when checking send failures.Frontend (React Compiler / hydration): Portal and marketing sites drop the
useEffect→setMounted(true)pattern in favor ofuseIsClientanduseWindowWidth(useSyncExternalStore). Several flows now reset or prefill state during render (settings/submit forms, admin resume filters, club scanner, hackathon edit forms, judge queue seeding) to avoid stale UI and cascading renders. Admin judging routes hackathon changes throughselectHackathonso in-flight prep cannot apply to the wrong edition. Smaller fixes include Stripe.js loaded viauseMemo, verify usinglocation.assign, and resume preview remounting persrc.Hacklytics 2027: Countdown hydration uses
useSyncExternalStore;LazySectionno longer special-cases missingIntersectionObserver.Reviewed by Cursor Bugbot for commit f125b60. Bugbot is set up for automated code reviews on this repo. Configure here.