Repository navigation
perf(mainweb): ship the rest of the portal's first data with the HTML - #582
Merged
Merged
Conversation
The list drew only after hydration plus a tRPC batch. A server page now reads the list, the visitor's registrations and projects and hands them to the unchanged client page; its dates print on the venue calendar so the server (UTC) and the browser agree. Cold load to the first card, 5 runs, 1440px, median: 436 to 334 ms (warm 190 to 158).
The page waited on the registrations, then on the submission, the event and the submission window for the event it picked. The server page reads the registrations, picks the same event (the URL's, else the first still open), and reads the other three with it. PrefetchedPage accepts an async reads function for this. Cold load to the submission notice, median of 5: 878 to 488 ms (warm 412 to 232).
…mber pass The dashboard's next step waited on the registrations and then up to four more queries; the server page works out which the client will ask for (the same checks as the page) and reads them. The club page also read member.myPass after load; it is prefetched now. Cold load median of 5: dashboard checked-in hacker 1247 to 530 ms (warm 420 to 279), club pass QR 514 to 290 (warm 203 to 150).
… list Each drew its list only after hydration plus a tRPC batch. Server pages read events.listAll, hackathon.listAll and the bans list for the unchanged client pages, and the dates they print move to the venue calendar. Cold load median of 5: /admin 493 to 308 ms, /admin/attendees 452 to 393, /admin/hackathons 1113 to 359.
…cations The dashboard waited on the edition, then on the applications page and the stats. The server page reads the edition, the first page of applications and the analytics. The dates in the applications table move to the venue calendar, and the registration controls (a deadline and inputs in the organiser's own time zone, which the server cannot know) are drawn after hydration so both renders agree; without that gate React threw #418. Cold load median of 5: 672 to 480 ms (warm 315 to 189).
The edition console waited on the overview and then the checklist. Server pages read the edition list, the overview and the checklist. shortTime moves to the venue clock so server and browser print the same times. Cold load median of 5: /admin/judging 560 to 365 ms, an edition 963 to 495.
Attendance and the workshop table drew after hydration plus a tRPC batch. A server page reads both for the current term and the session dates print on the venue calendar. Cold load median of 5: 858 to 613 ms (warm 321 to 251).
… sign-up Each page held a loading screen until its first tRPC batch returned. Server pages read the first reads of lead, lead/[id], initiatives, subteams, club/bootcamp and judge/register for the unchanged client pages. Cold load median of 5: lead 582 to 369 ms, a project 695 to 432, initiatives 437 to 301, subteams 643 to 475, club/bootcamp 621 to 392, judge/register 699 to 419.
Every search on the admin roster answered 500: "operator does not exist: uuid = text". The filter is shared by the paged roster, which runs as a relational query (db.query.hackathonParticipants.findMany), and inside that Drizzle renders every column in a raw sql template against the participants alias. The user-name and email subquery therefore compared "hackathonParticipants"."id" (uuid) with its own user_id (text), and read name and email off the participants alias too. The user table now has a hand-written alias in that subquery. Checked on a production build against the local Docker database: "example.test" matches the 8 seeded attendees through their user email, "casey" 1, "zzzz" 0.
Contributor
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Contributor
|
Visit the preview URL for this PR (updated for commit 83197e4): https://hacklytics2027--pr-582-frbtwl8r.web.app (expires Fri, 13 Nov 2026 17:49:49 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: c48ba34db61581e25fe2978355160b5eefe0e83f |
This was referenced Nov 6, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finishes the signed-in portal speed work from #580: the remaining pages now ship their first data with the HTML instead of fetching it after hydration. Also fixes a pre-existing 500 on the admin roster search.
Changes
PrefetchedPageserver wrappers (same pattern as perf(mainweb): ship the signed-in portal's first data with the HTML #580) for the hackathons list, submit, admin home, admin attendees, admin hackathons and one hackathon's admin dashboard, admin judging and an edition's console, admin bootcamp, lead andlead/[id], initiatives, subteams,club/bootcamp,judge/register.PrefetchedPagetakes an asyncreadsfunction for pages whose later reads depend on a first one (submit, dashboard,lead/[id], admin hackathon); a throw there costs the prefetch, never the page.team.submissionWindow,team.mySubmission,judging.hacker.itinerary,hackathon.getResults); club prefetchesmember.myPass.stripe.attemptAutoLink(a mutation) is untouched.lib/venue-time(America/New_York) so server and browser agree.RegistrationControlsprints in the organiser's own zone, which the server cannot know, soAttendeesTabdraws it after hydration.admin/membersskipped: it reads nothing until the admin types.hackathon.adminGetAttendeeswith a search term returned 500 (uuid = text). Inside the relational query Drizzle rendered the user subquery's columns against the participants alias; the user table now has its own alias.proxy.tsprivate no-store caching, schema.Measurements
Production build, server
TZ=UTC, local Docker test DB, signed in, Playwright time until the page's content is visible, 5 alternating runs, median. Busy machine.Testing
hackathons/[id]as a signed-in non-member (all tabs, no register button). Camera scanning could not be driven.tsrs --noEmit, oxlint, vitest (364 + 4 tests).Note
Medium Risk
Large-scale PrefetchedPage refactors across admin surfaces can misalign with client hooks, while the Drizzle alias change touches attendee search SQL; both need regression verification.
Overview
Summary
Extends the portal’s PrefetchedPage wrappers so dozens of admin, club, judging, and hackathon routes hydrate with their initial tRPC payloads already in the HTML; dashboard/club pages now chained-prefetch follow-up reads (next-step hackathon queries and member pass). Shared portal components date rendering was tuned to venue time helpers, hackathon attendee tooling waits for client hydration before time-zone-sensitive controls, and bootcamp/admin UIs picked up venue formatting tweaks.
Bugfix
Hackathon admin attendee search now aliases the nested
userstable so Drizzle stops emittinguuid = textcomparisons that broke searches with 500s.Risk
Medium: broad server prefetch orchestration plus SQL aliasing regressions could surface if query keys diverge from client hooks or prefetch logic mis-targets user state.
Reviewed by Cursor Bugbot for commit 83197e4. Bugbot is set up for automated code reviews on this repo. Configure here.