Repository navigation
Releases: Clubedge/clubedge-starter
Release list
v0.15.0
A docs-style dashboard
The dashboard in both apps now has the layout and colors of the Clubedge docs (#40):
- Layout. The sidebar sits on the page background, and the page sits beside it in a rounded card that scrolls on its own, with a thin scrollbar. The sidebar collapses with its button or Ctrl/⌘+B, and the dashboard remembers it.
- Sidebar. A section switcher at the top (Dashboard, plus Settings when an auth provider is selected), the dashboard's sections with the one in view highlighted as you scroll, links to the setup guide and the repository, and an account card at the bottom with the user's email and Settings and Sign out buttons.
- Top bar. Home and GitHub links, and a menu with a light and dark switch.
- Phones. A slim bar at the top opens the sidebar as a drawer from the right. The drawer closes by itself if the window grows to the desktop layout.
- Colors. The docs' neutral palette with the Clubedge blue, scoped to the dashboard:
apps/web/src/app/dashboard/_components/dashboard-palette.cssfor Next.js and the end ofapps/start/src/styles.cssfor TanStack Start. Other pages keep the@clubedge/uitheme, which is unchanged.
If you changed the dashboard
dashboard-shell.tsxreplacesapp-sidebar.tsxin both apps, andaccount-button.tsxis now the sidebar's account card.pnpm clubedge upgrademerges the new files with your edits and marks the lines both sides changed.- The dashboard page passes the switcher entries and the account card to the shell; edit them there.
Tests
- The settings browser test opens the settings page from the sidebar's account card.
Create a project with it using CLI 0.17.0 or later, and update an existing one with pnpm clubedge upgrade.
v0.14.0
Account settings
A settings page at /settings, linked from the dashboard, when an auth provider is selected (#38):
- Profile. Users set or clear a display name.
- Password. Changing it needs the current password, and signs out the user's other sessions with both Better Auth and Supabase.
- Delete account. It needs the password, and removes the user's profile and the data that depends on it before the auth account goes. If that cleanup fails, the account is kept so the user can try again. With payments, an active subscription has to be cancelled first. Supabase needs its secret key for this: set
SUPABASE_SECRET_KEY. - Password checks are limited to 10 a minute per user, and are refused while the rate limiter is unavailable.
Browser errors and source maps
When observability is on, errors in the browser are reported like server errors (#37):
- What is reported. Uncaught errors, unhandled promise rejections, and render errors stopped by an error boundary. New error pages (
app/error.tsxfor Next.js, the root route'serrorComponentfor TanStack Start) show a "Try again" message and report the error. - Where it goes. With
otel, the browser posts reports to/api/client-errors, which keeps only the expected fields, drops query strings, caps their size, and rate-limits each address. Withsentry, the page loads Sentry's browser SDK only when a DSN is set; the settings are read at runtime, not baked into the build. - Source maps. Sentry builds with
SENTRY_AUTH_TOKEN,SENTRY_ORG, andSENTRY_PROJECTset make source maps for the browser bundle, upload them, and delete them so they are never served.
Auth emails through the jobs queue
With both email and background jobs selected, verification and password-reset emails are sent by a send-email job with retries and backoff, so a slow or failing email provider never delays sign-up (#36).
Tests
- Unit tests for the new auth provider methods (password change, account deletion), the profile helpers on PGlite, the client error report parser, the bounded request-body reader, and the source map upload.
- Browser tests for the settings page with Better Auth and Supabase, and for browser error reports reaching a fake collector.
- CI prints the page snapshot of a failed browser test and uploads its trace.
Also
- Pages hydrate in development on
127.0.0.1(allowedDevOrigins). - Links styled as buttons use
buttonVariants, as the shadcn docs recommend, instead ofButtonrendering a link.
Create a project with it using CLI 0.16.0 or later, and update an existing one with pnpm clubedge upgrade.
v0.13.1
Fix
- In projects with background jobs or observability, a configuration error at startup, such as a missing or invalid
DATABASE_URLor an invalidOTEL_*orSENTRY_*value, no longer stops the server. Before, every page answered "Internal Server Error", the landing page and/api/healthincluded. Now the error is reported once, the jobs worker or observability stays off, and pages that need no database keep working. (#33)
Tests
- Unit tests for the jobs and observability startup hooks with invalid settings.
- CI now starts every generated project with an empty
DATABASE_URLand requires/api/healthand the landing page to answer.
Existing projects get the fix with pnpm clubedge upgrade once CLI 0.14.1 is published.
v0.13.0
Observability
A new optional module, observability, with the options otel, sentry, and none (default):
- Hooks in
@clubedge/core.reportError,traced, andwithSpanshave no dependencies and default to the console. The rest of the app calls only these, so the vendor SDK stays in its provider package, andnoneremoves it completely. - Two providers.
otel: OpenTelemetry over OTLP/HTTP to any collector or vendor. Traces go to/v1/traces, and errors go to/v1/logsas ERROR log records and are recorded on their span.sentry:@sentry/node11. Spans from Next.js and pg-boss become Sentry spans, and no user details, cookies, query strings, or request bodies are sent.
- What is traced and reported.
- Every request, plus calls through the email, storage, and payments composition roots (
email.send,storage.upload, and so on). - Server errors from pages, routes, server actions, and server functions, along with handled errors and failed background jobs. Each error is reported once, with the request's method and path.
- Every request, plus calls through the email, storage, and payments composition roots (
- Startup. Next.js uses
instrumentation.tswithonRequestError. TanStack Start uses a Nitro plugin plus request and server-function middleware. - Nothing is sent until configured: set
OTEL_EXPORTER_OTLP_ENDPOINTorSENTRY_DSN.
Tests
- Unit tests use in-memory exporters for OpenTelemetry and an in-memory transport for Sentry.
- A browser test checks that request traces reach a fake collector and a fake Sentry, for both frameworks.
Also
- Existing
console.errorcalls in the apps and in Better Auth now go throughreportError, and failed job attempts are reported with the job's name.
Not yet included: browser error reporting, and Sentry source maps.
Create a project with it using CLI 0.14.0 or later: pnpm dlx @clubedge/create-clubedge-app my-app --observability otel.
v0.12.0
Background jobs
A new optional module, jobs, with the options pg-boss and none (default):
- Jobs in your PostgreSQL. pg-boss stores jobs in the project's own database and manages its tables in the
pgbossschema, so there is no new service. - A worker inside the app server.
instrumentation.tsstarts it in Next.js, and a Nitro plugin starts it in TanStack Start, inpnpm devand in production. No deploy target needs a second process. - Retries, timeouts, and schedules come from each job's definition (
defineJob), with backoff and cron expressions. - Serverless hosts. On Vercel and Netlify the worker is off, and a scheduler calls
GET /api/jobs/runwithCRON_SECRETto run the jobs that are due. - Dashboard card. Queues a sample job and lists the user's recent runs.
- Provider-neutral. Job definitions, routes, and the card depend on the
JobQueueinterface in@clubedge/jobs, so another queue can be added without a refactor.
Tests
- Unit tests run pg-boss's real SQL against PGlite.
- A browser test queues the sample job and waits for the worker, against Docker, the Supabase stack, and the no-Docker PGlite server, for both frameworks.
Also
- TanStack Start's
vite.config.tssets Nitro'sserverDirto./src/nitro, so Nitro runs any plugins insrc/nitro/pluginsat startup. - ROADMAP.md marks phases 7 to 9 done and splits Phase 10 into 10.1 (jobs) and 10.2 (observability).
Create a project with it using CLI 0.13.0 or later: pnpm dlx @clubedge/create-clubedge-app my-app --jobs pg-boss.
v0.11.0
Highlights
-
Payments.
create-clubedge-app0.12.0 adds--payments stripe(it needs an auth module), andpnpm clubedge add payments stripeadds it to an existing project:- A billing card on the dashboard: checkout for each plan, then the plan, its status, and its renewal date, and "Manage billing" opens Stripe's customer portal.
/api/webhooks/payments: verifies every webhook's signature, applies each event once, and reads the subscription's current state from Stripe. Retried and out-of-order deliveries are safe.- Plans are defined in
src/config/plans.tswith the app's own ids, and each maps to a Stripe price (STRIPE_PRICE_<PLAN>). - Access checks: server code calls
getBilling().subscriptionFor(userId)andisEntitled.
-
Provider-neutral by design.
@clubedge/paymentsholds thePaymentProviderinterface, the billing logic, and thesubscriptionsandpayment_eventstables, with their own migrations. Every row records its provider.@clubedge/payments-stripeis the only Stripe code, and each app'ssrc/server/payments.tsis the only file that names it. Another provider is a new package implementing the interface; billing logic, tables, routes, and pages stay the same. -
Tested end to end. CI runs the whole flow in both frameworks with a real database:
- sign up and start checkout;
- a signed webhook is applied once, a duplicate changes nothing, and a forged one is refused;
- the dashboard shows the plan;
- the billing portal opens.
It uses a stand-in for Stripe's API, with webhooks signed exactly as Stripe signs them.
Other changes
upsertUserProfilereturns the user's id, andensureUserIdreads before writing.- TanStack Start's cross-site check skips
/api/webhooks/, whose handlers verify signatures. check:serviceschecks the Stripe key with a read-only request.- The production migrator from v0.10.0 applies the payment migrations automatically.
Compatibility
Use create-clubedge-app 0.12.0 or newer to scaffold this release.
v0.10.0
Highlights
-
Deployment module.
create-clubedge-app0.11.0 adds--deploy(also available later withpnpm clubedge add deploy <option>):Option Adds Migrations docker(default)The production image, for any container host A one-off node /app/migrate.mjstask before rolloutflyfly.tomlThe release command, before the new version replaces the old railwayrailway.jsonThe pre-deploy command renderA render.yamlBlueprint listing the project's variablesThe pre-deploy command (paid instance types) vercelapps/web/vercel.jsonDuring the build netlifyapps/web/netlify.tomlDuring the build TanStack Start builds for Vercel and Netlify without code changes, because its build detects the platform. SETUP has a guide per platform and a production variable list matching the project's modules.
-
A migrator in the production image.
node /app/migrate.mjsapplies every package's migrations with onlyDATABASE_URL, so platforms can migrate before a release goes live. It includes only the migrations of the project's modules, and skips migrations already applied. -
A manual deploy workflow.
.github/workflows/deploy.ymldeploys on demand with the chosen platform's tooling. Withdocker, it publishes the image to the repository's GitHub Container Registry (ghcr.io/<owner>/<repository>), without any secret. -
Readiness endpoint.
/api/readyreturns 200 when the database answers and 503 otherwise./api/healthstays the liveness check, so a database blip doesn't restart the app.
Tested
CI runs the image's migrator twice against PostgreSQL. It also checks each platform's config, builds TanStack Start with the Vercel and Netlify presets, lints every platform's deploy workflow with actionlint, and checks /api/ready with and without a database.
Compatibility
Use create-clubedge-app 0.11.0 or newer to scaffold this release.
v0.9.0
Highlights
-
Email module.
create-clubedge-app0.10.0 adds--email:Option What the project gets smtp(default)Any provider's SMTP endpoint ( SMTP_URL), or a local inboxresendResend's HTTP API ( RESEND_API_KEY), without an SDKnoneNo email packages, files, or variables Server code calls
email.send({ to, subject, text, html })from@/server/email.- Without a provider configured: development prints emails, links included, to the server log, and production refuses to send.
- Local inboxes:
--infra dockeradds Mailpit (inbox at http://localhost:8025), and--infra supabaseroutes the app's email to the stack's inbox. pnpm check:services: verifies SMTP or the Resend key without sending anything.
-
Password reset. "Forgot password?" works with both auth providers:
- Better Auth emails a one-hour, single-use link through the email module.
- Supabase sends its own email.
- The form answers the same way for every address, and response times don't reveal whether an account exists.
-
Email verification for Better Auth. New accounts get a confirmation link.
BETTER_AUTH_REQUIRE_EMAIL_VERIFICATION=truerefuses sign-in until the link is opened. -
Tested with real emails. CI reads the messages from Mailpit and from the Supabase inbox, then runs password reset (and required verification for Better Auth) in the browser for both frameworks.
Fixes
- Session lost after the auth callback (Next.js). The callback redirected to a URL built from
request.url, which can name a different host than the browser used (for examplelocalhostfor127.0.0.1in development). The session cookies it had just set didn't reach that host, so Supabase confirmation and reset links lost their session. The callback now redirects with a relative path.
Compatibility
Use create-clubedge-app 0.10.0 or newer to scaffold this release. It reads --email from this Starter's manifest.
v0.8.0
Highlights
-
pnpm clubedgein every project. A root script runs the latestcreate-clubedge-appinside the project:pnpm clubedge add # choose a module and option from a list pnpm clubedge add auth better-auth # add a module or switch its option pnpm clubedge remove storage pnpm clubedge upgrade # move to the newest Starter release
These commands merge the change into your code instead of overwriting it. Files you never touched are updated, your edits are kept, and lines you both changed get conflict markers. They need a clean Git tree, so
git diffshows the result. The README's command section describes them. -
Windows unit tests. The maintainer workflow runs the typecheck and unit tests on Windows, covering the local PGlite server, folder storage, and scripts.
Compatibility
Use create-clubedge-app 0.9.0 or newer to scaffold this release. The script calls @latest, so a project's pnpm clubedge also uses the newest CLI.
v0.7.0
Highlights
-
No-Docker local mode.
create-clubedge-app0.7.0 adds--infra local, so a project runs its development services with nothing installed:pnpm dlx @clubedge/create-clubedge-app my-app --infra local --auth better-auth cd my-app pnpm dev # starts the app and a local PostgreSQL, and applies the migrations
Service Local mode PostgreSQL packages/local-dbruns PGlite, PostgreSQL compiled to WebAssembly, and serves it over the PostgreSQL protocol on127.0.0.1:5433. The app, migrations, and Drizzle Kit keep usingDATABASE_URL. Data is kept in.data/pglite.File storage Uploads go to .data/storagewhileSTORAGE_DIRECTORYis set (@clubedge/storage/file-system).Cache Process memory. pnpm infra:upruns the database on its own, andpnpm infra:resetdeletes.data/.pnpm check:serviceschecks the database and the folder. Better Auth works fully offline; Supabase Auth and Supabase Storage still need a Supabase project. -
Safe with several connections. PGlite has a single session, so the local server makes clients take turns: a client keeps the session for a whole request (through Sync, including across Flush) and while its transaction is open. A client that disconnects mid-transaction has it rolled back. The off-the-shelf PGlite socket server can mix up statements from concurrent connections, so the Starter ships its own small server, tested against postgres.js.
-
Tested end to end in CI. For both frameworks, local-mode projects start the database, migrate, pass
check:services, and complete a Better Auth sign-up, sign-out, and sign-in in the browser. A restart then confirms that the data was kept.
Changes
- Folder storage safety. Uploads stream to a temporary file and are renamed when complete, and keys that would leave the folder (including through symbolic links) are rejected.
- Every infra option sets
dev. A manifest test guards core scripts against removal by the CLI.
Compatibility
Use create-clubedge-app 0.7.0 or newer to scaffold this release. Older CLIs don't offer --infra local, and without 0.7.0's lockfile pruning every generated project would also download PGlite.