NBA Feed
Your teams' news and games, with scores hidden until you reveal them.
Explore the story of any tennis match, star the ones you care about to build your own shareable story, and look up the records behind them. Launching before the 2027 Australian Open.
Sports & fitnessTypeScript · CSSGitHub @isaacslavens-beepOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A free, simple diary for the tennis you watch: explore the story of any tour-level singles match since 2003, star the ones you care about, read your saga back, look up the records in Statistics, and play with models of the numbers in Interact. Live at courtsaga.com.
main.drizzle/.scripts/import-matches.ts.| Path | What it is |
|---|---|
app/page.tsx, app/hero*.tsx | The landing page. Its demos run on a sample log and save nothing. |
app/demos/ | The sample log and its hand-checked rivalry records, for the demos only. |
app/diary/ | The log, ranking and saga, used by both the demos and real diaries. library.ts holds the shared types and rules, books.ts the saga's rules, store.tsx the state and saving, and finder.tsx match search. |
app/(app)/ | The app's pages (/explore, /saga, /statistics, /interact, /account) and the server actions that keep a diary. Explore opens signed out only for a shared match, with nowhere to go from it. |
app/diary/records.ts, lib/records.ts | Statistics: every board's rules, and the loader that reads a tour's matches and builds them. The pages are prerendered and rebuilt daily. |
app/diary/models.ts, lib/models.ts | Interact: models fitted to a tour's matches (the top 10 at any moment, upset odds, Elo ratings across eras, serving, big points under pressure, ages), and the loader that fits them. Built ahead of time and rebuilt daily, like Statistics. |
app/(auth)/ | Sign up and log in. |
app/s/[slug]/ | A shared saga: read-only, kept out of search engines. |
app/api/ | Better Auth's endpoints and GET /api/search. |
lib/ | Accounts (auth.ts), diary and search queries (diary.ts). |
db/, drizzle/ | The schema and database client, and the migrations generated from them. |
scripts/ | migrate.ts and import-matches.ts. |
proxy.ts | Sends signed-in fans from / to their log, and everyone else from diary pages to log in. |
npm installunaccent and pg_trgm extensions. A Neon branch works, and so does
local Postgres 15 or later..env.example to .env.local and set DATABASE_URL and BETTER_AUTH_SECRET
(openssl rand -base64 32).npm run db:migrate creates the tables.npm run import:matches -- ~/Desktop/courtsaga/tml-data loads the matches, in about ten seconds.npm run dev, then open http://localhost:3000.The import reads YYYY.csv (ATP) and YYYY_wta.csv (WTA) from a folder, every row of every year it
finds. The CSVs are never committed.
2026.csv and 2026_wta.csv in the folder and run the import again. No code
changes.tour_tourneyId_round_winnerId_loserId (for example atp_2025-540_F_S0AG_A0E2). They
never change between imports, because diary entries point at them.tourney_date is a tournament's first day, so a match's month is estimated from how far into
the event its round falls. The season always comes from the file name.The data is TML-Database (non-commercial use) built on Jeff Sackmann's tennis data (CC BY-NC-SA 4.0). CourtSaga credits both in its footers. Keep the site non-commercial (no ads, no paid tier) unless you get permission, and don't publish the raw data.
A push to main deploys to production. Vercel runs npm run vercel-build, which applies any new
migrations before next build. That only happens on production builds; previews skip it, so a branch's
migrations never reach the production database before it's merged. Every setting is in .env.example.
| Script | What it does |
|---|---|
npm run dev / build / start / lint | The usual Next.js commands. build needs no database, but without one the prerendered pages (the landing page's demo and Statistics) say the match data isn't available. |
npm run db:generate | Writes a new migration to drizzle/ after a change to db/schema.ts. |
npm run db:migrate | Applies migrations to DATABASE_URL_UNPOOLED (or DATABASE_URL). |
npm run import:matches -- <folder> | Loads or refreshes the match data. |