mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
032c71c5bfa1f4425d2f93072e28fa7d4e9a8321
100
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
38b74af3f1 |
fix(studio): fall back for framework icons without an asset (#51065)
I made the connected-project framework icons fall back when no shipped SVG exists for a framework. The three icon sites built `/img/icons/frameworks/<framework>.svg` straight from the integration's framework preset, which is an open-ended string. They only fell back when the value was empty, so any preset without an asset (`express`, `hono`, `fastapi`, `tanstack-start` and others) showed a broken image and logged a 404. **Changed:** - **Broken framework icons**: `getFrameworkIconUrl` returns the asset URL only for slugs in a set that mirrors `public/img/icons/frameworks/`. The integration connection row, the org project linker and the marketplace project picker now show their existing fallback icon for any other slug. A test keeps the set equal to the directory listing. - **Framework type**: I deleted the hand-kept `VercelFramework` union. It listed exactly the shipped icon slugs, while the API types the field as `string | null`, and that mismatch is what made the old empty-only check look safe. **Note:** I rejected an `onError` fallback because the browser still sends the 404 request. Adding logos for common presets is left for design. ## To test Tested on Vercel preview (staging): no real connection there uses these presets, so I rewrote the org integrations response in the browser to give one integration four connections. - [x] Open an org's Integrations page with connections whose framework has no shipped icon (`express`, `eve`, `tanstack-start-lovable`). Expect the fallback badge and no request under `/dashboard/img/icons/frameworks/` for those slugs. Observed: all three rows showed the badge and the network log had no request for their SVGs. - [x] Same page with a `nextjs` connection. Expect its framework logo. Observed: `nextjs.svg` loaded with a 200. - [x] Same page with the real, unmodified response (one connection with `framework: null`). Expect the badge, no frameworks requests, and no new console errors. Observed: as expected. ## Linear - fixes GROWTH-1309 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Vercel integration and project views now display framework icons when available and fall back to the Vercel icon when no matching icon exists. * Framework metadata now supports values beyond a fixed list, while unsupported frameworks continue to use the fallback icon. * **Tests** * Added coverage for supported and unsupported framework icons, including base-path handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9690efeb42 |
fix(vault): link to current dashboard route (#51058)
I updated first-party Vault links to open the current secrets route directly. Wrapper credentials, extension metadata, and blog posts still linked to the retired path and relied on a redirect. ## To test On the preview: - [x] Inspect a Wrapper credential's Vault link. Expect `/integrations/vault/secrets` with a `search` query for that credential. - [x] Open the inspected target URL. Expect Vault to show the matching secret. - [ ] Click a Wrapper credential's Vault link. Expect the filtered Vault view. - [ ] Open the pgsodium extension's Vault link and a Vault blog link. Expect `/integrations/vault/secrets` without the retired route in the address bar. ## Linear - fixes GROWTH-1312 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Updates** * Vault secrets links now direct you to the project’s Integrations page, including links from wrapper metadata, blog articles, and the `pgsodium` extension listing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
99c6e306c7 |
fix(www): repair legacy reference redirects (#51059)
I repointed legacy reference redirects and first-party links to the sections the Docs app currently serves. Old client and common-filter URLs still targeted bare slugs, while SDK landing URLs targeted `/start`. I preserved v1 auth destinations and linked the retired Dart v0 migration guide to its original source. ## To test On the www preview: - [x] Request `/docs/client/order` with a crawler user agent. Expect a redirect to `/docs/reference/javascript/using-modifiers-order`. - [x] Request `/docs/common/filters/_sl` with a crawler user agent. Expect a redirect to `/docs/reference/javascript/using-filters-rangelt`. - [x] Request `/docs/reference/kotlin` with a crawler user agent. Expect a redirect to `/docs/reference/kotlin/introduction`. - [x] Open the Storage permissions section. Expect its bucket reference link to target `/docs/reference/javascript/file-buckets-createbucket`. ## Linear - fixes GROWTH-1293 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated links across blog posts, product pages, and feature listings to point to current JavaScript, Flutter, and Dart documentation. * Updated legacy documentation redirects to current reference pages, including filter, modifier, client, and authentication guides. * Changed reference-root redirects to lead to each language’s introduction page. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e0e58f8814 |
fix(studio): redirect moved dashboard routes (#51056)
I added permanent redirects for the moved Dashboard routes that still send visitors to 404s. Project and organization identifiers carry through, while the old project billing path opens the organization picker for billing. **Note:** The bare `/dashboard/project` path redirects straight to Organizations instead of the `/dashboard/projects` hop named in GROWTH-1295, since `/projects` already redirects there. ## To test Tested on the Studio preview: - [x] Requested the eight old Dashboard paths in GROWTH-1295 while signed out. Each returned 308 with the specified destination. - [ ] Request bare `/dashboard/project` while signed out on the latest preview. Expect a single 308 to `/dashboard/organizations`. - [x] Requested a project backup path with a query string. The destination kept the project ref and query string. - [x] Requested `/dashboard/project/_`. The project picker remained reachable. ## Linear - fixes GROWTH-1295 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Added permanent redirects for legacy Studio routes covering account and project pages, backups, email templates, edge-function logs, secrets, and billing settings. * Redirects preserve incoming query parameters and URL fragments; the project selector remains unaffected. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
07c75e84fc |
fix(docs): repair internal content links (#51050)
I fixed stale links in six Docs pages. Guide links now include the Docs base path, and links to the old Database Hooks route point directly to the Dashboard Webhooks page. ## To test - Open the Logs ingest guide in the Docs preview and follow the updated guide links. Each destination should load. - Open each affected Docs page in the preview and follow its Webhooks link. The Dashboard Webhooks page should load after sign-in. ## Linear - fixes GROWTH-1301 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated webhook setup and troubleshooting links to point to the Integrations Webhooks dashboard. * Updated Postgres configuration and log-setting links to use current documentation paths. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
10445cee8e |
fix(www): redirect bare dashboard paths (#51052)
I added permanent www redirects for inbound Dashboard URLs that currently reach the www 404 page. The project rule requires a path segment and carries the full project suffix to Dashboard. ## To test - In the www preview, open `/login`, `/support/new?category=billing`, and `/account/tokens`. Each should redirect to its corresponding `/dashboard` page and preserve the query string. - Open `/project/<ref>` and `/project/<ref>/database/migrations?source=docs`. Both should redirect to the same path under `/dashboard`, preserving the suffix and query string. - Open bare `/project`. It should remain outside the new redirect rule. ## Linear - fixes GROWTH-1302 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Other Changes** * Visiting `/login`, `/support/new`, or `/account/tokens` now redirects to the corresponding dashboard page. * Project links redirect to the matching dashboard project page. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4a941518e3 |
feat(studio): track Explorer runs and saves (#51004)
I added outcome events for Explorer query runs and successful manual notebook saves. Existing page visits and preview toggles do not show whether users complete queries or persist notebooks. **Changed:** - **Query usage:** Accepted runs from query tabs and notebook cells emit submitted and terminal outcome events with a shared run ID. Canceled confirmations emit no run events. - **Notebook adoption:** Successful manual saves emit created or updated events. Recreated notebooks count as creations. Unsaved drafts and failed saves emit neither. - **Event metadata:** Explorer action events use `Explorer` as their page title. **Note:** Assistant-generated saves are outside this PR. Custom properties omit SQL and notebook content. Page visits still carry the browser title, which can include a notebook name. ## To test Tested on the staging preview: - [x] Run valid and invalid SQL from an Explorer query tab. Each run emits one submitted event and one matching completed or failed event with the same run ID. - [x] Run database and Logs notebook query cells, then add a markdown cell. The query cells emit matching event pairs; the markdown cell emits no query event. - [x] Save a new notebook, then edit and save it again. The successful saves emit created and updated events. - [x] Cancel a guarded query. It emits no query run event. - [ ] Recreate a notebook deleted on the server after local edits. A successful save emits created, not updated. - [x] Inspect an Explorer action event request. Its page title is `Explorer`; page visits still use the browser title. - [ ] Force a notebook save failure. It should emit no save event. This case was not tested manually. ## Linear - fixes GROWTH-1298 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Analytics** * Explorer query runs are tracked for database and log queries, including whether they complete or fail. * Query activity is associated with its location in Explorer, such as a query tab or notebook cell. * Successful notebook saves are tracked as creations or updates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com> |
||
|
|
177ef0cdd6 |
fix(www): repair broken docs links and redirect /blog/rss.xml (#51002)
## Problem Four www pages link to docs URLs that return 404: - `/storage`: the "Learn more" links for analytics and vector buckets (`storage/analytics-buckets`, `storage/vector-buckets`) - `/` (frameworks grid): the Vue quickstart (`quickstarts/vuejs`) - `/edge-functions`: the CI/CD link (`functions/cicd-workflow`; that page was merged into the deploy guide) - `/partners`: the Enterprise SSO link (`auth/sso`) Feed readers also request `/blog/rss.xml`, which renders the blog 404, while the feed lives at `/rss.xml`. Apart from prefetch requests, it is the most-requested 404 on www. ## Solution - Point each link at the live page: `storage/analytics/introduction`, `storage/vector/introduction`, `quickstarts/vue`, `functions/deploy#cicd-deployment`, `auth/enterprise-sso`. - Redirect `/docs/guides/functions/cicd-workflow` to the deploy guide. This also fixes the older blog post and changelog links to it. The redirect destination has no anchor because the `.md` redirect generator in `next.config.mjs` appends `.md` to the destination. - Redirect `/blog/rss.xml` to `/rss.xml`. ## To test On the www Vercel preview: - [x] On `/storage`, click "Learn more" under analytics buckets and vector buckets: expect the analytics and vector introduction pages. They navigate to `/docs/guides/storage/analytics/introduction` and `/docs/guides/storage/vector/introduction`. The www preview doesn't serve `/docs`, so I confirmed every new docs target returns 200 on production. - [x] On `/`, click Vue in the frameworks grid: expect the Vue quickstart. The Vue tab's "Read docs for Vue" link navigates to `/docs/guides/getting-started/quickstarts/vue`. - [x] On `/edge-functions`, click the CI/CD link: expect the deploy guide scrolled to CI/CD deployment. Opens "Deploy to Production" in a new tab with `#cicd-deployment` in view. - [x] On `/partners`, click the Enterprise SSO link: expect the enterprise SSO guide. Opens "Enterprise Single Sign-On" in a new tab. - [x] Request `/blog/rss.xml` and `/docs/guides/functions/cicd-workflow`: expect 308s to `/rss.xml` and `/docs/guides/functions/deploy`. Both return 308, and `/rss.xml` serves the feed as XML. - [x] `/docs/guides/functions/cicd-workflow.md` returns 308 to `/docs/guides/functions/deploy.md`. - [x] None of the four pages still links to an old path (`analytics-buckets`, `vector-buckets`, `quickstarts/vuejs`, `functions/cicd-workflow`, `auth/sso`). ## Linear - fixes GROWTH-1297 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated quickstart and integration links for Vue, Edge Functions CI/CD, analytics and vector storage buckets, and Enterprise SSO. * Added permanent redirects from the old RSS feed and Functions CI/CD guide URLs to their current locations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
138b654994 |
fix(studio): point Usage log ingest and query links at logs-* docs (#51001)
## Problem The org Usage page links added in #50570 point to `/docs/guides/platform/manage-your-usage/log-ingest` and `/log-query`. Neither page exists. The docs live at `logs-ingest` and `logs-query`, so every click from the Usage page lands on a 404. Within a day of #50570 shipping, these two paths were the top docs 404s by unique visitors. ## Solution - Point both Usage page links at the `logs-*` pages. - Add permanent redirects from the singular paths, because they're already being shared: search engines and AI assistants now send people to them. The existing generator in `apps/www/next.config.mjs` adds the `.md` variants. ## To test On the Vercel previews: - [x] Open an org's Usage page on the Studio preview and click the Log Ingestion docs link: expect the `logs-ingest` docs page, not a 404. Opens `supabase.com/docs/guides/platform/manage-your-usage/logs-ingest` in a new tab ("Manage Logs Ingest usage"). - [x] Click the Log Query docs link: expect the `logs-query` docs page. Opens `.../logs-query` in a new tab ("Manage Logs Query usage"). - [x] No docs link on the Usage page still points at the singular `log-ingest` or `log-query` paths. - [x] On the www preview, request `/docs/guides/platform/manage-your-usage/log-ingest` and `/log-query`: expect a 308 to the `logs-*` pages. Both return 308 to the matching `logs-*` path. The www preview doesn't serve `/docs`, so I confirmed both targets return 200 on production. - [x] `log-ingest.md` and `log-query.md` also return 308 to the matching `logs-*.md` paths. ## Linear - fixes GROWTH-1296 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Updated the Log Ingestion and Log Query documentation links to their current pages. * Added permanent redirects from the previous documentation paths to the corresponding pages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1716d87f59 | fix(studio): cap project name at 256 chars (#50804) | ||
|
|
64ab76262e | feat(studio): exhaustion banner links to metrics (#50276) | ||
|
|
66d4b4c19b | chore(studio): remove expired tos update banner (#50533) | ||
|
|
5e8e551e2c |
fix(www): include app routes in sitemap (#50277)
I added static App Router pages to the www sitemap, including the homepage, pricing, and product pages. The generator previously scanned only Pages Router and content files; it now strips route groups, excludes dynamic segments, and emits these URLs without lastmod. **Note:** The pre-existing Pages Router `/opt-out/[ref]` entry remains outside this change. ## To test Tested on the [www preview](https://zone-www-dot-com-git-pamela-growth-1214-app-rou-1d4879-supabase.vercel.app/sitemap_www.xml): - [x] Open `/sitemap_www.xml`: expect the homepage, `/pricing`, and product routes once each, without route-group names or lastmod on those entries. - [x] Compare the sitemap's changelog URLs with `/changelog-rss.xml`: expect every RSS item link to remain included, including text-slug entries. - [x] Open `/sitemap.xml`: expect the existing www and docs sitemap links. ## Linear - fixes GROWTH-1214 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Sitemap generation now includes static pages built with the Next.js App Router. - Route groups are correctly omitted from generated URLs. - Dynamic App Router routes are excluded from the sitemap. - **Bug Fixes** - Improved sitemap coverage and URL accuracy for applications using both App Router and Pages Router pages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f012dfa850 |
fix(www): sitemap lists all changelog slugs (#50275)
The www sitemap generator reads changelog URLs from the build-generated RSS feed but only accepted links whose slug starts with a number, the shape `computeChangelogEntrySlug` produces solely for entries carrying `legacy_gh_discussion`. Every changelog entry authored since that migration has a plain text slug and was silently missing from `sitemap_www.xml`. I widened the link match to any non-empty slug; the RSS builder is the only producer of that file and emits exactly one link per item, so no other filter is needed. I added a text-slug RSS item to the fixture-driven sitemap test and asserted that the changelog URL list equals the RSS item list, so a future filter that drops entries fails the suite. **Note:** text-slug entries now pass through the same fail-the-build pubDate check that numeric-prefixed entries already did after #50198. A changelog entry with no `publish_date` and no date-prefixed filename would produce an unparseable pubDate and stop the www build. The alternative, shipping the URL without lastmod, is a one-line change. I kept the gate because every current changelog entry carries a date-prefixed filename, the changelog repo documents that convention, and the build error names the entry URL. ## To test Tested on Vercel preview: - [x] Count `<item>` blocks in `<preview>/changelog-rss.xml`, then count `/changelog/` locs in `<preview>/sitemap_www.xml`, expect the two counts to match - [x] Search the preview sitemap for `/changelog/pipelines`, expect one `<loc>` entry with a `<lastmod>` date - [x] Search the preview sitemap for a numeric-prefixed entry such as `/changelog/47796-developer-update-july-2026`, expect it still present ## Linear - fixes GROWTH-1212 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Changelog pages with text-based slugs are now correctly recognized in the sitemap. - Sitemap entries for these changelog pages now use their RSS publication dates. - RSS links are matched more reliably, ensuring all valid changelog URLs are included. - Invalid publication dates are rejected instead of producing incorrect sitemap metadata. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e315fbcb53 |
feat(www): emit sitemap lastmod from content dates (#50198)
I added content dates to `sitemap_www.xml` and `dateModified` to blog
JSON-LD so crawlers can compare freshness with page metadata. Both use
`updated` when present, otherwise the publication date.
**Changed:**
- **Consistent dates:** I use the same frontmatter parser for the blog
page and sitemap. It preserves authored dates across quoting and
timezones and rejects JavaScript frontmatter.
- **Invalid dates stop the build:** I reject impossible calendar days,
out-of-range times and offsets, malformed dates, and `updated` before
publication. Content changes run the generator in CI.
- **Authoring:** I documented optional `updated` for substantive
revisions. Events, static pages, and `/evals` omit `<lastmod>`.
**Note:** Changelog dates come from RSS. Existing sitemap omissions for
nonnumeric changelog slugs (GROWTH-1212) and app-router pages
(GROWTH-1214) remain separate.
## To test
On the preview:
- [x] Open `/sitemap_www.xml`: blog, alternatives, customer stories, and
included changelog entries should carry `YYYY-MM-DD` lastmod values.
Verified on the
|
||
|
|
c6a1c2052c |
feat(www): cross-list openapi and mcp endpoint (#50180)
`/.well-known/ard.json` advertises the Management API OpenAPI spec and the MCP server, but `/llms.txt` listed neither and `/.well-known/api-catalog` listed only the Management API. I added both resources to the two surfaces that were missing them, so an agent finds the same spec and endpoint whichever discovery file it reads first. **Changed:** - **llms.txt gains an `## API and agent resources` section**: two described links, the same-origin `/openapi.json` spec and `https://mcp.supabase.com/mcp`, from a small list in `lib/agent-resources.ts`. A named heading rather than `## Optional`, since llmstxt.org defines Optional as links an agent may skip. The descriptions restate ard.json's on purpose; ard.json is curated to the ARD schema and stays untouched. - **api-catalog lists the MCP endpoint**: added as a catalog `item` plus its own linkset member carrying `service-doc` (the MCP guide) and `service-meta` (the OAuth protected-resource metadata the endpoint's 401 response already points at). - **Tests cover what the two files advertise**: `ard-catalog.test.ts` now parses api-catalog, checks that its `item` list and its anchored members agree, and runs every same-origin URL from api-catalog and the llms.txt resource list through the existing dead-URL resolver (public file, app route, rewrite, or docs guide). The www tests workflow now checks out `apps/docs/content/guides` (the directory the llms.txt route already reads at runtime) and runs on changes to it, so moving a guide that a catalog links to fails that PR rather than the next www one. **Note:** `/openapi.json` is an external rewrite served uncached on every request (338 KB). I tried `Cache-Control` and then the documented `x-vercel-enable-rewrite-caching` + `CDN-Cache-Control` pair on that path; the preview kept returning `x-vercel-cache: MISS`, so both are reverted. Caching the alias is a separate change. ## To test Tested on Vercel preview: - [x] `curl -s <preview>/llms.txt | tail -5`: expect an `## API and agent resources` heading followed by the OpenAPI spec link and the MCP server link; the diff against production `llms.txt` is those appended lines only - [x] `curl -s <preview>/.well-known/api-catalog | jq '.linkset[2]'`: expect a member anchored at `https://mcp.supabase.com/mcp` with `service-doc` and `service-meta`, served as `application/linkset+json` ## Linear - fixes GROWTH-1207 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added API and agent resource links to `llms.txt`, including the Management API specification and MCP server. - Added the Supabase MCP server to the API catalog with service documentation and metadata links. - **Tests** - Expanded catalog validation to cover API catalog entries, agent resources, and documentation guide links. - Updated pull request checks to run when guide content changes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5db8a0e960 |
feat(studio): instrument sign-in attempts and failures (#49853)
The /sign-in page emitted only a pageview on entry and the success-side `sign_in` event on exit: failed or abandoned attempts were invisible, so "never interacted" and "tried and failed silently" could not be told apart in the sign-in funnel. I added an unsampled `sign_in_submitted` event at every initiation point and classified failure capture via `dashboard_error_created` with a new `signin` origin. **Changed:** - **Submit attempts observable**: `sign_in_submitted` (method: `email`, provider id, `sso`, or partner) fires from the DOM submit handler on the password and SSO forms (so submits that fail client-side validation still count), and from the OAuth, custom-provider, and partner initiation handlers. - **Failures classified**: each sign-in error path feeds the existing funnel-error pipe with origin `signin` and a controlled reason slug (`invalid_credentials`, `email_not_confirmed`, `captcha_failed`, `sso_provider_not_found`, ...). GoTrue auth errors now classify via their numeric `status`, guarded so transport failures (`status: 0`) stay `network_error`. - **Attempt events survive the OAuth redirect**: the telemetry event POST sends with `keepalive` (scoped to `sign_in_submitted`, since keepalive requests share a per-page in-flight body quota), so a dispatched request is no longer aborted by the provider navigation; send rejections are caught centrally instead of surfacing as unhandled rejections. The fetch still dispatches after an async token lookup, so preview testing verifies the GitHub-path event actually lands on the wire. - **Captcha rejection is no longer silent**: a rejected hCaptcha challenge resolves the stuck loading toast with an error message, emits `captcha_challenge_failed` (distinct from `captcha_failed`, which stays reserved for the auth server rejecting a submitted token), reports to error monitoring, and resets the captcha widget (previously: unhandled promise rejection and a spinner that never resolved). - **Partner method validated**: the partner sign-in page resolves the URL-hash value against the provider registry and forwards the canonical provider id into `method` on both `sign_in_submitted` and `sign_in`; anything unregistered records as `unregistered_partner`, so a crafted link can't poison the breakdown on either event. **Note:** failure events stay on the shared 10% `dashboard_error_created` sampling rate (a per-origin carve-out would break cross-source volume comparability); the unsampled attempt event carries the tried-vs-never-interacted signal at full volume. ## To test Tested on Vercel preview (studio-staging, wire-level network capture + staging ingestion check): - [x] On `/sign-in`, submit a bogus email + password: expect a `POST */platform/telemetry/event` request with `action: sign_in_submitted`, `method: email` in the network tab, plus an error toast. Observed: 201, auth returned 400 as expected. - [x] Submit with an empty password: expect `sign_in_submitted` to still fire (validation failures count as attempts). Observed: event fired with 201 and no auth call followed. - [x] Click "Continue with GitHub": expect `sign_in_submitted` with `method: github` on the wire before the provider redirect. Observed: the POST completed (201) before the browser landed on github.com, so the keepalive path holds. - [x] Negative case: fresh page load with no interaction fires no `sign_in_submitted`. - [x] Ingestion: all fired events (methods `email`, `github`, plus organic `sso` submits from a real login on the same preview) arrived in the staging project with the expected properties. - [x] Re-ran the email and GitHub paths on the scoped-keepalive build (`129bf8d`): both `sign_in_submitted` POSTs returned 201 (the GitHub one completed despite the provider redirect), and both events ingested into the staging project with the expected `method`/`category` properties. ## Linear - GROWTH-1165 (no `fixes` keyword on purpose: the evidence checks run on prod data post-deploy, and the issue closes manually after they pass) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Improved sign-in protection with more reliable invisible CAPTCHA handling. * Added sign-in submission tracking across password, SSO, partner, custom OAuth, and external-provider flows. * Added detailed classification for authentication, validation, CAPTCHA, provider, and network errors. * **Bug Fixes** * Sign-in now stops safely and resets CAPTCHA when verification fails. * Improved error reporting for failed sign-in attempts, including redirects and OAuth flows. * Ensured sign-in telemetry is delivered reliably during OAuth redirects. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
121332c1ac |
feat(www): add service json-ld to homepage (#49849)
Follow-up to #49768. Agent-readiness scanners grade schema breadth by extended schema.org types (Service, FAQPage, Product); SoftwareApplication alone doesn't register, so I added a Service block whose offer catalog mirrors the products already rendered on the homepage. I also brought `/.well-known/api-catalog` up to the RFC 9727 API-catalog profile. **Changed:** - **Homepage emits Service JSON-LD**: new `serviceSchema` builder in `lib/json-ld.ts`; the offer catalog lists the six products the homepage products section renders (Database, Authentication, Storage, Edge Functions, Realtime, Vector). - **api-catalog leads with the catalog context**: `linkset[0]` now anchors the catalog URL and carries an `item` link to the Management API base, per the RFC 9727 profile; the existing service-desc context moves to `linkset[1]` unchanged. ## To test Tested on Vercel preview: - [ ] View source on the preview homepage: expect a fourth `application/ld+json` script with `"@type":"Service"` and six offerings - [ ] `curl <preview>/.well-known/api-catalog`: expect `linkset[0]` to contain an `item` array pointing at `https://api.supabase.com/v1` ## Linear - fixes GROWTH-1175 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added structured service information to the home page, including Supabase’s platform offerings. * Added an API catalog entry linking to the Supabase API endpoint. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
eba2aeb517 |
chore: remove stale references to the removed build:llms pipeline (#49848)
## What The `build:llms` script no longer exists in apps/docs (its output, `apps/docs/public/llms/*.txt`, is superseded by `apps/www/app/llms/[slug]/route.ts` serving the generated reference markdown directly). Four stale references remained: - `apps/docs/.gitignore`: removed the `public/llms/` entry and its comment referencing the dead script. Nothing writes to that directory anymore; if you have leftover local files there, delete them. - `apps/docs/spec/reference/README.md`: the react-server `tsx` warning cited `pnpm build:llms` as the consumer. Replaced with `pnpm embeddings`, a live script that runs under `tsx --conditions=react-server`. I verified the constraint still holds: importing `Reference.utils.ts` crashes under `--conditions=react-server` (in `next/navigation`) and loads fine under plain `tsx`. - `apps/www/pages/modules/vector.tsx`: the maintenance comment pointed at `public/llms/vector.txt`, which doesn't exist in www. The hand-maintained markdown sibling lives at `content/md/modules/vector.md`. - `.agents/skills/ask-the-docs/reference/llm-agent-parity.md`: the "In-flux / stale wiring" bullet asserted the exact `.gitignore` line this PR deletes (and its "generation path is unclear" caveat no longer holds; per-source links resolve live via `apps/www/app/llms/[slug]/route.ts`). Removed the bullet so the ask-the-docs skill doesn't report a gitignore entry that no longer exists. No behavior change; docs and comments only (plus a gitignore entry). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Updated the embeddings documentation to use the current `pnpm embeddings` command. - Clarified where vector module content should be maintained alongside the corresponding page. - Removed outdated references to generated per-source LLM files and retired documentation describing stale generation paths. - Improved consistency between reference documentation and the current content-generation workflow. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3338be76f0 |
fix(studio): emit sign_in on totp challenge (#49755)
The dashboard's `sign_in` event never fires when a user completes a TOTP challenge: `SignInForm` only tracks when no MFA challenge is needed, and the /sign-in-mfa page only tracks on mount when the assurance level is already satisfied (OAuth/SSO returns). Sign-ins that go through the actual MFA form were invisible to analytics, and the login audit event was missing on the same path. **Changed:** - **MFA-challenged sign-ins now tracked**: `SignInMfaForm` fires `sign_in` (reading the same `method` query param the page mount site reads) plus the login audit event on successful TOTP verification, in the sign-in context only. The forgot-password flow stays untracked: it is a reset, not a sign-in. - **Password+MFA sign-ins report `method: email`**: `SignInForm` now passes `?method=email` when routing to /sign-in-mfa instead of falling through to `unknown`. - **Partner TOTP sign-ins carry their provider**: `SignInPartner` now passes `?method=<partner>` when routing to /sign-in-mfa, matching the raw-provider-name convention the other entry points use. - **Join caveat documented**: the `SignInEvent` doc comment now notes the event is captured server-side and races the identify call, so it is not a valid funnel join key across the auth boundary. ## Linear - fixes GROWTH-1156 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added sign-in method details to MFA redirects for email and partner authentication, improving sign-in flow tracking. * Added telemetry and login auditing for successful MFA sign-ins while keeping forgot-password flows untracked. * **Documentation** * Clarified sign-in event tracking coverage, including OAuth providers, server-side capture, anonymous identifiers, and the sign-in page. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
3dddb60149 |
feat(www): publish agent discovery catalog and complete json-ld (#49768)
I added the agent-discovery surfaces the www app was missing: a resource catalog at `/.well-known/ard.json` plus completed structured data on the homepage. I scoped this from the agent-readiness gaps that are truthfully closable on the www side; the catalog lists only resources that already exist and serve 200 (MCP OAuth metadata, Management API OpenAPI spec, llms.txt, agent-skills index). **Changed:** - **Agents can discover our machine-readable resources from one document**: new static catalog at `/.well-known/ard.json` (Agentic Resource Discovery format); the legacy `/.well-known/ai-catalog.json` path serves the same file via rewrite, keeping a single source artifact. - **Organization JSON-LD carries verifiable company details**: adds `legalName`, a support `contactPoint`, and the registered address already public on our Terms of Service. - **Homepage declares the product as an application entity**: emits `SoftwareApplication` JSON-LD via the existing `softwareApplicationSchema` builder, same pattern as the vector module page. ## To test Tested on Vercel preview: - [ ] `curl <preview-url>/.well-known/ard.json`: expect 200 with a JSON catalog of 5 entries - [ ] `curl <preview-url>/.well-known/ai-catalog.json`: expect the same document with status 200 (rewrite, not a redirect) - [ ] View homepage page source: expect three `application/ld+json` scripts: Organization now includes `address` and `contactPoint`, and a `SoftwareApplication` block is present ## Linear - fixes GROWTH-1164 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added an Agent Resource Description catalog listing Supabase’s MCP, API, documentation, and agent skill resources. - Added support for the legacy AI Catalog URL through a canonical redirect. - Enhanced website structured data with software application details, legal information, support contact details, and business address. - **Tests** - Added validation ensuring discoverable `.well-known` resources are cataloged and resolve correctly. - **Chores** - Updated marketing site test coverage for `.well-known` resource changes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8aade77966 |
fix(www): gate changelog md alternate on slug set (#49754)
Changelog entry pages advertised a `.md` alternate tag unconditionally while the page is ISR, so an entry published in the changelog repo between www deploys pointed agents at a `.md` sibling that 404s until the next build (the static file and `CHANGELOG_PAGES` are both build-time artifacts). PR #49357 made bare-URL negotiation fail closed for those entries; I gate the advertising side here the same way. **Changed:** - **No more dead `.md` links on freshly published entries**: `getStaticProps` passes a `hasMarkdownVariant` flag computed from `CHANGELOG_PAGES` membership and the page renders the alternate tag only when true. An entry published between deploys carries no tag until the build that ships its `.md` file; the set reference stays inside `getStaticProps`, so the generated module stays out of the client bundle. - **Drift coverage**: `md-alternates.test.ts` gains the changelog direction, source-level like the existing `_app.tsx` drift test; the assertion pins the full `CHANGELOG_PAGES.has(` + backtick-`changelog/${entry.slug}`-backtick + `)` expression so a dropped key prefix fails the suite, and removing the gate fails it too. **Note:** without changelog sync secrets `CHANGELOG_PAGES` is empty, so the tag never renders in local dev. Preview and prod are the verification surface. ## To test Tested on Vercel preview: - [x] Open a published changelog entry page and view source: expect `<link rel="alternate" type="text/markdown" href="/changelog/<slug>.md">` in the head — observed exact href `/changelog/19669-supavisor-1-0.md` - [x] Fetch that href: expect 200 with `content-type: text/markdown` — observed 200, `text/markdown; charset=utf-8` - [x] (added) Client-side nav from `/changelog` into an entry: alternate tag appears with that entry's slug; hopping to a second entry updates the href (no stale tag) - [x] (added) Navigating back to `/changelog`: entry tag gone; the index shows its own pre-existing `/changelog.md` alternate (hardcoded in `pages/changelog.tsx`, outside this diff), and `/changelog.md` returns 200 `text/markdown` - [x] (added) Console: zero new errors across all scenarios vs page-load baseline ## Linear - fixes GROWTH-1120 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Changelog pages now advertise a Markdown alternate link only when a Markdown version is available. * Prevented links to unavailable Markdown content from appearing on changelog entries. * **Tests** * Added coverage to verify correct Markdown alternate detection and rendering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9d5c19e580 |
chore(repo): add public-surfaces rule to agent instructions (#49750)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Repo maintenance: one bullet added to the committed agent instructions in `.claude/CLAUDE.md`. ## What is the new behavior? Agent sessions working in this repo (including Slack-triggered ones) get an explicit rule that PR descriptions, issues, and code comments are world-readable, so internal content stays out of them: absolute production metrics (percentages, ratios, or relative change instead), internal decision detail (vendor, legal, pricing, or strategy discussions), and competitor names (protocol identifiers such as user-agent strings are fine). That context goes in the linked Linear issue. I added this after an agent-authored PR quoted absolute internal event volumes in its description. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added guidance to keep public PRs, issues, and code comments free of sensitive internal details. * Clarified that production metrics, internal decisions, and competitor information should be documented privately instead. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
35531ea2f4 |
feat(www): serve openapi spec at /openapi.json (#49587)
Agent-readiness scanners and agent fetchers look for an OpenAPI spec at conventional same-origin paths, but the Management API spec is only served on api.supabase.com and linked from the /.well-known/api-catalog linkset, which scanners do not read. I added a rewrite so supabase.com/openapi.json proxies the spec from its source of truth at api.supabase.com/api/v1-json, using the same fall-through proxy mechanism as /humans.txt and /evals. **Note:** no cache or CORS headers on purpose: no consumer needs them today, and the upstream response's set-cookie header defeats edge caching regardless. I rejected a checked-in copy of the spec in favor of proxying live (staleness). The /.well-known/api-catalog linkset already points at the spec (PR #44880) and is untouched here; this PR only adds the conventional same-origin path. **Merge order:** merge only after supabase/platform#37571 deploys. The spec currently ships `servers: []`, so OpenAPI consumers resolve relative paths against the fetch origin; without the platform fix this proxy would point spec-compliant clients at supabase.com/v1/*. ## To test Tested on Vercel preview: - [x] `curl -s https://<preview-url>/openapi.json | head -c 40` returns `{"openapi":"3.0.0"` - [x] `curl -sI https://<preview-url>/openapi.json` returns 200 with `content-type: application/json` - [x] `curl -sI https://<preview-url>/humans.txt` returns 200 (control: rewrite fall-through chain intact) ## Linear - fixes GROWTH-1138 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added access to the OpenAPI specification at `/openapi.json`. * Requests are automatically routed to the API specification endpoint. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Aleksi Immonen <aleksi@supabase.io> |
||
|
|
164de2c347 |
feat(www): markdown 404 for markdown-negotiated paths (#49596)
Nonexistent paths return a real 404 everywhere, but always with an HTML body, even when the client asked for markdown via `Accept: text/markdown` or a `.md` suffix. Middleware can't fix this: it gates on a static slug allowlist and can't know a path will 404. I added two `fallback` rewrites (`.md` suffix; Accept header containing `text/markdown` or `text/*`) that run only after every route has failed to match and route the request to a small `md-404` handler returning a short markdown 404 pointing at /docs, /sitemap.xml, and /llms.txt. Real pages are structurally unaffected. **Note:** `lib/rewrites.js` is untouched (the plain rewrites array became the `afterFiles` phase), so #49587 merges independently. I updated next.config.test.ts's rewrites assertion for the phased shape; it now also pins the two fallback rules. ## To test I verified on the Vercel preview: - [x] `curl -s -D - -H "Accept: text/markdown" <preview>/definitely-not-a-page` (404, `Content-Type: text/markdown`, body with the three pointers) - [x] Same URL with a browser Accept header (existing HTML 404, unchanged) - [x] `curl -s -D - <preview>/definitely-not-a-page.md` (markdown 404) - [x] `curl -s -D - -H "Accept: text/markdown" <preview>/auth` (200 markdown, unchanged) and `<preview>/support` (200 HTML, unchanged) - [x] `/homepage.md` still 308s to `/index.md` (redirects phase wins); `Accept: text/*` gets the markdown 404, matching real-page negotiation Known boundary: `/changelog/<unknown>` keeps the HTML 404 body (pages-router `fallback: 'blocking'` routes take priority over fallback rewrites per Next docs); the status is still 404, verified on the preview. ## Linear - fixes GROWTH-1142 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added Markdown-formatted 404 responses for unmatched documentation and `.md` page requests. - Included helpful documentation links in not-found responses. - Requests that explicitly accept Markdown now receive a consistent Markdown response. - Added appropriate response headers for security, caching, and content variation. - **Bug Fixes** - Improved routing for unmatched Markdown paths, ensuring they are handled by the appropriate not-found response instead of returning an unexpected format. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
013af4ed58 |
feat(www): homepage json-ld, canonical, 404 links (#49533)
An agent-readiness scan of supabase.com (is-agentic.com, public report) flagged that the homepage serves no structured data and no canonical tag in raw HTML, and that the 404 page gives crawlers and agents no recovery path. I fixed both. **Changed:** - **Homepage structured data**: the raw HTML now carries Organization and WebSite JSON-LD plus `<link rel="canonical" href="https://supabase.com">`. The schema builders already existed in `lib/json-ld.ts` but were never wired to any page; this reuses the exact inline-script pattern from the blog post pages. The canonical is hardcoded to the production origin on purpose: `SITE_ORIGIN` resolves to the branch URL on previews. - **404 recovery links**: the 404 body now links to the docs, the sitemap, and llms.txt, so a dead URL leads somewhere instead of a dead end. The decorative giant "404" backdrop is a `div` instead of a second `h1`, marked `aria-hidden`, and gets `pointer-events-none`: browser testing showed the absolutely positioned backdrop was silently swallowing clicks on the new links (positioned elements paint above static siblings for hit-testing even when visually behind). - **Drift-guard test**: `md-alternates.test.ts` asserted the literal one-liner `alternates: mdAlternates('<slug>')`, which the canonical wrapper breaks. I broadened the assertion to accept the spread shape too; the rule it guards (every markdown-served slug advertises its `.md` sibling) is unchanged and still enforced. ## To test Tested locally against the dev server: - [x] `curl -s localhost:3000` and parse the two `application/ld+json` blocks: both valid JSON, types Organization and WebSite - [x] `curl -s localhost:3000 | grep canonical`: expect `<link rel="canonical" href="https://supabase.com"/>`, with the existing `text/markdown` alternate link still present - [x] `curl -s localhost:3000/some-nonexistent-page`: expect HTTP 404 with hrefs to `/docs`, `/sitemap.xml`, `/llms.txt` and exactly one `<h1>` in the body On the Vercel preview (verified via curl + Playwright browser run): - [x] View source on the preview homepage: the two JSON-LD blocks present and a canonical pointing at `https://supabase.com` (prod origin, even on the preview host) - [x] Open a nonexistent preview URL: 404 page renders the new link row under the "Head back" button, visually unchanged otherwise (screenshots in session records) - [x] Added: click each recovery link: element hit-testing returns the anchor for all three, and clicking Sitemap navigates to a valid `/sitemap.xml` document (this check caught the pointer-events regression, fixed in this PR) ## Linear - Part of GROWTH-1124 (kept open: remaining scan findings are tracked in a sub-issue) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **SEO & Discoverability** - Added canonical URL metadata and structured organization and website information to the homepage. - Improved 404 page navigation with links to Documentation, Sitemap, and `llms.txt`. - **Accessibility** - Updated the 404 page’s decorative background marker to be non-interactive and hidden from screen readers. - Added reduced-motion handling for page transitions. - **Tests** - Updated metadata validation to support multiple alternate metadata configurations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
6d4263bf1a |
fix(www): pricing table spacing and sticky offset (#49532)
Multi-line cells in the /pricing comparison table rendered as one cramped block once lines wrapped: the stacked values had no gap between entries, and the continuation lines' `leading-4` is a no-op at `text-xs`. At the same narrow desktop widths (1024 to 1280px), category headers clipped behind the sticky plan header because their hardcoded `top-[108px] xl:top-[84px]` offsets desync from the plan header's variable height. **Changed:** - **Value cells get vertical padding**: the value tds were `pl-6 pr-2` with no vertical padding, so tall multi-line cells pressed text against the row dividers; they now carry `py-5` like the row-label column (text ink sits ~21px off both borders, measured). - **Stacked price lines read as separate entries**: `gap-2` between entries while wrapped lines inside one entry stay tight (`leading-4`), so each price reads as its own block. Two earlier iterations that only widened the entry gap (`gap-1`, then `gap-2` with looser `leading-5` wraps) reviewed as still-cramped; the missing cell padding was the dominant cause. Mobile untouched. - **Category headers never clip behind the plan header**: a ResizeObserver measures the sticky thead's real height and publishes `--pricing-category-top` as a CSS variable on the table; the category header consumes it via `var()`, keeping the old 108px/84px values as pre-hydration fallbacks so SSR paint is unchanged. Runs in a layout effect so the first client paint already has the measured value. **Note:** I rejected re-tuning the hardcoded offsets: numbers desyncing from the header's content-driven height is the root cause, and new constants re-break on the next copy or breakpoint change. ## Before / after **Before** (prod: multi-line prices pressed against the row dividers as one dense block; rows clipped under the sticky category header): <img width="1442" height="450" alt="pr-before" src="https://github.com/user-attachments/assets/91d428df-e04c-4494-b454-84a8a46b3ddd" /> **After** (this PR: padded cells, each price its own block, headers pin flush): <img width="1497" height="375" alt="pr-after" src="https://github.com/user-attachments/assets/5aa2c3c7-b41e-42c0-8159-bb294a5d1dea" /> ## To test Tested on the Vercel preview (Playwright, measured values in parens): - [x] At a 1024 to 1280px viewport, open /pricing and find the Pipelines row: the 3 price lines in the Pro and Team cells read as distinct blocks (row-gap 8px between entries, tight 16px line boxes within an entry, 20px cell padding; verified on localhost pre-push and on the preview, light and dark) - [x] Multi-line cell text no longer touches the row dividers: ~21px from border to text ink top and bottom (was 0-3px) - [x] At the same width, scroll the whole comparison table: Database and Auth section headers pin flush below the plan-price header, with no row text clipped between them (pinned category top within 0.2px of thead bottom) - [x] At ~1800px wide: Pipelines and Point in time recovery (Enterprise) cells show the same separated lines (PITR Enterprise is a single wrapping string, renders cleanly) - [x] Resize across 1280px after load: category headers stay flush under the plan header (ResizeObserver refires; same 0.2px alignment after 1900px to 1150px resize without reload) - [x] Below 1024px: the mobile comparison view is unchanged - [x] Added: cold navigation to /pricing#compare-plans lands with `--pricing-category-top` already applied (151px at 1150px width) and the pinned header flush - [x] Added: no new console errors versus the page's pre-existing baseline ## Linear - fixes GROWTH-1137 |
||
|
|
21a27eeb4f |
feat(www): canonicalize homepage markdown at /index.md (#49384)
The www root markdown lived at an accidental URL: `/.md` served the
homepage markdown only because middleware strips the `.md` suffix and
the empty slug fell through to the homepage allowlist entry, while the
canonical-looking `/index.md` 404'd. The served markdown also opened
with stale legacy positioning copy that no longer matches the site. I
renamed the homepage content slug to `index` end-to-end so `/index.md`
is the one canonical markdown URL.
**Changed:**
- **`/index.md` serves the homepage markdown (200 `text/markdown`)**:
`content/md/homepage.md` renamed to `index.md`; the middleware bare-root
slug mapping, the generator's sort special-case, and the homepage
alternate tag follow, so the tag now advertises `/index.md`.
- **Legacy aliases 308 to the canonical URL**: `/.md`, `/homepage.md`,
and bare `/index` redirect via `lib/redirects.js`; `/llms/homepage.txt`
retargeted straight to `/index.md` to avoid a redirect chain. New
`next.config.test.ts` assertions pin all four.
- **Positioning refreshed**: the markdown now opens with "Supabase is
the Postgres development platform" (matching the site title), replacing
the outdated tagline.
- **Generator safety**: the redirect-exclusion filter in
`generateMdContent.mjs` now exempts the `index` slug (its HTML page is
`/`, not `/index`, so a `/index` redirect never refers to it), and the
build fails if `content/md/index.md` ever goes missing while middleware
still maps `/` to the `index` slug.
- **CI actually runs the new assertions**: I widened the `www-tests.yml`
paths filter to include `apps/www/lib/**/*.js`,
`apps/www/content/md/**`, and `apps/www/scripts/**/*.mjs`. It previously
only matched `.ts*` and the next.config files, so a PR touching only
`lib/redirects.js`, the markdown content, or the generator would skip
the tests that pin these redirects.
**Note:** the existing homepage alternate tag still exists, re-pointed
to the canonical URL. Whether the homepage should advertise a markdown
sibling at all is a separate decision; leaving it aimed at a 308 would
break tag consumers. Positioning wording is editorial, happy to tweak.
## To test
Tested on Vercel preview:
- [x] `curl -si <preview>/index.md`: expect 200 `content-type:
text/markdown`, body opens with the Postgres development platform
positioning and no longer contains the old tagline
- [x] `curl -sI <preview>/.md`: expect 308 with `location: /index.md`
- [x] `curl -sI <preview>/homepage.md` and `curl -sI
<preview>/llms/homepage.txt`: expect 308 with `location: /index.md`
- [x] `curl -sI <preview>/index`: expect 308 with `location: /`
- [x] `curl -s -H "Accept: text/markdown" -o /dev/null -w "%{http_code}
%{content_type}" <preview>/`: expect `200 text/markdown` (bare-URL
negotiation unchanged)
- [x] `curl -s <preview>/ | grep -o 'type="text/markdown"
href="[^"]*"'`: expect href ending `/index.md`
## Linear
- fixes GROWTH-1117
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added support for `/index.md` as the canonical Markdown representation
of the homepage.
- Added permanent redirects for legacy homepage Markdown and text URLs.
- Added `/index` to `/` redirect handling.
- **Bug Fixes**
- Updated homepage metadata, alternate links, Markdown negotiation, and
content generation to consistently use the new canonical path.
- Improved homepage content description.
- **Tests**
- Expanded coverage for homepage Markdown routes, redirects, and URL
matching.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
f208743432 |
fix(www): changelog md negotiation slug-set gate (#49357)
Bare-URL `Accept: text/markdown` negotiation never fires on changelog entries authored after the GitHub-discussions backfill: the middleware gate `/^changelog\/\d+/` only matches legacy numeric slugs (from `legacy_gh_discussion` frontmatter), so agents that signal markdown via Accept get HTML on every new entry. I found this in the independent review round on #48475; pre-existing, not introduced there. **Changed:** - **Non-legacy entries negotiate markdown**: `generateMdContent.mjs` now lists `public/changelog/*.md` (written moments earlier by `generateStaticContent.mjs` in the same `content:build:core` chain) and emits a `CHANGELOG_PAGES` set into the generated module; the middleware regex becomes a set lookup, so negotiation coverage derives from the exact static files served and can't drift from what's published. - **Unknown and deep changelog paths stop negotiating**: the old regex prefix-matched paths like `changelog/100/bar` and nonexistent numeric slugs, rewriting them to missing `.md` files (404 under a markdown Accept); they now pass through to the dynamic route's canonicalizing 308/404. - **Build guard**: zero collected changelog slugs on Vercel fails the build (today a zero-entry changelog fetch ships empty output with a green build), and a shape assertion fails the build if collected slugs ever lose the `changelog/` prefix the middleware matches on. Locally without `CHANGELOG_SYNC_APP_*` secrets it warns and changelog negotiation is off, matching the absent content. - **`/changelog` index gated the same way**: the index slug is emitted into the set only when `public/changelog.md` was generated, replacing the hardcoded `slug === 'changelog'` branch; locally without secrets the index no longer rewrites to a nonexistent file. **Note:** script order in `content:build:core` is load-bearing (static content generation must precede md content generation); the Vercel guard turns a reorder into a loud build failure instead of a silent empty gate. ## To test Tested on the Vercel preview (`zone-www-dot-com` deployment of head `f451da3`): - [x] `curl -sI -H "Accept: text/markdown" <preview>/changelog` and `curl -sI <preview>/changelog.md`: got 200 `text/markdown` (index via the generated gate) - [x] `curl -sI -H "Accept: text/markdown" <preview>/changelog/pipelines`: got 200 `text/markdown` (prod today returns `text/html`) - [x] Same curl against the legacy numeric slug `48235-migration-of-...`: got 200 `text/markdown` (no regression) - [x] `curl -sI -H "Accept: application/json" <preview>/changelog/pipelines`: got 406 (prod today returns 200 HTML) - [x] Explicit `.md` fetches for both slug shapes (`/changelog/pipelines.md`, `/changelog/48235-....md`): got 200 `text/markdown` - [x] `curl -sI -H "Accept: text/markdown" <preview>/changelog/does-not-exist-xyz`: got a 404 HTML passthrough from the dynamic route, not a 406 - [x] `pnpm test middleware.test.ts` in `apps/www` at head: 41/41 pass (36 pre-existing + 5 new). No CI job runs the www vitest suite, so this local run is the only oracle for the new tests. ## Linear - fixes GROWTH-1062 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved changelog page handling, including markdown versions of published entries. - Added content negotiation for supported changelog formats, with clear responses for unsupported requests. - **Bug Fixes** - Prevented unpublished numeric-prefix pages from being treated as published. - Fixed deep links under published changelog entries. - **Reliability** - Changelog availability is now detected automatically, with improved validation during content generation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
10c425ad0b | feat(www): markdown copy/ask affordances (#48475) | ||
|
|
65e786ba13 | feat(docs): manifest-gated markdown alternate helper (#48389) | ||
|
|
9ae6e54dd5 |
fix(www): exclude redirected slugs from generated markdown (#48476)
11 of the generated `MD_PAGES` entries are blog slugs whose HTML pages 308-redirect away via `apps/www/lib/redirects.js` before any `<head>` renders. They can never carry an alternate tag and Accept negotiation never fires (Next.js `redirects()` runs before middleware), so their `.md` siblings are orphaned content reachable only by guessing the suffixed URL. Six of them duplicate live, correctly-tagged pages (`/customers/*`, `/pricing`). **Changed:** - `generateMdContent.mjs` derives an exclusion set from `lib/redirects.js` at generation time: any unconditional exact-match redirect source (wildcard/param patterns and conditional `has`/`missing` redirects are skipped) drops the matching slug from both `MD_CONTENT` and `MD_PAGES`. The build log names every excluded slug, currently the 11 known ones. - Self-maintaining by design (per the decision recorded on the issue): a future redirected post auto-excludes on the next build, and removing a redirect brings its `.md` sibling back. The MDX sources stay in the repo; nothing is deleted. - Effect on the 11 slugs: alternate tags stay absent (nothing rendered them anyway), and explicit `.md` URLs go from serving orphaned markdown to 404, the same external effect deletion would have had. ## To test Tested locally: - [x] `node scripts/generateMdContent.mjs` logs `🚫 Excluded 11 redirected slugs: ...` naming exactly the 11 known slugs; output drops 483 → 472 pages - [x] Generated file carries no MD_CONTENT/MD_PAGES key for any excluded slug (raw URL mentions inside other posts' bodies remain, as expected) - [x] `apps/www` vitest: 71/71 (GROWTH-1013 drift tests unaffected) Post-merge: - [ ] `https://supabase.com/blog/case-study-xendit.md` returns 404 (previously 200 orphaned markdown); `https://supabase.com/pricing.md` still 200 ## Linear - fixes GROWTH-1022 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Excluded content with valid exact-path redirects from generated documentation. * Preserved content associated with conditional or wildcard redirects. * Updated generated page counts and output statistics to reflect the filtered content. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
c4c58ef3e3 |
feat: remove pandadoc dpa request flow (#48525)
Terms of Service v3 (effective August 1, 2026, #48482) incorporates the Data Processing Addendum by reference, so customers no longer sign a separate DPA. Legal confirmed the PandaDoc signing flow can go; previously signed DPAs remain binding. This removes the frontend flow only. I'll remove the platform endpoint (`POST /platform/organizations/{slug}/documents/dpa`) separately once the PandaDoc contract conversation wraps. **Changed:** - **Dashboard DPA card no longer requests PandaDoc documents**: the Request DPA button and confirm modal are replaced with a View DPA link to the canonical legal page, with evergreen copy explaining the DPA is part of the Terms. Tracked via the same `document_view_button_clicked` event the other document cards use. - **Legacy `/legal/dpa` page retired**: the page told users to request a signed DPA from the dashboard, which no longer exists. It now permanently redirects to `/legal/customer-resources/data-processing-addendum` (the follow-up already flagged in #48483), and the footer link is removed. The `dpa_pdf_opened` and `dpa_request_button_clicked` events are removed with their last call sites. The latest privacy version links the canonical page directly; archived v1/v2 keep their original `/legal/dpa` link, served by the redirect. - **Orphaned DPA PDFs removed**: the four dated `Supabase+DPA+*.pdf` files under `/downloads/docs` had zero remaining references once the signing flow is gone. No redirect: nothing links these URLs, so they 404. - **Subscription tracking**: the subprocessor updates form now fires `www_subprocessor_updates_subscribed` on successful submit, so we can measure uptake of the notification list that replaces per-customer DPA emails. ## To test Verified on the Vercel previews (Playwright): - [x] Studio: `/org/_/documents` shows the DPA card with the incorporation copy and a working View DPA link (href = canonical page); no Request DPA button, no PandaDoc mention; TIA/SOC2/ISO27001/HIPAA cards unaffected - [x] www: `/legal/dpa` permanently redirects to `/legal/customer-resources/data-processing-addendum`; footer no longer shows DPA; zero console errors - [x] www: subscribing on the subprocessor page succeeds (200 from the form route, profile created with topic_4) and fires `www_subprocessor_updates_subscribed` (201 from the telemetry endpoint); test profile unsubscribed afterwards - [x] www: `/downloads/docs/Supabase+DPA+260601.pdf` returns 404 with no redirect; DPA card copy verified without the effective date ## Linear - fixes GROWTH-1068 |
||
|
|
4ae0c08967 |
feat: tos v3 update banner + publish subprocessor list (#48524)
Terms of Service v3 (effective August 1, 2026, #48482) incorporates the Data Processing Addendum by reference, and Legal asked for an in-app notice announcing the change. The subprocessor list page that the new Terms, DPA, and notice all point at was merged as an intentionally hidden draft (#48100) and never un-hidden. **Changed:** - **Dashboard ToS-update banner**: re-enables `BannerTOSUpdate` with the v3 copy provided by Legal (DPA incorporation, subprocessor list location, fees provisions). New expiry (August 29) and a new localStorage key, since anyone who dismissed the May v2 banner would otherwise never see this one. - **Subprocessor list page published**: removes `noindex,nofollow` and links the page from the Legal Hub index, so the page customers are told to subscribe on is actually discoverable. - **Studio e2e fixture updated**: the global Playwright fixture suppressed the banner via the old localStorage key; with the gate live again it would have rendered the banner into every e2e run. It now sets the new key. ## To test Verified on the Vercel previews : - [x] Studio: banner renders on dashboard load with the Notice badge and new copy; Learn more dialog shows the three changes with correct hrefs (DPA page, subprocessor list, /terms); Understood dismisses and persists across reload via `terms-of-service-update-2026-08-01` - [x] www: `/legal` lists Subprocessor List under Customer Legal Resources; `/legal/customer-resources/subprocessor-list` serves `robots` meta `index,follow` and renders the download button + subscribe form; zero console errors on all tested pages ## Linear - fixes GROWTH-1067 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a publicly accessible Subprocessor List to the legal resources. * Updated the Terms of Service notice to reflect the August 1, 2026 update, including data processing, subprocessors, fraud prevention, and consumer provisions. * **Documentation** * Made the Subprocessor List discoverable through standard search indexing and the legal resources page. * Extended the Terms of Service banner availability through August 29, 2026. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
01541b95cb |
fix(studio): keep organization_slug in oauth signup redirect (#48352)
Email signups inside a partner OAuth flow lose `organization_slug` on the post-confirmation redirect: the OAuth branch in `SignUpForm` hand-builds the `/authorize` return URL from only `auth_id` and `token`, and the component's nuqs hook never reads the param at all. The consent screen uses `organization_slug` to preselect and lock the partner's requested org, so affected multi-org users land on an empty picker and the partner's requested org is silently dropped. The GitHub-OAuth signup path goes through `buildPathWithParams` and preserves the param, which is how this went unnoticed. I validated the drop in production traffic before fixing: joining sign-up pageviews to their post-signup `/authorize` return on the `auth_id` URL param (30d), 28 of 36 resolvable flows came back without the slug, and the 8 that kept it were the GitHub branch. ## To test Needs a partner OAuth authorize link that includes an org, opened signed-out: `/dashboard/authorize?auth_id=<id>&organization_slug=<slug>` (note `auth_id` records expire quickly, so generate a fresh authorize request from an OAuth app). - [x] Sign up with email from that flow; after confirmation the redirect lands on `/authorize` with `organization_slug` still in the URL - [ ] Consent screen shows the requested org preselected and locked - [x] Same flow without `organization_slug` behaves as before (no trailing empty params in the redirect URL) ## Linear - fixes GROWTH-1031 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved sign-up redirects during authentication flows by preserving invitation tokens and organization information. * Enhanced handling of sign-up links containing organization details. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9113c2ba04 |
feat: markdown alternate tags + llms.txt cleanup (#48287)
The June/July marketing redesign (#47271, #47228) rebuilt the homepage and product pages off the Pages Router, silently dropping their `<link rel="alternate" type="text/markdown">` head tags, and llms-full.txt has been accidentally embedding every blog/customer/event page via an `MD_CONTENT` spread. I restored the tags behind a shared helper, added a CI drift test so a future redesign can't drop them silently again, and trimmed both llms files to the agreed docs-index shape. **Changed:** - **Markdown siblings advertised again**: homepage, the 5 product pages, pricing, and blog emit absolute `.md` alternate URLs via a new `mdAlternates(slug)` helper (the one documented consumer of the tag parses it from `<head>` and fetches the `.md` sibling, so tags must point at the sibling, never the page itself). - **Drift test**: a vitest file walks `content/md/**` and asserts every markdown-served slug's page wires the helper (or is covered by the Pages Router `_app.tsx` mechanism, whose alternate-link wiring the test also asserts directly so removing it fails CI too). Source-level assertions by design: page modules can't be imported under www's vitest config. Fails correctly when wiring is removed (verified by hiding a page and by altering the `_app.tsx` tag). - **Vector orphan fixed**: `content/md/vector.md` moved to `modules/vector` matching the live route (the page previously had no negotiation or tag, and `/modules/vector.md` 404'd); `/vector.md` now 308s to `/modules/vector.md` and the legacy `/llms/vector.txt` redirect no longer chains. - **llms.txt + llms-full.txt**: the `## Product Overview` sections are gone from both, each keeps a `## Pricing` section. This deletes the hand-maintained links array (a drift trap) and fixes the accidental ~470-page embed, shrinking llms-full.txt from ~9.8MB to ~4.9MB and dropping the 4.1MB generated content module from that route's serverless bundle. **Note:** this PR is scoped to apps/www only. The docs side (troubleshooting pages and the rest of the docs surface) is handled separately through a consolidated manifest-gated mechanism; an earlier troubleshooting-tag commit was reverted out of this branch to keep the scopes clean. <details> <summary>Why alternate tags matter (background)</summary> Agents ingest markdown far more efficiently than our rendered HTML: a fraction of the tokens and no extraction step. Since #47770 removed UA-based serving (UA sniffing broke a major AI app's fetcher and poisoned CDN caches), markdown is served only on explicit request: a `.md` suffix URL, an `Accept: text/markdown` header, or llms.txt. That's the right serving model, but it makes the markdown twin invisible to any agent that doesn't already know our URL convention, and the major AI fetchers send browser/wildcard Accept headers, so bare URLs hand them HTML. The `<link rel="alternate" type="text/markdown">` head tag is the standards-based advertisement of the sibling. It has a documented consumer today: an agent CLI that parses the tag from `<head>` and then fetches the `.md` sibling, which is also why the tag must point at a real sibling URL and never at the page itself. Peer docs sites ship this tag as table stakes. These www pages used to carry it until the June/July marketing redesign silently dropped it; the drift test in this PR turns that regression class into a CI failure. </details> ## To test Tested locally (www + docs dev servers): - [x] `/llms.txt` renders `## Documentation` + single-link `## Pricing`, no Product Overview - [x] `/llms-full.txt` renders `# Supabase` → `## Pricing` → `## Documentation`, no Product Overview, ~4.9MB - [x] Full www suite: 6 files / 71 tests green; drift test fails correctly when a page is removed or the `_app.tsx` wiring is altered - [x] `generateMdContent.mjs` emits `modules/vector`, bare `vector` slug gone On the Vercel preview (browser-verified with Playwright): - [x] Alternate tag present on `/`, `/auth`, `/database`, `/storage`, `/edge-functions`, `/realtime`, `/pricing`, and a blog post: exactly one tag each, href = preview origin + `.md` sibling - [x] `/vector.md` → 308 → `/modules/vector.md`, renders as markdown (`# Supabase Vector`) - [x] `/llms.txt` shows single-link `## Pricing`, no Product Overview - [x] Coverage sweep: all 482 `MD_PAGES` slugs + changelog index/entry curled on the preview; 471 pages carry exactly one tag, all `.md` siblings 200 as `text/markdown`. The 11 misses are legacy blog slugs whose HTML 308-redirects away (stale `MD_PAGES` entries predating this PR, no head to tag; follow-up tracked in Linear) Post-merge prod: - [ ] Full llms.txt link sweep (every linked URL 200s; previews can't cover the docs-hosted links) ## Linear - fixes GROWTH-1013 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Markdown alternate links across key product, pricing, blog, and troubleshooting pages. * Added Supabase Vector documentation covering features, use cases, workflows, and technical details. * Updated AI-focused documentation indexes with dedicated pricing content. * Added redirects for updated Vector documentation URLs. * **Tests** * Added coverage to verify Markdown documentation links stay aligned with available pages. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1b1b1ea0e0 |
chore: remove dead telemetry code (#47950)
## Summary Removes two pieces of dead telemetry code found while root-causing the docs pageview re-fire investigation (GROWTH-997, closed with no fix needed). Pure deletion, 30 lines, no behavior change. ## Changes - Delete `apps/www/app/ConsentWrapper.tsx`: an unwired third `PageTelemetry` mount. www already mounts `PageTelemetry` in `pages/_app.tsx` (Pages Router) and `app/providers.tsx` (App Router); nothing imports this wrapper. - Remove the exported `POSTHOG_URL` constant from `apps/studio/lib/constants/index.ts`: zero consumers. The CSP allowlist in `apps/studio/csp.ts` defines and uses its own local `POSTHOG_URL`, which stays. ## Testing Deadness verified before deletion, on current master: - [x] Repo-wide grep for `ConsentWrapper`: only self-references inside the deleted file - [x] Repo-wide grep for `POSTHOG_URL`: remaining references are the `csp.ts` local const only ## Linear - fixes GROWTH-1002 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Removed an obsolete consent-related page wrapper to streamline page behavior. * Updated application configuration handling without changing existing payment or usage settings. * **Refactor** * Simplified internal configuration and page composition while preserving the existing user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9858562b8b |
fix(telemetry): dedupe funnel toast error events (#47802)
## Summary Since #47293, an API failure on a signup / org-creation / project-creation form emitted `dashboard_error_created` twice: `useTrackFunnelError` fired the origin-tagged event and the global `ToastErrorTracker` independently fired the legacy untagged `source:'toast'` event for the same toast, each behind its own 10% sampling draw. I verified the twin rate empirically at 8-11% of origin-tagged funnel toasts, exactly the floor for two independent 10% draws, meaning the twin co-fires for effectively every funnel error ([Hex thread](https://app.hex.tech/supabase/thread/019f3bc1-3a5c-7200-9122-8e3439bfbe8c)). Any consumer counting funnel errors without an `origin IS NOT NULL` filter saw ~2x inflation. The fix makes `ToastErrorTracker` the sole emitter of `source:'toast'` events, so the duplicate is unrepresentable rather than suppressed. Funnel call sites pass the id returned by `toast.error()` into `trackFunnelError`, which registers the funnel properties against that toast id instead of firing its own event – the tracker then emits a single `dashboard_error_created` enriched with `origin` / `errorCategory` / `errorReason` / `errorCode` for registered toasts, and the plain untagged event otherwise. The `'toast'` overload of `trackFunnelError` requires the toast id, so a missed pairing is a compile error rather than a silent double count. Registration is unconditional and there's only one sampling draw, so suppression can't lose a sampling race. `'form'`-sourced funnel events are unchanged. ## Changes - `lib/toast-errors.tsx`: toast-id → funnel-properties registry (`registerFunnelErrorToast`); `ToastErrorTracker` emits one (optionally enriched) event per error toast under a single 10% draw, deleting entries once consumed - `lib/telemetry/use-track-funnel-error.ts`: overloaded signature – `'toast'` requires the id returned by `toast.error()` (type-enforced), `'form'` keeps direct emission with its own sampling - Update the 7 funnel `toast.error` call sites in `NewOrgForm`, `SignUpForm`, and `pages/new/[slug]` to pass the toast id - Component tests for the tracker (previously uncovered), including an end-to-end test through `useTrackFunnelError` - Code hygiene (also flagged by CodeRabbit): all four `dashboard_error_created` emitters (toast, form, `AlertError`, `ErrorMatcher`) independently encoded the 10% draw – downstream analysis assumes a uniform sampling multiplier across sources, so one site drifting would silently skew comparisons. The rate and the draw now live in one place (`isDashboardErrorSampled()` in `lib/telemetry/error-sampling.ts`). No behavior change. - Mount `ToastErrorTracker` in the TanStack root (`routes/__root.tsx`), mirroring `pages/_app.tsx`. The TanStack tree mounted `Toaster` but never the tracker, so untagged toast error telemetry has never fired in that flavour – and with the tracker now the sole emitter, the missing mount would have silently dropped funnel toast events there too. Side effect once the TanStack flavour ships: untagged `source:'toast'` volume from it goes from zero to normal. ## Testing Component-tested (`apps/studio/lib/toast-errors.test.tsx`): - [x] Unregistered error toast fires exactly one untagged `dashboard_error_created {source:'toast'}` - [x] Registered funnel toast fires exactly one event, enriched with `origin`/`errorCategory`/`errorReason`/`errorCode` - [x] `useTrackFunnelError` with a toast id routes through the tracker as a single enriched event - [x] Non-error toasts ignored; the 10% sampling gate still applies Full Studio unit suite passes (392 files / 4371 tests), plus typecheck and lint. Also verified end-to-end in a local browser (TanStack flavour, sample rate temporarily forced to 1): a failed signup produced exactly one `dashboard_error_created` with `{source:'toast', origin:'signup', errorCategory:'api', errorReason:'email_already_registered', errorCode:403}` and no untagged twin (two independent trials); an unregistered error toast produced exactly one plain `{source:'toast'}`; a client-side validation failure produced exactly one `{source:'form', origin:'signup', errorCategory:'validation', errorReason:'email_invalid'}`; success toasts produced nothing. Post-deploy I'll re-run the twin-rate query from the Hex thread; the untagged-twin rate on funnel pages should decay to ~0 as stale bundles reload over 2-3 days. ## Notes - Origin-tagged funnel toast events now ride the tracker's single 10% draw instead of their own independent draw – statistically identical volume, but the event fires on the tracker's next effect rather than synchronously at the call site (irrelevant for PostHog) - Registration must happen in the same synchronous block as `toast.error()` (documented on the `TrackFunnelError` type) – all current call sites comply - The invalid Postgres version toast in `pages/new/[slug].tsx` (~line 416) needs no special-casing: unregistered toasts keep the plain untagged event, so its telemetry is preserved - Heads-up for `dashboard_error_created` consumers: overall untagged `source:'toast'` volume will dip slightly after this deploys, since funnel-page twins disappear. A volume monitor seeing that drop is this fix landing, not a tracking regression (same class as the intended GROWTH-893 sampling-unification drop). ## Linear - fixes GROWTH-965 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Enhanced error telemetry for organization creation, sign-up, payment, and project-creation flows by associating failures with toast identifiers and enriched funnel context. * Standardized dashboard error sampling logic across error handling components for consistency. * **Tests** * Added comprehensive test coverage for toast error tracking, including funnel registration, deduplication, filtering, and sampling behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com> |
||
|
|
770f1c2b06 |
fix(aeo): remove ua-based markdown serving (#47770)
## Summary The `ChatGPT-User` live-fetch agent's user-facing reader hard-fails (`(400) OK`) on pages we serve it as markdown via user-agent matching, which made supabase.com blog and product pages unreadable in that assistant. I root-caused this with a controlled fetch diagnostic cross-checked against our request logs: the failing fetches never reach our origin (the failure is cached on their side), pages served as plain HTML read fine everywhere we tested, and the same failure reproduces on other major sites that serve UA-matched markdown, so the reader bug is upstream. This PR removes user-agent-based markdown serving entirely rather than special-casing one agent: UA sniffing is a guess about contractless clients whose fetchers change without notice, and this incident showed the failure mode is silent (we keep serving 200s while the user-facing agent breaks). Markdown remains available on every explicit signal — `Accept: text/markdown` q-value negotiation, explicit `.md` URLs, and llms.txt — which is the same contract-driven model the Claude fetcher already uses successfully (it sends `Accept: text/markdown, text/html, */*` and keeps receiving markdown after this change). ## Changes - Remove the `LLM_USER_AGENT` regex and the `userAgent` parameter from `negotiateMarkdown` in `packages/common/markdown-negotiation.ts`; decisions now depend only on `Accept`, the `.md` suffix, and the markdown-variant manifest - Update both consuming middlewares (`apps/www`, `apps/docs`) to the new signature; no behavior change for Accept-negotiated or `.md` requests - Add the missing `Vary: Accept` header to docs guides-md 200 responses (the www `api-v2/md` route already declares it) - Fix a pre-existing www bug surfaced in review: explicit changelog `.md` URLs rewrote to a doubled `.md.md` path (404) under a markdown-preferring `Accept`, and 406'd on a non-matching `Accept`. The www middleware now strips the `.md` suffix before slug lookup and passes `isMarkdownSuffix` into `negotiateMarkdown`, folding the separate `MD_PAGES` `.md` block into the single negotiation path (same shape as the docs middleware) - Rework tests: UA-independence suites replace the per-agent rewrite tests; a probe Accept header now 406s regardless of user agent (previously agent UAs were exempt); new changelog `.md` negotiation coverage ## Testing Tested locally: - [x] www middleware suite 36/36, docs middleware suite 17/17 - [x] typecheck green for common, www, docs Verified on the Vercel previews (www + docs) with curl: - [x] `ChatGPT-User` and `Claude-User` UA GETs on blog/pricing/guide pages return `text/html` with a default Accept - [x] Claude's real Accept (`text/markdown, text/html, */*`) still returns `text/markdown`; `Accept: text/markdown` and `.md` URLs return `text/markdown`; probe Accept returns 406 - [x] `/changelog/<slug>.md` with `Accept: text/markdown` returns the entry markdown as a direct 200 (production today detours through a 308 to the bare URL); changelog index `.md` and bare-entry Accept negotiation also verified - [x] docs guides markdown 200s carry `Vary: Accept` The intermediate commit (ChatGPT-User-only exclusion) was already verified on the preview: `ChatGPT-User` got HTML while `Accept`/`.md`/other-UA markdown was unaffected. Expected effects post-merge: UA-driven markdown volume in the request logs (~92% of md traffic) collapses to the Accept + `.md` baseline; named-agent page requests return to prerendered/static serving, reversing the extra Vercel function invocations the UA rewrite introduced; user-facing readability in the affected assistant recovers within ~24h as its fetch cache revalidates. The md-share dashboard gets a dated annotation; the ratio is not comparable across this change. ## Linear - fixes GROWTH-973 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Markdown and HTML routing now depends on the request’s `Accept` header and `.md` links, making content negotiation more predictable. * Requests that don’t accept available content now consistently return `406 Not Acceptable`, even for bot-like user agents. * Guide markdown responses now include an `Accept`-based cache variation header to improve correct caching behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
6dfae09b6d |
fix(www): postgres card art overlaps text column (#47734)
Before <img width="588" height="439" alt="Screenshot 2026-07-08 at 7 29 07 PM" src="https://github.com/user-attachments/assets/8a3380d3-571f-49cd-8f32-7b38650dd0e5" /> After <img width="578" height="436" alt="Screenshot 2026-07-08 at 7 29 55 PM" src="https://github.com/user-attachments/assets/09e74c31-e131-41d5-87da-b4518514cfb5" /> ## Summary On the homepage, the Postgres Database product card's elephant artwork renders over the card's description text at every viewport ≥1280px. I traced it to #47226 (merged June 24): standardizing the marketing container to `section-container` (`max-w-7xl` + `xl:px-24`) capped products-grid content at 1088px, shrinking the card to ~538px while its fixed geometry (250px text column + 398px right-anchored square artwork box) needs ~625px. Large monitors regressed hardest: before June 24 they got ~676px cards and no overlap. The app-router homepage move (#47228), the color system PR (#47288), and the artwork PNGs are all unrelated (verified against production DOM and full diffs). Two changes to the artwork span in `DatabaseVisual.tsx`: 1. Cap the box width at `calc(100% - 280px)` from `md` up, where 280 covers the text column's `md:max-w-[250px]` cap (in `ProductCard.tsx`'s `isDatabase` branch) plus card padding and breathing room. The art scales down through its existing `object-contain`. At md/lg the card is full-width (`md:col-span-12`), so the clamp never binds and rendering is unchanged there. 2. Remove the `xl:-right-12` bleed (base `right-0` stands, matching how 2xl already rendered). The bleed used to clip only the art's transparent canvas margin; with the clamped box the art fills its full width, so the 48px offset was cropping the elephant itself at 1280-1535px. The hover line-art SVG scales with the box and stays aligned with the PNG (identical aspect ratios: viewBox 390:430, PNG 585x645). ## Changes - Add `md:max-w-[calc(100%-280px)]` to the artwork span in `DatabaseVisual.tsx` so the Postgres card art can never cross its text column - Drop `xl:-right-12 2xl:right-0` from the same span so the smaller art isn't clipped at the card's right edge ## Testing Round 1 on the Vercel preview (commit |
||
|
|
0099ad1aec |
fix(account): stop sb marker leaking into email toast (#47455)
## Summary
The email-change confirmation toast rendered a trailing `&sb=` ("...sent
to the other email&sb="). The dashboard parsed the auth-redirect URL
fragment with a naive `split('#message=')` that grabbed everything after
the key, including the empty `sb` origin marker the auth service appends
to every redirect fragment (an intentional, server-side Supabase-Auth
identifier so clients can tell a Supabase redirect from a third-party
OAuth one). The marker is working as designed; the bug is that the
dashboard wasn't parsing the fragment as URL params, so I fixed the
parse rather than the marker.
## Changes
- Parse the redirect fragment with `URLSearchParams` via a new
`parseRedirectMessage` helper, reading only the `message` key. Any other
trailing fragment param (the `sb` marker, or future ones) is now ignored
instead of being concatenated into the toast.
- Drop the manual `+`-to-space replacement. `URLSearchParams.get()`
already decodes form-encoded values, and the old `.replaceAll('+', ' ')`
would have clobbered a legitimately encoded `+`.
- Add unit tests for the helper: marker stripped, no hash, no `message`
key, `message` not first, and percent-encoded `+` preserved.
## Testing (Vercel preview)
The toast only reads the URL fragment, so the redirect can be simulated
directly. Do not use the real email round-trip on the preview: a real
confirm-link click is redirected to prod (the backend sets
`redirect_to`), not the preview build.
- [x] On the preview, log in and open the account preferences page with
this fragment appended:
`/account/me#message=Confirmation+link+accepted.+Please+proceed+to+confirm+link+sent+to+the+other+email&sb=`
— toast shows the clean sentence with no `&sb=`.
- [x] Open the same page with no fragment — no toast fires.
## Linear
- fixes GROWTH-938
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved how success messages are read after redirect in account
identity preferences, so notifications now display the correct text more
reliably.
* Supported messages with spaces and special characters, including cases
where the message appears later in the URL fragment.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
98cfe3307e |
feat(telemetry): fix creation-funnel tracking gaps (#47386)
## Summary The creation-funnel instrumentation that shipped Jun 25 (#47291, #47293) had real gaps, surfaced by the weekly telemetry audit and confirmed against production PostHog data before I touched code. The two automated reports also contradicted each other on `errorReason`; I checked production (every value is a controlled slug) and the emit path (only `useTrackFunnelError` sets it, and it only accepts classified slugs), so I left the type as-is rather than add a cross-package abstraction for a risk that cannot occur today. ## Changes - Classify HTTP 401/403/404 API errors as `unauthorized` / `forbidden` / `not_found` instead of the catch-all `other`. In production the `org_creation` `other` bucket was ~96% 401s (~1,300 real over 4 days), invisible in reason breakdowns. The status-code fallback runs after the message-pattern match, so specific reasons still win and it only rescues errors that would otherwise be `other`. - Add a single `tier` property (`tier_free` / `tier_pro` / `tier_payg` / `tier_team`) to `organization_creation_completed`, which previously carried no properties. One canonical billing slug (matching `SubscriptionTier`) instead of two overlapping plan/tier fields, so the org-creation funnel segments cleanly by tier and joins against subscription data. `tier_payg` is uncapped PRO. - Freeze the submitted tier at submit time (snapshot in `createOrg`) rather than reading live form state in the success callback, so the event records the tier that was actually created even if the user edits the form during the async payment flow. - Emit `project_creation_form_exposed` with `surface: 'vercel'` on the integration deploy-button project-creation page (the enum value existed but was never fired). Gated on the URL `slug` so the impression is captured as soon as the form renders, matching the sibling exposure hook on that page. I also checked the confirm-modal error path flagged in the insights post: it already classifies via the shared `useProjectCreateMutation.onError`, so adding instrumentation there would double-count. No change made. ## Testing These are analytics events with no UI change, so correctness is in what lands in PostHog. Post-deploy validation I will run against production (project 34344): - `dashboard_error_created` where `origin='org_creation'` and `errorReason='other'` drops ~96%, with `unauthorized` / `not_found` appearing. - `organization_creation_completed.tier` populated on 100% of new events with one of the four tier slugs. - `project_creation_form_exposed` with `surface='vercel'` goes from 0 to greater than 0. ## Linear - fixes GROWTH-948 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added telemetry for organization creation completion that includes the selected billing tier. * Added one-time telemetry when the Vercel project creation form is exposed. * **Bug Fixes** * Improved API error classification to more accurately distinguish unauthorized, forbidden, and not found responses. * **Documentation** * Updated telemetry event definitions to require tier metadata for organization creation events. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
20290c71bd |
fix(docs): stop named-bot markdown 404s on guides (#47337)
## Summary Since the guides UA-redirect shipped (GROWTH-811), named LLM bots requesting `/docs/guides/*` get rewritten to the markdown handler, which returns a 404 when no `.md` file exists. About 90K of those 404s per day land on real pages that serve HTML 200 fine: the bot gets nothing on a page that works. The root cause is that the docs middleware hardcoded `hasMarkdownVariant: true` for every guide path, so it never checked whether a `.md` actually existed. I fixed it in two layers: 1. A build-time slug manifest makes `hasMarkdownVariant` truthful. Guide pages with no `.md` now fall through to HTML 200 instead of a 404. This is content-source-agnostic and future-proof: a new content source can never silently regress to a 404. 2. A second generator pass emits real markdown for the troubleshooting collection (the largest source, ~70% of the 404 volume), so those bots get clean markdown rather than just HTML. ## Changes - Add a shared `markdown-sources` module: a single source of truth for which slugs get a `.md` (guides + troubleshooting), so the generator output and the manifest cannot drift. - Generate markdown for the troubleshooting collection (196 pages, TOML frontmatter parsed via `smol-toml`), written under `public/markdown/guides/troubleshooting/`. - Emit a build-time slug manifest (a gitignored generated `.ts` module, regenerated in `prebuild`, `predev`, and `pretypecheck`, mirroring the existing `__generated__/graphql.ts` lifecycle). - Gate the middleware's `hasMarkdownVariant` on the manifest: serve HTML 200 instead of a 404 for guide paths with no markdown variant. This PR intentionally does not generate markdown for the ai-prompts, YAML config, and externally-fetched (splinter) sources. The HTML fallback covers them now; generating their markdown is follow-up work. ## Testing Local verification (deterministic, against the real manifest and the real negotiation function): - Manifest invariant holds: 744 manifest slugs equal 744 generated `.md` files. - Generator emits 196 troubleshooting files with zero warnings, frontmatter stripped, no leaked delimiters. - Negotiation decision matrix, 6/6: covered slug + bot UA to markdown; uncovered real page + bot UA to pass (HTML 200); nonexistent + bot UA to pass; browser to HTML; covered + `.md` suffix to markdown; uncovered + `.md` suffix to pass. Verified on the Vercel preview deploy: - [x] `User-Agent: ChatGPT-User` on a troubleshooting page returns `200 text/markdown` (real markdown body, frontmatter stripped). - [x] `User-Agent: ChatGPT-User` on an uncovered real page (`ai-tools/ai-prompts/code-format-sql`) returns `200 text/html` (was 404). - [x] Browser request to the same uncovered page returns `200 text/html` (unchanged for humans). - [x] `User-Agent: ChatGPT-User` on a covered standard guide returns `200 text/markdown` (no regression). - [x] `User-Agent: ChatGPT-User` on a nonexistent guide URL returns `404` (correct). Known limitation: an explicit `.md`-suffix request on an uncovered page still 404s by design (an explicit markdown request for a page that has no markdown). The ~90K/day volume is plain-URL UA-based, so it is unaffected. Post-deploy, I will re-run the request-grain 404 reclassification in the GROWTH-915 BQ workspace to confirm fixable guide markdown 404s drop to near zero. ## Linear - fixes GROWTH-946 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added generated markdown slug tracking for docs guides, improving markdown availability detection. * Added automated manifest generation and validation during docs build and CI workflows. * **Bug Fixes** * Improved guide markdown negotiation so only supported guide slugs are treated as having a markdown variant. * Standardized markdown source handling for guides and troubleshooting pages. * **Tests** * Added coverage for guide and troubleshooting slug generation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com> |
||
|
|
0a797ef4ea |
feat(studio): add creation funnel telemetry (#47291)
## Summary Adds frontend funnel telemetry to the organization-creation and project-creation flows in Studio, so each is measurable as a funnel (form exposed → completed) entirely from frontend events. Feeds the KPI 3 FE Benchmark Friction dashboard. Org creation had zero frontend funnel events before this (only a backend event that fires across every surface), and project creation had no clean form-view impression. ## Changes - Define `organization_creation_form_exposed`, `organization_creation_completed`, and `project_creation_form_exposed` in the telemetry constants. - Fire `organization_creation_form_exposed` when the new-org form renders, gated on the profile resolving so pre-auth redirects are not counted. Fire `organization_creation_completed` from the create success callback, covering both the free and the paid pending-payment-intent paths, attaching the new org slug as the organization group. - Fire `project_creation_form_exposed` once the org and the create-project permission have resolved, so it anchors on the form being visible rather than the route loading. Project completion reuses the existing client-side success event, so no duplicate completion event was added. ## Notes I chose exposed → completed over exposed → submitted. The org slug only exists after the create API resolves, so the completion event is the only org-funnel event that can carry the organization group; a submit-time event cannot, which would break org-level segmentation. A pageview is not a sufficient exposure anchor either: pageview capture is off, and the manual pageview fires on route change before the form is interactive (pre-auth redirect, async permission load, the no-org redirect). The `completed` verb follows the repo's approved-verb list (`.claude/skills/telemetry-standards`); the repo previously migrated `branch_merge_succeeded` to `branch_merge_completed` for the same reason. ## Testing Tested on the preview deploy: - [x] `/dashboard/new` while signed in → `organization_creation_form_exposed` fires once. - [x] Create a free org → `organization_creation_completed` fires with the organization group set. - [x] `/dashboard/new/[slug]` with create permission → `project_creation_form_exposed` fires once with `surface=main` and the organization group. - [x] No event re-fires on re-render or tab refocus. Post-deploy: confirm in PostHog prod (project 34344) via HogQL that each event fires with the expected properties and the organization / project group set. ## Linear - fixes FE-3690 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added improved tracking for organization and project creation flows, including when forms are shown and when organization creation completes. * Captures creation metadata to support better reporting on onboarding and setup progress. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2d0bcd4714 |
feat(telemetry): classify funnel creation errors (#47293)
## Summary The KPI-3 friction dashboard needs to know *why* users hit errors on the signup, project-creation, and org-creation funnels, not just that they did. The existing `dashboard_error_created` event already fires for these paths (10% sampled, with `$pathname`), but carries no reason: ~98.5% of events have no `errorType` and no property carries an error message. This adds PII-safe classification computed client-side from a controlled vocabulary, so raw error text never leaves the browser. Validation errors (previously invisible, since they are inline form errors that never raise a toast) are now captured on invalid submit. ## Changes - Extend `dashboard_error_created` with `origin`, `errorCategory`, `errorReason`, `errorCode`, and a `form` source value - Add a pure, unit-tested classifier (`funnel-errors.ts`) and a 10%-sampled tracking hook (`use-track-funnel-error.ts`); the classifier maps errors to stable slugs and emits only slugs + HTTP status, never raw message text - Classify signup errors (API failures + validation) in `SignUpForm` - Classify project-creation errors (API failures, OrioleDB guard, validation) in the new-project wizard - Classify org-creation errors (API failures, payment/card declines, confirm-subscription, validation) in `NewOrgForm` ## Testing 13 unit tests cover every classifier branch (validation / api / network / payment, status-code handling, message-pattern matching, and fallbacks). To verify on the Vercel preview (events are 10% sampled; set the sample rate to 1 locally to observe each fire): - Signup with a weak but non-empty password: `origin=signup, source=form, errorCategory=validation, errorReason=password_invalid` - Signup with an already-registered email: `origin=signup, source=toast, errorCategory=api, errorReason=email_already_registered` - New project with an empty name: `origin=project_creation, source=form, errorReason=project_name_invalid` - New org with an empty name: `origin=org_creation, source=form, errorReason=org_name_missing` - New org with a declined test card: `origin=org_creation, errorCategory=payment` PII: raw `error.message` is never sent; only controlled slugs and HTTP status. Dashboard consumers must filter `origin IS NOT NULL` so these do not collide with the generic toast events the global tracker still emits. ## Linear - fixes FE-3691 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added improved, categorized telemetry for signup, project creation, and organization creation errors, including payment, subscription-change, and validation failures. * Extended dashboard error events with optional structured diagnostics (origin, category, reason, and optional error code) and support for form-origin reporting. * **Bug Fixes** * Improved project-creation handling to record a validation telemetry event when an Oriole image is unavailable. * Ensured payment-related and subscription-change failures are captured consistently alongside existing user toasts. * **Tests** * Added unit tests covering API/network/validation/Stripe error classification and reason mapping. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d549e1f16b | fix(billing): clarify averaged usage messaging (#47181) | ||
|
|
4c636e2074 | fix(studio): track homepage service-card clicks (#47210) | ||
|
|
af9e531abc | chore(docs): gitignore extracted public/docs/ markdown (#47208) | ||
|
|
dc5ddd8c4c |
chore(telemetry): align event interface names with action strings (#47048)
## Summary Aligns 16 telemetry event interface identifiers in `packages/common/telemetry-constants.ts` so each interface name equals the PascalCase of its `action` string (`PascalCase(action) + Event`). This is a follow-up to the GROWTH-798 audit (#45964), which fixed the `action` strings and several interface names but left these 16 identifiers mismatched. I renamed identifiers only: every `action` string is untouched, so there is zero impact on PostHog event names or historical data. Before this change, 176/192 interfaces matched the convention. This brings it to 192/192. ## Changes Structural renames (interface name was dropping or reordering words vs the action): - `AskAIEvent` to `AskAiClickedEvent` - `CopyAsMarkdownEvent` to `CopyAsMarkdownClickedEvent` - `DocsRecommendation404ClickedEvent` to `Docs404RecommendationClickedEvent` (from #46990) - `EventPageCtaClickedEvent` to `WwwEventPageCtaClickedEvent` (completes the interface side of GROWTH-798 HIGH #1) - `ImportDataFileAddedEvent` to `ImportDataDropzoneFileAddedEvent` (also updates the consumer `apps/studio/hooks/ui/useCsvFileDrop.ts`) - `QueryPerformanceAIExplanationButtonClickedEvent` to `QueryPerformanceExplainWithAiButtonClickedEvent` Initialism casing (normalized to the file-majority lowercase transform; `Sql` 9:2, `Api` 3:2, `Ai` 6:2): - `CustomReportAddSQLBlockClickedEvent` to `CustomReportAddSqlBlockClickedEvent` - `CustomReportAssistantSQLBlockAddedEvent` to `CustomReportAssistantSqlBlockAddedEvent` - `HomepageGitHubButtonClickedEvent` to `HomepageGithubButtonClickedEvent` - `MetricsAPIBannerCtaButtonClickedEvent` to `MetricsApiBannerCtaButtonClickedEvent` - `MetricsAPIBannerDismissButtonClickedEvent` to `MetricsApiBannerDismissButtonClickedEvent` - `TableRLSEnabledEvent` to `TableRlsEnabledEvent` - `RLSGeneratePoliciesClickedEvent` to `RlsGeneratePoliciesClickedEvent` - `RLSGeneratedPolicyRemovedEvent` to `RlsGeneratedPolicyRemovedEvent` - `RLSGeneratedPoliciesCreatedEvent` to `RlsGeneratedPoliciesCreatedEvent` - `RLSTesterRunQueryClickedEvent` to `RlsTesterRunQueryClickedEvent` ## Testing Type-only change, no runtime or PostHog behavior to exercise. Verified that all 192 interfaces now match `PascalCase(action) + Event` (0 mismatches), the `TelemetryEvent` union has no duplicates, no old identifier names remain anywhere in the repo, and the one external consumer (`useCsvFileDrop.ts`) still resolves via its `['action']` indexed access since the action strings are unchanged. ## Linear - fixes GROWTH-928 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated internal telemetry infrastructure for consistency and maintainability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
91982e6a2d |
feat(studio): track unified logs cta variant and auto-dismiss on explore (#46940)
## Summary The Unified Logs promo banner (shipped in #46847) had two telemetry/UX gaps I found while auditing the weekly PostHog event review. Its CTA fired one event for two different user paths with no way to tell them apart, and clicking "Explore" left the banner in place. This adds an `is_enabled` property to the CTA event and auto-dismisses the banner on the Explore path. ## Changes - Add `is_enabled: boolean` to `unified_logs_banner_cta_button_clicked`. It is `true` when the user is already enabled and the button navigates to the logs page ("Explore"), and `false` when not enabled and the button opens the feature-preview modal ("Enable"). The two cohorts are now queryable independently, which is what makes the CTA data usable for measuring adoption. - Auto-dismiss the banner when an already-enabled user clicks "Explore". Previously only the X button dismissed it, so an Explore click left the banner showing on the next project page load. Scoped to the Explore path on purpose: the not-enabled path only opens a preview modal (it does not enable), so dismissing there would hide the banner from users who never enabled. ## Testing Behavior to verify on the Vercel preview: - [x] Enabled user clicks "Explore Unified Logs": navigates to the logs page, banner does not reappear on the next project page load, CTA event fires with `is_enabled` true. - [x] Non-enabled user clicks "Enable Unified Logs": preview modal opens, banner is still present after closing the modal, CTA event fires with `is_enabled` false. - [x] X button: banner dismissed as before, dismiss event fires. Out of scope on purpose: no impression event (it would fire on every banner render, low-millions of events per month for one banner), so true click-through rate stays unmeasurable for now. ## Linear - fixes GROWTH-925 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Unified Logs banner now intelligently updates behavior based on feature state. * When enabled, exploring the feature automatically dismisses the banner. * When disabled, the enable action opens the feature preview flow. * Enhanced tracking for banner interactions. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
cd52669f1f |
fix(docs): negotiate /guides/* markdown via shared helper (#45432)
## Summary
This brings docs `/guides/*` to full content negotiation for AI agents
(GROWTH-811):
RFC 9110 q-value parsing instead of a `.includes('text/markdown')`
substring match,
a 406 when the client rejects every type the route can produce, and
markdown rewrites
for known LLM user agents.
I implemented it by extracting the negotiation into a shared
`common/markdown-negotiation`
module consumed by both `apps/docs/middleware.ts` and
`apps/www/middleware.ts`, rather than
duplicating the helpers into docs and keeping them in sync by hand with
www (#45394). Single
source of truth, no re-sync burden. www is refactored onto the shared
helper with no behavior
change.
## Changes
### docs `/guides/*` content negotiation (GROWTH-811)
- Replace the `.includes('text/markdown')` substring match with RFC 9110
q-value parsing.
- Return 406 (`Cache-Control: no-store`, `Vary: Accept`) when Accept
excludes every type the
route serves. Bypassed for LLM user agents, the `.md` suffix, and
clients sending no Accept.
- Rewrite to `/api/guides-md/<slug>` for LLM user agents (Claude-User,
Claude-Web, ChatGPT-User,
PerplexityBot) regardless of Accept.
- Preserve the existing `.md` suffix routing and the entire
`/reference/*` block.
### Shared negotiation helper
- New `packages/common/markdown-negotiation.ts`:
`negotiateMarkdown(signals, route)` returns
`'markdown' | 'not-acceptable' | 'pass'`. Internalizes q-value parsing,
the LLM user-agent
match, the UA-length cap, and the markdown-vs-html preference.
- `apps/www/middleware.ts`: refactored to consume the shared helper; its
duplicated copy of the
negotiation helpers (added in #45394) is removed. `.md` early-return,
changelog routing, and
first-referrer cookie stamping are unchanged (no behavior change,
covered by its existing tests).
### Tests
- New `apps/docs/middleware.test.ts`: q-value priority, the 406 path,
`.md` suffix, LLM UA
override, browser default Accept, training-crawler and substring-embed
exclusion, and the
`/reference/*` exemption.
- New `packages/common/markdown-negotiation.test.ts`: the same decision
matrix at the unit level
(q-values, 406, LLM UAs, `.md`, `*/*`, training crawlers, OWS,
out-of-range q).
## Testing (Vercel preview)
After Vercel posts a preview URL, save it once then run the probe set.
```bash
echo 'PREVIEW_HOST' > /tmp/growth-811-host.txt
HOST=$(cat /tmp/growth-811-host.txt)
# 1) Browser-style Accept -> HTML 200
curl -sI -A "Mozilla/5.0" \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' \
"https://$HOST/docs/guides/auth"
# 2) Accept: text/markdown -> markdown 200
curl -sI -H 'Accept: text/markdown' "https://$HOST/docs/guides/auth"
# 3) text/html;q=1.0, text/markdown;q=0.5 -> HTML 200
curl -sI -H 'Accept: text/html;q=1.0, text/markdown;q=0.5' "https://$HOST/docs/guides/auth"
# 4) unsupported Accept -> 406 + Cache-Control: no-store + Vary: Accept
curl -sI -H 'Accept: application/x-content-negotiation-probe' "https://$HOST/docs/guides/auth"
# 5) User-Agent: Claude-User/1.0 (any Accept) -> markdown 200
curl -sI -A 'Claude-User/1.0' "https://$HOST/docs/guides/auth"
```
### After merge
Run
[acceptmarkdown.com/readiness-check](https://acceptmarkdown.com/readiness-check)
against `https://supabase.com/docs/guides/auth`: expect 100/100.
## Linear
- fixes GROWTH-811
|
||
|
|
bf81e46173 | chore: update DPA to June 2026 version (#46558) | ||
|
|
f8a3a2e28c | feat(billing): label logs ingest/query in restriction banners (#46609) | ||
|
|
7f5e3cab93 | chore(studio): remove expired Fly.io deprecation banner (#46535) | ||
|
|
9da249135c | fix(telemetry): fix and unify dashboard_error_created tracking (#46537) | ||
|
|
b42bebf4c9 |
chore(llms): consolidate /llms.txt to single root path (#46468)
## Summary Removes the preemptive 301 redirects from `/docs/llms.txt` and `/docs/llms-full.txt` to root, and switches the docs homepage `rel=alternate` to an absolute URL. The `/docs/*` paths never had a producer in `apps/docs` (no route handler, no static file at `public/llms.txt` or `public/llms-full.txt`); the redirects were cruft from before the root paths were canonical. Now `/docs/llms*` cleanly 404s and the only advertised path is the root canonical per llmstxt.org. ## Changes - Delete `/docs/llms.txt` and `/docs/llms-full.txt` 301 entries from `apps/www/lib/redirects.js` - Switch `apps/docs/app/page.tsx` rel=alternate `text/markdown` from relative `/llms-full.txt` to absolute `https://supabase.com/llms-full.txt`, so `basePath: '/docs'` does not reconstruct the now-dead `/docs/llms-full.txt` path ## Testing Verify on Vercel preview: - [ ] `curl -sI -L <preview>/docs/llms.txt` returns 404 (no producer, redirect removed) - [ ] `curl -sI -L <preview>/docs/llms-full.txt` returns 404 - [ ] `curl -sI -L <preview>/llms.txt` returns 200 with `content-type: text/plain` - [ ] `curl -sI -L <preview>/llms-full.txt` returns 200, ~9 MB body - [ ] `curl -s <preview>/docs/ | grep 'rel="alternate".*llms-full'` shows the absolute `https://supabase.com/llms-full.txt` href (no `/docs/` prefix) Caveat: the absolute alternate URL points at production from preview deploys. Acceptable because Vercel previews are `noindex` by default and the existing `canonical: BASE_PATH` already crosses environments via basePath resolution. ## Linear - fixes GROWTH-881 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated documentation URL references to use absolute Supabase-hosted paths * Reorganized legacy product documentation redirects to new markdown-based routes <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46468?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
43bacd2f70 |
fix(www): use service role key for LWX edge function storage uploads (#46411)
## Summary The three LWX edge functions consume `MISC_USE_ANON_KEY` while their inline comments claim "SERVICE ROLE KEY": a copy-paste bug. This PR swaps them to `MISC_USE_SERVICE_ROLE_KEY`, matching `lw11-og` (the reference implementation that already uses the service role pattern correctly). The secret is already configured on the project. Service role bypasses RLS, so uploads succeed regardless of bucket policy and the key is never exposed (edge functions are server-side). ## Changes - `lwx-ticket/handler.tsx`: swap storage client to use `MISC_USE_SERVICE_ROLE_KEY` - `lwx-og/handler.tsx`: swap storage client to use `MISC_USE_SERVICE_ROLE_KEY` - `lwx-ticket-og/handler.tsx`: swap storage client to use `MISC_USE_SERVICE_ROLE_KEY` ## Testing (Vercel preview) 1. Hit an LWX ticket share URL with a Twitterbot UA: `curl -A 'Twitterbot/1.0' '<preview>/launch-week/x/tickets/<username>'` — expect HTML with OG meta tags, no edge function error. 2. Confirm the storage object got upserted at `images/lwx/og/<type>/<username>.png` on `obuldanrptloktxcffvn`. 3. Confirm `MISC_USE_SERVICE_ROLE_KEY` is set as an edge function secret on the project (it already powers `lw11-og`, so it should be). ### Follow-up (separate, manual) After merge and deploy, the storage policy on the `images` bucket needs tightening via the dashboard (no migration setup exists for this project): revoke `INSERT`/`UPDATE`/`DELETE` policies that grant `anon`, keep `SELECT` public. All legitimate writers (dashboard team uploads, LWX/LW11 edge functions) use service role and will continue to work. ## Linear - fixes GROWTH-882 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated backend authentication configuration for image generation and ticket-related operations. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46411?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
47c084e51d |
refactor(studio): migrate telemetry to useTrack (#46140)
## Summary
I migrated every `useSendEventMutation` call site in `apps/studio` to
`useTrack`, deleted the legacy hook, and added a lint guardrail so it
can't return. `useTrack` is the type-safe replacement: it auto-injects
`groups: { project, organization }` from the selected project/org and
types `action` + `properties` against `TelemetryEvent`. Existing call
sites built groups manually and were not type-checked at the action
level. The migration covers 81 files (60 trivial swaps, 9 org-only, 3
pre-auth, 5 bespoke, 4 test mocks).
## Changes
- Migrated trivial call sites across `pages/project/[ref]`,
`components/interfaces/*` (Reports, Storage, Realtime/Inspector,
SQLEditor, Functions, EdgeFunctions, Integrations, ProjectAPIDocs,
Branching/BranchManagement, TableGridEditor, Connect, Docs, Auth,
Support, Home, ProjectHome, App), `components/layouts/*`, and
`components/ui/*`.
- Migrated org-only sites (`Organization/Documents/*`,
`Organization/BillingSettings/Subscription/*`,
`Organization/SecuritySettings.tsx`,
`Account/Preferences/DashboardSettingsToggles.tsx`) by dropping the
manual `groups: { organization: ... }` and letting `useTrack`
auto-inject. Verified `useSelectedProjectQuery` is disabled on org
routes (gates on URL `[ref]`).
- Migrated pre-auth sites (`SignInForm.tsx`, `sign-in-mfa.tsx`,
`profile.tsx`) where neither project nor org is resolved.
- Bespoke handling:
- `execute-sql-mutation.ts` and `table-row-create-mutation.ts`: pass `{
project: projectRef }` via `groupOverrides` since the mutation can
target a non-selected project ref.
- `useStudioCommandMenuTelemetry.ts`: kept a direct `sendTelemetryEvent`
call because studio groups must override pre-built event groups
(opposite of `useTrack`'s override direction).
- `AIAssistantOption.tsx`: passes sentinel-aware `groupOverrides` so
`NO_PROJECT_MARKER`/`NO_ORG_MARKER` continue to suppress group emission.
- `SidePanelEditor.utils.tsx`: utility functions `createTable` and
`updateTable` now take a `track: Track` parameter (threaded from
`SidePanelEditor.tsx`); dropped the `organizationSlug` arg since groups
are no longer assembled manually.
- Branch-event attribution: preserved `parentProjectRef` overrides on
`branch_updated`, `branch_merge_completed`, `branch_merge_failed`,
`branch_merge_submitted`, `branch_delete_button_clicked`,
`branch_review_with_assistant_clicked`, and
`branch_*_merge_request_button_clicked`. Original code grouped these
under the parent (production) project, not the branch ref;
auto-injection would have shifted them onto the branch.
- Switched 4 test mocks from `@/data/telemetry/send-event-mutation` to
`@/lib/telemetry/track`. Removed obsolete tests around manual groups and
`try/catch` on telemetry rejection.
- Deleted `apps/studio/data/telemetry/send-event-mutation.ts`. The
deleted module is its own guardrail: any reintroduction of the import
fails at TypeScript module resolution before lint runs.
## Testing
Tested on preview deploy:
- [x] SQL editor `CREATE TABLE` fires `table_created` with method
`sql_editor` and `groups.project` set to the mutation's `projectRef`.
- [x] Table editor creates a table from the side panel; `table_created`
fires from `SidePanelEditor.utils` via threaded `track`.
- [x] Help button (`/project/[ref]/...`) fires `help_button_clicked`
with auto-injected project + org groups.
- [x] Sign-in form fires `sign_in` with empty groups (pre-auth,
expected).
- [x] Org documents page (`/org/[slug]/documents`) fires
`document_view_button_clicked` with org group only, no stale project
ref.
- [x] Command menu (`Cmd+K`) inside a project still fires
`command_menu_opened` with studio's project/org overriding any
event-supplied groups.
- [x] Support form "Ask the Assistant" without selected org fires
`ai_assistant_in_support_form_clicked` with no project/org groups
(sentinels suppress).
- [x] On a branch, "Update branch" / "Merge branch" / "Close merge
request" events fire with `groups.project` set to the parent project
ref, not the branch ref.
Local checks:
- [x] 22/22 tests pass across the 4 updated test files
(`SidePanelEditor.utils.createTable`, `EdgeFunctionRenderer`,
`LayoutSidebar`, `PlanUpdateSidePanel`).
- [x] `rg useSendEventMutation apps/studio` returns 0 hits.
## Linear
- fixes GROWTH-860
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Chores**
* Standardized telemetry across the Studio to a unified tracking system;
events now send simplified payloads with less contextual/grouping data.
* No user-facing flows changed; UI behavior, permissions, and
interactions remain the same.
* **Tests**
* Updated telemetry mocks and tests to align with the new tracking
approach.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46140?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
0bed80b340 |
chore(telemetry): clean up frontend event catalog (#45964)
## Summary Resolves 13 findings (2 HIGH, 5 MEDIUM, 6 LOW) from the frontend telemetry audit: 1 action-string collision, 1 camelCase experiment name, 9 dead events removed, 4 missing org groups attached, 1 ambiguous property renamed, 1 raw-string property narrowed, plus consolidations and a structural tightening on TABLE_EVENT_ACTIONS. ## Changes ### HIGH - Rename `EventPageCtaClickedEvent.action` to `www_event_page_cta_clicked` so it no longer collides with the pricing CTA event (which had a different schema sharing the same action string) - Snake_case the header-upgrade experiment exposure name (`headerUpgradeCta_experiment_exposed` → `header_upgrade_cta_experiment_exposed`); PostHog flag key and `?source=` URL param unchanged ### MEDIUM - Remove 4 dead `ProjectCreation*Step*` events (referenced a v2 route that doesn't exist; 0 emissions) - Remove 4 dead experiment exposure events: `ProjectCreationRlsOptionExperimentExposed`, `HomeNewExperimentExposed`, `TableCreateGeneratePoliciesExperimentExposed`, `TableCreateGeneratePoliciesExperimentConverted` (0 emissions) - Attach org group to `dpa_request_button_clicked` (0% had `$group_0` per Hex) - Delete `RegisterStateOfStartups2025NewsletterClicked` (interface naming outlier, 0 emissions, page renamed to 2026) - Rename `AssistantSuggestionRunQueryClickedEvent.category` to `mutationType` with tightened literal union (`'functions' | 'rls-policies' | 'unknown'`) - Attach org group to `project_creation_default_privileges_exposed` on Vercel surface via explicit `groupOverrides` (auto-injection misses because `useSelectedOrganizationQuery` is undefined on that page) ### LOW - Consolidate `IndexAdvisorBannerEnableButtonClickedEvent` + `IndexAdvisorDialogEnableButtonClickedEvent` into one event with `origin: 'banner' | 'dialog'` - Rename `ImportDataFileDroppedEvent` → `ImportDataFileAddedEvent` so the interface name matches the action and the verb is on the approved list - Rename `LogDrainConfirmButtonSubmittedEvent` → `LogDrainRemovedEvent` and action to `log_drain_removed` (fires on delete-confirm modal, matches `CronJobRemovedEvent` pattern) - Add `type` property to `CronJobRemovedEvent` (parsed from the job's command), matching the create/update event shape - Tighten `TABLE_EVENT_ACTIONS` values with `satisfies` against the event union so renames in the union fail typecheck here too - Attach org group to `www_pricing_plan_cta_clicked` at 5 emission sites when an org is available in the page context - Narrow `unified_logs_row_clicked.logType` from raw `string` to the 5-literal `LOG_TYPES` union (zod already validates server values) ### Bundled refactor Migrated 5 emission sites from deprecated `useSendEventMutation` to `useTrack` while their containing files were being edited: `DPA.tsx`, `DisplayBlockRenderer.tsx`, `Grid.tsx` (2 events), `DeleteCronJob.tsx`. Full sweep of the remaining ~79 files is a separate follow-up. ## Testing Mostly just renaming of events ## Linear - fixes GROWTH-798 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Standardized telemetry to a unified tracking system for more consistent analytics. * Simplified experiment exposure reporting for upgrade prompts. * **New Features** * More granular tracking for CSV import, cron job deletions, log drain removals, DPA downloads/requests, and pricing CTAs. * Assistant now classifies mutation queries more precisely. * **Bug Fixes** * Improved default-privileges exposure logic on Vercel deployments (skips when org missing). <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45964) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e55411da5e |
feat(studio): Fly.io deprecation banner (#45778)
## Summary Adding an in-dashboard banner for the Fly.io May 31 suspension. Banner targets users on a Fly project (or with a Fly project in their currently-selected org) and surfaces a per-project breakdown of what's affected in a dialog. Detection is self-correcting: as soon as the user migrates off Fly, the banner disappears with no follow-up. <img width="557" height="502" alt="Screenshot 2026-05-11 at 5 08 22 PM" src="https://github.com/user-attachments/assets/7bafb712-3490-4555-9667-66e9909f1b1a" /> <img width="1675" height="536" alt="Screenshot 2026-05-11 at 3 55 06 PM" src="https://github.com/user-attachments/assets/6c1bf9d1-4dcc-4aac-a679-2ed477d2ed1c" /> ## Changes - **Detection hook** (`useFlyDeprecationProjects`): reads only from already-cached data — `useSelectedProjectQuery` for the current project, plus `useOrgProjectsInfiniteQuery` scoped to the selected org. Zero cross-org fan-out: worst case is one paginated query per session (the same one the project list page already makes). - **Banner component** (`FlyDeprecationBanner.tsx`): mounted in `AppBannerWrapper`. Dynamic title (primaries / branches / both), dialog lists affected projects with org name, numbered migration steps, links to backup/restore CLI + Dashboard backup + branching docs. List truncates to 5 entries with "…and N more." tail when more are affected. - **Telemetry**: `fly_deprecation_banner_exposed` and `fly_deprecation_banner_dismissed` events emitted via `useTrack` (auto-injects project + org groups). Properties: `primaryCount`, `branchCount`. CTA click tracking intentionally omitted — migration outcome is measured via warehouse `cloud_provider = 'FLY'` decay. - **LocalStorage**: dated dismissal key `FLY_DEPRECATION_2026_05_31`; orphan `FLY_POSTGRES_DEPRECATION_WARNING` from PR #33510 removed in the same change so users who dismissed the Feb 2025 banner still see this one. - **Support contact**: email `success@supabase.io` only (no support ticket link), per Brian's outreach copy in the Linear issues. ## Coverage trade-off Banner renders on project pages (selected-project check) and pages where the selected org's projects list is cached (org overview, project list). It does **not** render on `/dashboard` home or other pages without org context. Email outreach from GROWTH-817 / GROWTH-819 handles those users. This was a deliberate trade-off to avoid cross-org fan-out load. ## Lifecycle Banner expires `2026-06-01T00:00:00Z` (right after the May 31 deadline). Stale client bundles stop rendering it without a redeploy. Cleanup PR planned post-deadline to remove the component, hook, localStorage key, and telemetry events. ## Testing Tested on the Vercel preview with React Query cache overrides to mock a Fly project: - [x] Banner renders for a user with at least one project where `cloud_provider === 'FLY'` - [x] Banner does **not** render for a user with no Fly projects - [x] Banner does **not** render on `/sign-in` - [x] Title varies by primaries-only / branches-only / both - [x] Dialog lists affected projects with org name in parens - [x] Dialog list truncates to 5 with "…and N more." for larger sets - [x] Migration guide / Dashboard backup / branching links open in a new tab - [x] Dismiss (×) closes the banner and persists across hard reload (localStorage `fly-deprecation-2026-05-31-dismissed`) - [x] PostHog receives one `fly_deprecation_banner_exposed` per mount with `primaryCount` + `branchCount` and `$groups.organization` populated - [x] PostHog receives one `fly_deprecation_banner_dismissed` on close with the same property shape ## Linear - fixes GROWTH-817 - fixes GROWTH-819 |
||
|
|
95d1e8abe8 |
fix(www): drop malformed legal URLs from sitemap_www.xml (#45775)
## Summary Google Search Console flagged 4 "URL not allowed" errors on `sitemap_www.xml` — malformed URLs like `https://supabase.comdata/legal/terms/v1` (missing slash, non-existent path). The generator was globbing `data/**/*.mdx`, picking up the 4 content-source MDX files under `data/legal/` that are imported by `pages/terms.tsx` and `pages/enterprise-terms.tsx` but are not themselves routed. With no path replacement mapping `data/...` to a route and no leading slash, the URL template concatenated to garbage. The real `/terms` and `/enterprise-terms` URLs come from the `pages/*.tsx` glob and are unaffected. ## Changes - Remove `data/**/*.mdx` glob (and its companion `!data/*.mdx` exclude) from the sitemap generator. `apps/www/data/` has no routed MDX, only content sources imported into pages. - Anchor the `pages` prefix replace: `.replace('pages', '')` → `.replace(/^pages/, '')`. String-form replace is first-occurrence and would mangle any future filename containing `pages` as a non-prefix substring (e.g., `_blog/about-pages.mdx` → `/blog/about-`). No current files trigger this; defensive hardening. ## Testing Regenerated the sitemap locally and verified: - [x] `grep -c "supabase.comdata" public/sitemap_www.xml` → `0` (was 4) - [x] `<loc>https://supabase.com/terms</loc>` and `<loc>https://supabase.com/enterprise-terms</loc>` still present - [x] Every `<loc>` matches `^<loc>https://supabase\.com(/[a-zA-Z0-9].*)?</loc>$` (no malformed URLs of any kind) - [x] Total loc count stable across both commits (regression-free for the anchor change) Local count is lower than prod (527 vs 906) because `.next/server/pages/**` partner/expert/feature HTML globs only resolve after a full build — runs correctly via `postbuild` on Vercel. After deploy lands, resubmit `sitemap_www.xml` in Google Search Console to force a re-crawl (otherwise daily-ish). Expect status to flip from "4 errors" to "Success" and Discovered pages: 906 → 902. ## Linear - fixes GROWTH-837 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Chores** * Improved sitemap generation to properly index specific content sections (blog, case studies, customers, events, and alternatives) with refined route path processing for better search engine discoverability. [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45775) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5a5099adba |
fix(docs): always emit BreadcrumbList item field (#45744)
## Summary Eliminates the Google Search Console "Missing field 'item' (in 'itemListElement')" critical error on 230 `/docs/guides/*` pages. The schema was emitting `ListItem`s without an `item` field for intermediate category nodes that lack a URL in the docs nav. Per [Google's spec](https://developers.google.com/search/docs/appearance/structured-data/breadcrumb), `item` is required on every BreadcrumbList position except the last leaf — so url-less items are filtered out instead. Also fixes a smaller quality gap surfaced during preview verification: the `auth` section root in `NavigationMenu.constants.ts` was missing a `url`, so auth trails were dropping the "Auth" breadcrumb level (`Docs > Guides > JSON Web Tokens (JWT) > Overview` instead of `Docs > Guides > Auth > JSON Web Tokens (JWT) > Overview`). Every other section root already has a `url`; auth was the lone outlier. ## Testing Tested locally via vitest (`pnpm --filter docs exec vitest run lib/json-ld.test.ts`): - [x] All-urls chain: every `itemListElement` has string `item` and `name` - [x] Leaf-url-mismatch: leaf uses `pathname` even when the chain leaf URL differs - [x] All-url-less chain: returns `null` - [x] Empty chain: returns `null` Tested on the preview deploy against 7 representative GSC-flagged paths: - [x] `/docs/guides/getting-started/ai-prompts` — 4 positions, 0 missing - [x] `/docs/guides/getting-started/ai-skills` — 4 positions, 0 missing - [x] `/docs/guides/auth/jwts` — 4 positions, 0 missing (after auth fix: includes "Auth") - [x] `/docs/guides/auth/social-login/auth-google` — 4 positions, 0 missing (after auth fix: includes "Auth") - [x] `/docs/guides/database/postgres-js` — 4 positions, 0 missing - [x] `/docs/guides/storage/quickstart` — 4 positions, 0 missing - [x] `/docs/guides/platform/migrating-within-supabase/dashboard-restore` — 5 positions, 0 missing Post-merge: - [ ] validator.schema.org against deployed URL: 0 errors - [ ] GSC "Validate fix" on the breadcrumb issue (1-2 week re-crawl window) ## Linear - fixes GROWTH-835 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved breadcrumb validation to filter incomplete entries and avoid broken documentation links. * Restored root link for the Auth navigation section so the Auth menu item now navigates to /guides/auth. * **Tests** * Added comprehensive tests covering breadcrumb generation and edge cases. * **Refactor** * Streamlined breadcrumb JSON‑LD schema generation for clearer output and maintainability. [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45744) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
dc0cc42d14 |
chore(www): stop tracking auto-generated rss.xml (#45745)
## Summary `apps/www/public/rss.xml` is committed to the repo AND rewritten on every `pnpm dev`, `pnpm build`, and `pnpm typecheck` run via `content:build` -> `apps/www/scripts/generateStaticContent.mjs`. Anyone working in `apps/www` (or running `pnpm typecheck` from the monorepo root) sees a phantom `M apps/www/public/rss.xml` in `git status` every session. Easy to accidentally commit, otherwise has to be repeatedly stashed. The sibling `changelog-rss.xml` (written by the same script) is already gitignored. This aligns the main blog rss with the same treatment. ## Changes - Add `/public/rss.xml` to `apps/www/.gitignore` (alongside the existing changelog-rss entries; renamed the section header from "Changelog generated feeds" to "Generated feeds" to reflect what it now covers). - `git rm --cached apps/www/public/rss.xml` so git stops tracking the file. The on-disk copy is preserved for local dev. ## Testing No behavior change — the file is still generated by `content:build` and served from `public/` at build time. - [x] `git check-ignore -v apps/www/public/rss.xml` resolves to `apps/www/.gitignore:36` - [x] `apps/www/public/rss.xml` still on disk after `git rm --cached` - [ ] On a fresh clone + `pnpm install + pnpm --filter www build`, the file regenerates and is served at `/rss.xml` on the resulting build (Vercel preview will confirm via deploy). ## Linear - fixes GROWTH-836 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated repository configuration to properly manage auto-generated files. [](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45745) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5ce163fd69 |
feat(docs): add BreadcrumbList JSON-LD to guide pages (#45477)
## Summary Emits `BreadcrumbList` JSON-LD on every `/docs/guides/*` page served by `GuideTemplate`. Search engines and AI crawlers get an explicit hierarchical signal for the docs site (the marketing site already shipped JSON-LD via #45451). The chain prepends `Docs > Guides` to the existing resolver output, so a page like `/docs/guides/auth/passwords` produces a 5-level chain with the leaf URL set per Google's spec. ## Changes - New `apps/docs/lib/breadcrumbs.ts`: pure pathname → chain resolver, server-safe. Extracted from the existing client `useBreadcrumbs` hook so the same logic runs in both contexts. - New `apps/docs/lib/json-ld.ts`: `serializeJsonLd` + `breadcrumbListSchema` mirroring `apps/www/lib/json-ld.ts`. - `Breadcrumbs.tsx` (visual) now delegates to the shared resolver — single source of truth for visual + SEO chains. - `GuideTemplate` takes a required `pathname` prop and emits `<script type="application/ld+json">` next to `<Breadcrumbs />`. Skipped when the chain is empty (e.g., page not in nav menu). Middle items without URLs (e.g., the "Auth" section root) omit `item`, matching the visual breadcrumb. - 8 explicit-prop callers updated; `[[...slug]]` callers already spread `data` (which carries `pathname`). ## Scope **Out of scope:** - `/docs/reference/*` (SDK reference) — no breadcrumbs rendered today, would need separate traversal over spec JSON. - `/guides/troubleshooting/*` — uses its own template, not `GuideTemplate`. - `TechArticle` per-page schema — high maintenance for marginal value. ## Testing (Vercel preview) ```bash curl -s https://<preview>/docs/guides/auth/passwords | grep -oE '<script type="application/ld\+json"[^>]*>[^<]+</script>' ``` Expect a script tag with the chain `Docs > Guides > Auth > Flows (How-tos) > Password-based`, leaf URL `https://supabase.com/docs/guides/auth/passwords`. - [x] `/docs/guides/auth/passwords` — 5-item chain, leaf URL present - [x] `/docs/guides/getting-started/features` — 4-item chain, all items have URLs - [x] `/docs/guides/getting-started/ai-prompts/<slug>` — special-case chain (`Getting started > AI Tools > Prompts > <slug>`), leaf URL falls back to pathname - [x] `/docs/guides/database/database-advisors` (explicit-prop caller) — chain renders - [x] Visual breadcrumb on the same pages still renders correctly - [ ] Validate output through [Google Rich Results Test](https://search.google.com/test/rich-results) on a deployed preview URL - [x] `/docs/guides/troubleshooting/<slug>` — no JSON-LD emitted (different template, intentional) ## Linear - fixes GROWTH-820 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added JSON-LD breadcrumb markup to guide pages to improve search/discovery. * **Improvements** * Centralized breadcrumb generation for consistent, accurate breadcrumbs across guides. * Multiple guide pages updated to ensure breadcrumbs and page context display correctly. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
c26b64a033 |
feat(www): emit BreadcrumbList JSON-LD on marketing surfaces (#45478)
## Summary
- Adds `breadcrumbListSchema(items)` helper to `apps/www/lib/json-ld.ts`
and a hand-curated `apps/www/lib/breadcrumbs.ts` route map.
- Wires inline `<script type="application/ld+json">` BreadcrumbList
blocks into 18 marketing surfaces: blog (index + slug), customers (index
+ slug), events (index + slug), 5 product pages (database, auth,
storage, edge-functions, realtime), 3 modules (vector, cron, queues),
pricing, careers, company, features.
- Pages router callers wrap the script in `<Head>`; app router callers
place it directly in JSX. Dynamic surfaces append a leaf at render time
using the page's title (`frontmatter.title` for blog, `meta_title ??
title` for customers, `event.meta_title ?? event.title` for events).
- Modules sit at `Home > {Name}` since no `/modules` index page exists;
products sit at `Home > {Product}` (no shared products parent). Absolute
`https://supabase.com` URLs match the existing `CANONICAL_ORIGIN`
convention so anchors stay stable across Vercel previews.
Linear:
[GROWTH-822](https://linear.app/supabase/issue/GROWTH-822/add-breadcrumblist-json-ld-to-www-marketing-surfaces)
(sub-issue under
[GROWTH-724](https://linear.app/supabase/issue/GROWTH-724)).
> **Note on branch name:** the branch is
`pamela/growth-820-www-breadcrumb-jsonld`; the actual Linear issue is
GROWTH-822. The branch was named before the sub-issue was created.
Ignore the `820` in the branch.
Explicitly deferred (separate PRs / low SEO ROI): `/launch-week/*`,
`/solutions/*`, `/partners/*`, `/alternatives/*`, `/changelog`,
`/legal/dpa`, `/aws-reinvent-2025`, `/wrapped`, `/contribute/*`,
`/brand-assets`, `/ga`, `/ga-week`, `/state-of-startups*`, and the
homepage (Organization + WebSite already cover homepage entity signals;
single-item BreadcrumbList is ignored by Google).
## Test plan
- [x] On the Vercel preview, `curl -s https://<preview>/database | grep
'"BreadcrumbList"'` returns the script block with `Home > Database`.
- [x] `curl -s https://<preview>/blog/<recent-slug> | grep
'"BreadcrumbList"'` returns `Home > Blog > {post title}`.
- [x] `curl -s https://<preview>/customers/<slug> | grep
'"BreadcrumbList"'` returns `Home > Customer Stories > {customer
title}`.
- [x] `curl -s https://<preview>/events/<slug> | grep
'"BreadcrumbList"'` returns `Home > Events > {event title}`.
- [x] `curl -s https://<preview>/modules/vector | grep
'"BreadcrumbList"'` returns `Home > Vector`.
|
||
|
|
e2480538ad | feat(www): add JSON-LD structured data to homepage, products, and blog (#45451) | ||
|
|
97583f0791 | feat(www): include pricing FAQ in generated pricing.md (#45455) | ||
|
|
db0379f848 | fix(www): normalize blog frontmatter dates to ISO 8601 (#45453) | ||
|
|
5823986e72 | chore(www): explicit AI crawler rules + homepage canonical URL (#45450) | ||
|
|
baabcb189c | feat(www): serve blog, customers, events as .md for AI agents (#45403) | ||
|
|
dff4744805 | fix(www): respect Accept q-values and 406 unsupported types (#45394) | ||
|
|
a98a4928b4 | feat(www): rewrite to md for known llm user agents (#45328) | ||
|
|
99447cdcbe |
chore: gitignore stale committed sitemap_www.xml and ui-library llms.txt (#45289)
## Why Two auto-generated files have been tracked in git but the committed copies are stale because every Vercel deploy overrides them in the build artifact. Both have misled at least one investigation into thinking production was out of date. | File | Generator | Live (prod) | Committed (master) | |---|---|---|---| | `apps/www/public/sitemap_www.xml` | `pnpm postbuild` -> `internals/generate-sitemap.mjs` | 704 URLs, `last-modified` matches latest deploy | 604 URLs, last touched 2025-09-30 (`cb66b6b6`) | | `apps/ui-library/public/llms.txt` | `pnpm build:llms` -> `scripts/build-llms-txt.ts` (wired into `build`) | 57 entries, `Last updated: 2026-04-27` | 56 entries, `Last updated: 2026-02-20` (last touched 2026-03-17, `9bf981f3`) | Production has been correct the whole time. The git copies just look authoritative when they aren't, and they show up in audits and code searches as if they were the live data. The same gitignore pattern is already applied to `apps/www/public/sitemap.xml` at `apps/www/.gitignore:29` — this PR extends the pattern to two more files that should never have been committed. Discovered while investigating AI-bot crawl optimization for `supabase.com/llms-full.txt`. An audit initially flagged "sitemap is stale, missing 44 blog posts" — that turned out to be entirely about reading the dead git file. Same kind of false signal would happen for anyone inspecting the ui-library llms.txt locally. ## What changes **`apps/www`:** - Add `public/sitemap_www.xml` to `apps/www/.gitignore`. - `git rm --cached apps/www/public/sitemap_www.xml`. - Updated the gitignore comment to mention the regeneration path. **`apps/ui-library`:** - Add `public/llms.txt` to `apps/ui-library/.gitignore` (with a comment pointing at the generator). - `git rm --cached apps/ui-library/public/llms.txt`. Both files remain on disk locally; their respective build steps regenerate them as before. ## What does NOT change - Production behavior for either file: postbuild / build:llms remain authoritative. - Generator code: `apps/www/internals/generate-sitemap.mjs` and `apps/ui-library/scripts/build-llms-txt.ts` are untouched. - robots.txt, sitemap index, docs sitemap: unchanged. - `apps/learn/public/llms.txt`: left tracked. Its generator (`apps/learn/scripts/build-llms-txt.ts`) is **not** wired into the `build` script, so it's manually generated and committed — different pattern, leave alone. ## Test plan - [ ] After merge, trigger or wait for next prod deploy. Confirm `https://supabase.com/sitemap_www.xml` still serves a fresh ~700-URL response with current `last-modified` header. - [ ] Confirm `https://supabase.com/ui/llms.txt` still serves a fresh response with `Last updated:` matching the deploy date. - [ ] Confirm `git ls-files apps/www/public/ apps/ui-library/public/` no longer lists either file. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Improved handling of auto-generated sitemap files so they are no longer tracked in the repo after builds. * **Documentation** * Removed the published auto-generated LLMs reference page from the UI library to avoid shipping stale documentation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d409836ca7 |
feat(www,docs): serve marketing pages as .md, advertise via link rel=alternate (#45277)
## Summary Adds `/<page>.md` routes for 10 marketing/product pages (homepage, auth, database, edge-functions, realtime, storage, vector, pricing, modules/cron, modules/queues) so AI agents can fetch clean markdown instead of parsing JS-rendered HTML. Also advertises the markdown alternate via `<link rel="alternate" type="text/markdown">` on marketing and docs pages so agents can discover it. Pricing is generated dynamically via `generatePricingContent()` (single source of truth with `/llms.txt` and `/llms-full.txt`); the other nine slugs are bundled at build time from `content/md/*.md` into a `MD_CONTENT` map. Supersedes #44891 (rebased fresh off current master to avoid a 9-commit replay over rename/rename conflicts created by #44897). ## Changes - New `/api-v2/md/[...slug]` route handler returns the bundled markdown (or dynamic pricing) with `Content-Type: text/markdown`, `X-Content-Type-Options: nosniff`, and appropriate cache headers - Middleware rewrites `/<slug>.md` and `Accept: text/markdown` to the API route for the `MD_PAGES` allowlist; trailing-slash variants (`/auth/`) are normalized so they resolve the same as `/auth` - Build-time codegen `scripts/generateMdContent.mjs` scans `content/md/` and emits `app/api-v2/md/content.generated.ts` exporting both `MD_CONTENT` (Map) and `MD_PAGES` (Set, incl. dynamic `pricing`). Fails the build on slug collision between `content/md/` and `DYNAMIC_SLUGS`. Adding a new marketing `.md` is just dropping a file in `content/md/` (also update `PRODUCT_OVERVIEW_LINKS` in `/llms.txt` since that list is editorial). - 8 permanent redirects `/llms/<product>.txt` → `/<product>.md` so legacy URLs in caches and downstream `llms.txt` copies keep working - `/llms.txt` product overview now references `.md` URLs (incl. `modules/cron`, `modules/queues`); `/llms-full.txt` iterates `MD_CONTENT.values()` (homepage first, then alphabetical) and appends dynamic pricing - `/llms/[slug]` route slimmed to proxy SDK reference files (`js.txt`, `dart.txt`, etc.) since redirects handle product slugs and pricing; pricing branch retained as fallback in case redirects are bypassed - `apps/www/pages/_app.tsx` injects the alternate link conditionally based on `MD_PAGES`; `/pricing` (app router) sets it via page metadata - `apps/docs/app/page.tsx` (the `/docs` root) sets the text/markdown alternate to `/llms-full.txt`; per-guide pages override with their specific `.md` URL via `genGuideMeta` in `GuidesMdx.utils.tsx`. Other docs pages (reference, troubleshooting) inherit nothing. - `apps/www/.vercelignore`: replaces the prior `*.md`/`README.md` rules with `*.md` + `!content/md/**/*.md` so Edge Function READMEs and future scratch `.md` files aren't silently shipped to the build artifact - Drops `apps/www/data/llms/*.txt` and the related `outputFileTracingIncludes` - Test coverage for the new middleware branches: `.md` suffix rewrite (allowlisted vs. fall-through), `Accept: text/markdown` content negotiation, trailing-slash normalization ## Testing (Vercel preview) Local dev server smoke tests passing on `:3771` after each iteration. Re-verified on the preview URL after the latest hardening commit: - [x] `curl -I https://<preview>/llms/auth.txt` — expect `308 Permanent Redirect` to `/auth.md` - [x] `curl https://<preview>/auth.md | head -3` — expect `# Supabase Auth` - [x] `curl https://<preview>/pricing.md | head -3` — expect `# Supabase Pricing` with current tier values - [x] `curl https://<preview>/modules/cron.md | head -3` — expect `# Supabase Cron` - [x] `curl -H 'Accept: text/markdown' https://<preview>/ | head -3` — expect `# Supabase` (homepage.md) - [x] `curl https://<preview>/llms.txt` — Product Overview section lists `.md` URLs and includes Cron + Queues - [x] `curl https://<preview>/llms-full.txt | grep -E '^# Supabase (Cron\|Queues\|Pricing)'` — Cron and Pricing each match once; Queues matches twice (marketing module + existing docs guide) - [x] View source on `/`, `/pricing`, `/database` — expect `<link rel="alternate" type="text/markdown" href="/<slug>.md">` - [x] View source on `/docs` — expect `<link rel="alternate" type="text/markdown" href="/llms-full.txt">` - [x] View source on a docs guide page (e.g., `/docs/guides/auth`) — expect per-guide `.md` alternate; reference/troubleshooting pages should NOT emit a markdown alternate - [x] `curl -I https://<preview>/auth.md` — expect `X-Content-Type-Options: nosniff` - [x] `curl -I -L -H 'Accept: text/markdown' https://<preview>/auth/` — should resolve to markdown content (trailing-slash normalization, with Vercel's auto-redirect) ## Linear - fixes GROWTH-760 ## Follow-up (separate PR) GROWTH-760 also asks about extending `.md` to blog/customers/events. Different mechanism (path-prefix middleware, MDX read at request time via `gray-matter`) so it deserves its own review. Will open a follow-up PR after this lands. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Serve prebuilt and dynamic Markdown docs via new markdown endpoints and routing; pages now advertise markdown alternates (including pricing). * Added Cron and Queues module documentation pages. * **Documentation** * Minor formatting tweaks to Realtime and Storage docs. * **Chores** * Added build-time Markdown content generation and adjusted ignore/deploy rules for generated files. * Added redirects from legacy text-based product URLs to new markdown pages. * **Tests** * Expanded tests for markdown routing and content-negotiation behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2a9555d459 |
feat(docs,www): make supabase.com machine-readable for LLM agents (#44670)
## Summary Makes supabase.com machine-readable for LLM agents and AI crawlers. Adds a combined `llms-full.txt` (product overview + docs), curated product overview `.txt` files, auto-generated pricing content, and updates the `llms.txt` index to reference everything. **Why:** supabase.com had `llms.txt` pointing to per-SDK doc files, but no `llms-full.txt` (every competitor has one), no machine-readable marketing content, and no pricing data agents could parse. Agents evaluating Supabase got docs but no product overview or pricing, leading to inaccurate comparisons. ## What's new ### `llms-full.txt` (auto-generated, combines www + docs) - Product Overview section (~500 lines): homepage, all 6 products, pricing - Documentation section (~122K lines): guides, SDK references, CLI reference - 4.4MB total, regenerated on every docs deploy - Reads product `.txt` files from `apps/www/public/llms/` at build time ### Product overview `.txt` files (`apps/www/public/llms/`) - Curated summaries: homepage, database, auth, storage, edge functions, realtime, vector - `pricing.txt` is auto-generated from `packages/shared-data` (plans, pricing, compute add-ons) via `generateLlmsPricing.mjs`, includes full feature comparison matrix - Other product files are hand-maintained (these pages change ~1x/year per git history) - Reminder comments added to all 7 marketing page source files ### `llms.txt` index (auto-generated) - Two sections: Documentation (existing SDK/guide links) and Product Overview (marketing page links) - Links to `llms-full.txt` for bulk ingestion ### Rewrite changes - Added `/llms-full.txt` rewrite to docs app - Scoped `/llms/*.txt` wildcard to only match docs source slugs (guides, js, dart, etc.), so marketing `.txt` files in `www/public/llms/` are served directly ## Changes - `apps/docs/scripts/llms.ts`: generate `llms-full.txt` combining www product content + docs, update `llms.txt` index with marketing links - `apps/www/lib/rewrites.js`: add `llms-full.txt` rewrite, scope docs proxy to known slugs - `apps/www/public/llms/*.txt`: 8 product overview files (7 hand-curated, 1 auto-generated) - `apps/www/scripts/generateLlmsPricing.mjs`: build script generating pricing.txt from shared-data; uses `getPlanValue()` guard in `buildAddOnsSection` and `buildFeatureComparisonSection` to handle missing plan keys defensively - `apps/www/package.json`: add pricing generation to content:build - `apps/www/pages/*.tsx`: reminder comments for LLM content updates ## Testing Tested locally: - [x] `pnpm run build:llms` generates combined `llms-full.txt` (4.4MB) with Product Overview + Documentation sections - [x] `llms.txt` index has Documentation + Product Overview sections with `llms-full.txt` reference - [x] `pricing.txt` auto-generated from shared-data with correct plan tiers, compute add-ons, disk pricing, and feature comparison - [x] Scoped rewrite regex matches docs slugs but not marketing slugs - [x] Marketing `.txt` files served from `public/` Post-deploy verification: - [ ] `curl https://supabase.com/llms-full.txt` returns combined product + docs content - [ ] `curl https://supabase.com/llms/database.txt` returns product overview (not proxied to docs) - [ ] `curl https://supabase.com/llms/guides.txt` still proxies to docs app - [ ] `curl https://supabase.com/llms/pricing.txt` returns auto-generated pricing ## Maintenance | Content | Auto-updates? | Trigger | |---------|--------------|---------| | `llms-full.txt` | Yes | Every docs deploy | | `llms.txt` index | Yes | Every docs deploy | | `pricing.txt` | Yes | Every www build (reads from shared-data) | | Product `.txt` files (7) | No | ~1x/year, reminder comments in source pages | ## Linear - fixes GROWTH-758 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added comprehensive AI-friendly product overviews (Auth, Database, Edge Functions, Realtime, Storage, Vector) and a detailed pricing document with plan comparisons, add-ons, disk tiers, and feature tables * Added a consolidated "full" markdown output that combines curated product overview content with per-source documentation * **Chores** * Build now generates the richer documentation outputs and pricing automatically * Improved routing so only scoped documentation .txt assets are proxied while others are served directly <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2536593594 |
docs: CLI telemetry disclosure (#44662)
## Summary The CLI now collects usage telemetry (supabase/cli#5019) with opt-out via `supabase telemetry disable` or `SUPABASE_TELEMETRY_DISABLED=1`. The self-hosting docs previously stated "no telemetry" without distinguishing Docker from the CLI. This PR discloses CLI telemetry across docs and regenerates the CLI reference spec to include the new telemetry commands. ## Changes - Update self-hosting telemetry section: Docker has no telemetry, CLI is separate with opt-out instructions and link to full telemetry docs - Regenerate `cli_v1_commands.yaml` from CLI v2.88.0, adding consolidated `supabase telemetry` reference page - Add Telemetry category to `common-cli-sections.json` for CLI reference nav - Add telemetry section to CLI getting-started guide with opt-out commands and env vars ## Testing Tested on Vercel preview: - [x] `/docs/guides/self-hosting#telemetry` renders with CLI telemetry note and link to full docs - [x] `/docs/reference/cli/supabase-telemetry` renders single consolidated reference page - [x] `/docs/guides/local-development/cli/getting-started#telemetry` shows opt-out section ## Linear - fixes GROWTH-754 |
||
|
|
bb66ba884c |
docs(billing-faq): add paused/deleted project usage note (#44570)
## Summary - Add an Admonition note to the Fair Use Policy section clarifying that pausing or deleting a project does not remove accumulated usage from the current billing cycle - Addresses a common source of confusion identified in #team-support thread (2026-04-02) ## Test plan - [ ] Preview the MDX locally to confirm Admonition renders correctly <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated billing FAQ to clarify that pausing or deleting a project stops new usage from accruing but does not remove usage already incurred in the current billing cycle; quota-based usage remains counted until the billing period resets. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4c10f65de7 |
feat(tracking): add compute_badge_upgrade_clicked event (#44560)
## Summary The `compute_badge_upgrade_clicked` event had zero fires since Apr 1 because the PostHog tracking call was never wired up on the "Upgrade compute" button in the compute badge hover card. This adds the missing `useTrack` call using the existing event definition in `telemetry-constants.ts`. ## Changes - Add `useTrack` hook and `onClick` handler to the upgrade button in `ComputeBadgeWrapper.tsx` - Fires `compute_badge_upgrade_clicked` with `computeSize`, `planId`, and `upgradeType` properties - Preserves existing `asChild` + `<Link>` navigation pattern ## Testing Tested on Vercel preview: - [x] Hover compute badge on a project with non-max compute, click "Upgrade compute", verify `compute_badge_upgrade_clicked` event appears in PostHog live events with correct properties - [x] Verify navigation to compute settings page still works (client-side, no full reload) - [x] Test with paid-plan nano project: `upgradeType` should be `free_micro_upgrade` - [x] Test with paid-plan non-nano project: `upgradeType` should be `compute_upgrade` ## Linear - fixes GROWTH-751 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Added analytics tracking for compute upgrade button clicks to record upgrade type (free micro vs. paid), selected compute size (with fallback), and plan identifier. This ensures upgrade interactions are captured for product insights without changing visible UI behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8364ffb5e0 |
chore(studio): remove privacy policy update notification banner (#44380)
## Summary Remove the privacy policy update notification banner that was added for the March 2026 policy update. The effective date has passed and users have had sufficient notice. ## Changes - Remove `PrivacyUpdateBanner` component from `AnalyticsSettings.tsx` and its unused imports - Remove banner usage from the organizations list page and org projects page - Remove `PRIVACY_NOTICE_ACKNOWLEDGED` localStorage key ## Testing No setup needed - this is a removal of UI elements. Tested on Vercel preview: - [x] Organizations page loads without the banner - [x] Org projects page loads without the banner - [ ] No console errors on either page - [x] Analytics settings page still renders correctly ## Linear - fixes GROWTH-692 |
||
|
|
edacf2413d |
chore(studio): ship connect section, remove getting started and experiment plumbing (#44329)
## Summary The `connectSection` A/B experiment concluded as a true null (no effect on activation or any downstream metric after 13 days at 50/50, ~153K mature orgs). Saxon decided to ship the Connect section as the permanent experience. This PR removes the Getting Started control variant, the old Connect modal, all experiment flag gating, and related telemetry types. ## Changes - Delete `GettingStarted/` directory (5 files: section component, types, utils, progress hook) - Delete old `Connect.tsx` dialog modal (replaced by ConnectSheet) - Remove `connectSection` PostHog flag reads from `Home.tsx` and `LayoutHeader.tsx` - Remove `getSectionVisibility()` experiment logic and `ConnectSectionVariant` type - Remove `getting-started` from `DEFAULT_SECTION_ORDER` - Always render `<ConnectSheet />` in header (no more conditional with old `<Connect />` modal) - Remove `variant` prop from `ConnectSection` component - Remove 4 getting-started telemetry event interfaces from `telemetry-constants.ts` - Update `mergeSectionOrder` tests to reflect new section order ## Testing Tested on Vercel preview: - [x] Project homepage shows Connect section for new projects (< 10 days old) - [x] Connect section hidden for mature projects (> 10 days old) - [x] Header Connect button opens ConnectSheet (not old modal) - [x] Connect tiles open ConnectSheet with correct tab - [x] Section drag-and-drop still works without getting-started in the order - [x] Existing users with `getting-started` in localStorage order don't break (mergeSectionOrder strips it) ## Linear - fixes GROWTH-730 --------- Co-authored-by: Alaister Young <alaister@users.noreply.github.com> |
||
|
|
e01fa312fb | feat(studio): add connection_string_copied tracking to ConnectSheet (#44327) | ||
|
|
e09a663292 |
fix(billing): clarify service restriction reset delay after billing cycle (#44122)
## Summary Customers were upgrading to Pro to clear service restrictions, unaware that restrictions aren't lifted immediately when the billing cycle resets — there's a delay. This adds a clarifying note to both the docs FAQ and the Studio restricted alert so users know what to expect. Note: upgrading your plan or disabling spend cap *does* lift restrictions immediately (unchanged). The delay only applies to the billing-cycle-reset path for usage-limit violations. ## Changes - `billing-faq.mdx`: Removes the implication that restrictions clear the moment the billing period resets; adds "Note that there may be a short delay after your billing period resets before restrictions are fully lifted" - `Restriction.tsx`: Updates the restricted alert to make clear that upgrading lifts restrictions immediately, while the billing-reset path may have a delay ## Testing To test the Studio banner, find or create an org with `restriction_status === 'restricted'`, or temporarily hardcode `shownAlert = 'restricted'` in `Restriction.tsx` to preview: - [x] Restricted alert reads clearly and distinguishes immediate (upgrade) vs delayed (billing reset) paths - [x] Docs FAQ answer under "How can I remove restrictions" reads correctly with the new caveat ## Linear - fixes GROWTH-704 |
||
|
|
882dff3bbb |
chore: add github copilot code review instructions (#43901)
## Summary Adds `.github/copilot-instructions.md` to configure GitHub Copilot Code Review with telemetry and testing guidelines. Replaces CodeRabbit as our automated PR reviewer. Copilot will now advisory-comment when PRs touch growth-oriented components (onboarding, upgrade CTAs, A/B experiments) without telemetry tracking, or when Studio changes lack appropriate test coverage. ## Changes - Add `.github/copilot-instructions.md` with: - Telemetry review rules: event naming (`[object]_[verb]`), property conventions, `useTrack` enforcement, missing tracking suggestions for growth surfaces - Testing review rules: logic extraction to `.utils.ts`, test type decision tree, file naming conventions - Repo context and references to full Claude skill docs ## Testing - [x] Verified `.github/copilot-instructions.md` is the correct path for Copilot Code Review - [x] Rules are condensed from existing Claude skills (`telemetry-standards`, `studio-testing`) — verified alignment ## Linear - fixes GROWTH-698 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
4a4fe253ce | feat(www,studio): privacy policy amendments — advertising, cookie disclosure, banner (#43681) | ||
|
|
fe5b22d314 |
fix(studio): fall back to legacy connect dialog when connectSection flag is unresolved (#43733)
## Summary When the `connectSection` PostHog flag is unresolved (`undefined`) — e.g. during loading or if PostHog is unreachable — the header Connect button renders nothing (`null`), making it a no-op. This changes the fallback to render the legacy `<Connect />` dialog instead, so the button always works. ## Changes - Remove `isFlagResolved` gate in `LayoutHeader` — render `<Connect />` (legacy) whenever `connectSection !== 'connect'`, including when `undefined` ## Testing Tested on Vercel preview: - [x] Flag returns `'connect'` — ConnectSheet opens - [x] Flag returns `'getting-started'` or `false` — legacy Connect dialog opens - [x] Flag is `undefined` (unresolved) — legacy Connect dialog opens instead of nothing |
||
|
|
578a73f966 |
feat(studio): connectSection experiment — replace Getting Started with Connect section (#43629)
## Summary Re-ports PR #43119 against the current `ProjectHome` codebase (the original PR targeted `HomeNew/` which was removed during the `homeNew` graduation in #43437). Also unifies the `connectSheet` and `connectSection` feature flags into a single `connectSection` flag — the `connectSheet` flag is removed entirely, so both the ConnectSheet (header) and ConnectSection (homepage row) are controlled by one experiment. - Adds `connectSection` PostHog experiment flag that controls two things: 1. Swaps the Getting Started section for a Connect section on the project homepage for new projects (< 10 days old) 2. Swaps the legacy Connect dialog for the new ConnectSheet panel in the header - **Control** (`getting-started`): existing Getting Started section + legacy Connect dialog in header - **Treatment** (`connect`): new 4-tile Connect section + ConnectSheet in header - `undefined` (loading): neither section renders, avoiding flash - Tiles filtered by the same `useIsFeatureEnabled` flags as ConnectSheet (`show_app_frameworks`, `show_mobile_frameworks`, `show_orms`) - Connect tile clicks tracked via `home_connect_action_clicked`; section render tracked via `home_connect_section_exposed`; sheet opens tracked via `connect_sheet_opened` with source attribution (`header_button` or `connect_section`) ## Changes - `packages/common/telemetry-constants.ts` — new `home_connect_section_exposed`, `home_connect_action_clicked`, and `connect_sheet_opened` event types - `ConnectSheet/ConnectSheet.tsx` — read `connectTab` query param and sync to active mode on open; `handleModeChange` keeps param in sync on tab switch; fire `connect_sheet_opened` event with source attribution on open - `ConnectButton/ConnectButton.tsx` — set `connectSource=header_button` query param on click - `ProjectHome/ConnectSection.tsx` — new component (4-tile connect card); set `connectSource=connect_section` on tile click - `ProjectHome/Home.tsx` — experiment flag wiring - `LayoutHeader/LayoutHeader.tsx` — read `connectSection` flag instead of `connectSheet` to toggle ConnectSheet vs legacy Connect dialog ## Test plan To test on the Vercel preview, set `connectSection=connect` to 100% in PostHog (or override via cookie `ph_override_connectSection=connect`). No separate `connectSheet` flag is needed — `connectSection` controls both features. - [x] Treatment (`connect` variant) — "Get connected" section renders on new project, Getting Started hidden - [x] Control (`getting-started`) — Getting Started renders, Connect section hidden - [x] Mature project (> 10 days) — neither section regardless of flag - [x] Clicking each tile opens ConnectSheet on the correct tab (Framework / Direct / ORM / MCP) - [x] Switching tabs inside sheet updates `connectTab` URL param - [x] Closing sheet clears `connectTab` param - [x] Direct URL deep-link (`?showConnect=true&connectTab=orm`) opens sheet on correct tab - [x] Dark mode — background gradient renders correctly - [x] Light mode — background switches to light gradient - [x] Responsive layout — 4 cols (xl), 2×2 (md), stacked (mobile) - [x] Telemetry — `home_connect_section_exposed` fires once on load; `home_connect_action_clicked` fires with correct `mode` - [x] Treatment — header Connect button opens ConnectSheet (not legacy Connect dialog) - [x] Control — header Connect button opens legacy Connect dialog (not ConnectSheet) - [x] Telemetry — clicking a ConnectSection tile fires `connect_sheet_opened` with `source: 'connect_section'` - [x] Telemetry — clicking header Connect button fires `connect_sheet_opened` with `source: 'header_button'` --------- Co-authored-by: Ali Waseem <waseema393@gmail.com> |
||
|
|
01c178e159 |
chore(studio): graduate homeNew experiment (#43437)
## Summary
The `homeNew` PostHog experiment has concluded. This PR graduates it by
making the new homepage (`ProjectHome`, formerly `HomeV2`) the permanent
default for all users, and removes all dead code from the old
experiment.
## Changes
- Remove `homeNew` PostHog feature flag checks and `home_new` experiment
exposure tracking from 3 files
- Rename `HomeNew/` → `ProjectHome/` directory and `HomeV2` →
`ProjectHome` export
- Delete old `Home/Home.tsx` component (shared components like
`ProjectList/` are kept — still used by org pages)
- Delete `pages/project/[ref]/building.tsx` and add a server-side
redirect from `/project/:ref/building` → `/project/:ref` to prevent 404s
during rollout (old cached JS bundles may still route to `/building`)
- Simplify `ContentWrapper` building-state logic in `ProjectLayout` —
always redirect building projects to home, always suppress building
interstitial on home page
- Always route to `/project/{ref}` after project creation (remove
`/building` path)
- Update all Observability imports from `HomeNew` → `ProjectHome`
## Self-hosted behavior change
Self-hosted Studio previously showed the old `Home` component (client
libraries + example projects) since PostHog flags don't load. This PR
changes self-hosted to show `ProjectHome` (TopSection with service
status + instance diagram, advisor, custom reports). All sections query
backend APIs that exist on self-hosted. E2E tests pass against the
self-hosted build.
## Testing
- [x] `pnpm turbo run build --filter=studio` passes
- [x] No remaining references to `homeNew`, `home_new`, or `HomeNew` in
codebase
- [x] No broken imports to deleted files
- [x] Self-hosted E2E tests pass (145 passed, 1 flaky, 4 skipped)
- [x] `/building` redirect added to both platform and self-hosted config
blocks
**Quick test:**
1. Navigate to any project homepage — should render the ProjectHome
component
2. Create a new project — should redirect to `/project/{ref}` (not
`/building`)
3. Visit a project in `COMING_UP` state on a non-home route — should
redirect to home
4. Visit `/project/{ref}/building` directly — should 302 redirect to
`/project/{ref}`
## Linear
- fixes GROWTH-671
|
||
|
|
4a9455cb2f |
chore(studio): update downgrade survey to use 'what made you' framing (#43435)
## Summary - Updates exit survey question wording from "why" framing to "what made you" framing across both downgrade and project deletion flows - Based on [Jason Cohen's research](https://www.lennysnewsletter.com/p/why-your-product-stopped-growing) showing this reframing roughly doubles response rates and improves response quality by prompting users to recall a specific trigger event ### Changes | Location | Before | After | |---|---|---| | ExitSurveyModal (downgrade) | "Share with us why you're downgrading your plan." | "What made you decide to downgrade your plan?" | | DeleteProjectModal (delete) | "Help us improve by sharing why you're deleting your project." | "What made you decide to delete your project?" | ### No downstream impact - `CANCELLATION_REASONS` chip values unchanged - API payload fields (`reasons`, `additionalFeedback`, `exitAction`) unaffected - No PostHog event names or properties tied to question wording Closes GROWTH-657 ## Test plan - [ ] Trigger downgrade flow (paid plan → Free) and verify new wording appears - [ ] Trigger project deletion on a paid plan and verify new wording appears - [ ] Confirm survey submission still works end-to-end |
||
|
|
5880966b15 |
chore: add telemetry standards skill for CodeRabbit (#43436)
## Summary - Adds a combined telemetry standards skill (`.claude/skills/telemetry-standards/SKILL.md`) that covers PostHog event naming conventions, property standards, review rules, and implementation guide - Intended to be imported as CodeRabbit learnings after merge so CodeRabbit can flag missing/incorrect tracking in PRs - Consolidates standards from existing `review-telemetry` and `implement-tracking` Claude commands into a single source of truth ## Post-merge steps ### 1. Import as CodeRabbit learnings (one-time) Comment on any PR in the repo: ``` @coderabbitai add a learning using .claude/skills/telemetry-standards/SKILL.md ``` This teaches CodeRabbit the telemetry standards. It will then: - Flag naming/property violations when `telemetry-constants.ts` is changed - Suggest adding `useTrack()` tracking when PRs add user-facing interactions without it - Propose event names following `[object]_[verb]` convention ### 2. Add path instructions in CodeRabbit web UI (optional, recommended) Go to CodeRabbit settings > Review > Path Instructions and add: - **Path:** `packages/common/telemetry-constants.ts` - **Instructions:** "Strictly enforce event naming: [object]_[verb] in snake_case. Only approved verbs: opened, clicked, submitted, created, removed, updated, retrieved, intended, evaluated, added. Properties must be camelCase and self-explanatory. Flag any usage of useSendEventMutation." ### 3. Remove old Claude commands (after verifying skill works) Delete `.claude/commands/review-telemetry.md` and `.claude/commands/implement-tracking.md` — this skill replaces both. Closes GROWTH-661 |
||
|
|
3411387312 | feat(telemetry): track log explorer query execution (#42714) | ||
|
|
ef84dddc22 |
feat(telemetry): track home page and getting started exposure (#42247)
## Summary Adds exposure tracking for the HomeV2 experiment and the Getting Started section. These events help measure user engagement with the new home page experience and track how many users see the Getting Started onboarding flow. Resolves [GROWTH-602](https://linear.app/supabase/issue/GROWTH-602/track-home-page-and-getting-started-exposure-events) ## Changes - Add `home_new_experiment_exposed` event that fires when users see the HomeV2 experiment (captures which variant they're assigned) - Add `home_getting_started_section_exposed` event that fires when the Getting Started section is displayed (captures current workflow state) - Define corresponding TypeScript interfaces in telemetry-constants.ts - Both events use `useRef` to ensure they only fire once per component mount ## Testing Tested locally and on staging that events fire to posthog. **Quick test:** 1. Navigate to a project home page with the `homeNew` flag enabled 2. Verify `home_new_experiment_exposed` event fires in PostHog with the correct variant 3. For projects < 10 days old, verify `home_getting_started_section_exposed` fires when the section is visible 4. Confirm events don't fire for dismissed sections or mature projects <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added exposure tracking for home experiment variants and the Getting Started section to surface which experience users see. * **Chores** * Standardized and consolidated analytics calls to improve reliability of interaction tracking. * Enhanced event payloads to capture workflow choices, step interactions, and dismissals for better product insights. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b6d8f2ae2d |
chore: remove tableQuickstart and realtimeButtonVariant experiments (#42388)
## Summary Removes two concluded A/B experiments that didn't produce positive results: - **tableQuickstart**: Tested AI-powered table generation, template selection, and assistant integration for new table creation - **realtimeButtonVariant**: Tested hiding the realtime button or replacing it with a triggers button ## Changes - Delete `TableQuickstart/` folder with AI widget, templates widget, and generation hooks - Delete `useRealtimeExperiment` hook and remove variant-conditional logic - Delete `/api/ai/table-quickstart/generate-schemas` endpoint - Remove telemetry event definitions for both experiments - Remove local storage exposure tracking key - Remove API endpoint from proxy whitelist - Clean up eslint baseline references ## Testing - [x] Tested locally - Table Editor renders correctly without experiment widgets - [x] TypeScript compiles without errors - [x] No remaining references to removed experiment code **Quick test:** 1. Navigate to Table Editor → New Tab shows only "Create a table" card (no AI/Templates/Assistant variants) 2. Open table create panel → Realtime checkbox shows unconditionally when realtime is enabled ## Linear fixes GROWTH-609 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Removed Features** * AI-powered table generation and quickstart assistance (templates, AI widget, generation hook, templates data, and related utilities) * Quickstart templates widget and predefined table templates * Database triggers management interface * Realtime experiment gating and related experiment variants * **API & Storage** * Hosted AI quickstart API endpoint removed * Local storage key for quickstart exposure tracking removed * **Telemetry** * Quickstart- and realtime-experiment telemetry events removed * **UI Changes** * Simplified realtime toggle control in the table editor <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
30093962d4 |
feat(telemetry): dedupe experiment exposure events by PostHog session ID (#42386)
## Summary
Adds session-based deduplication for experiment exposure events.
Previously, exposure events used `useRef` which reset on page refresh,
causing duplicate events. Now events dedupe by PostHog session ID using
`sessionStorage`, firing once per session even across page refreshes.
## Changes
- Add `captureExperimentExposure()` method to `PostHogClient` with
session-based dedupe
- Add `getSessionId()` method to retrieve PostHog session ID
- Create `useTrackExperimentExposure` hook for clean usage
- Migrate existing experiments to new hook:
- RLS option experiment (`/new/[slug]`)
- Realtime button experiment
- Table create generate policies experiment
## How it works
1. Event triggered → if PostHog not ready, queue to `pendingExposures[]`
2. PostHog `loaded` → flush queue through `fireExposureIfNew()`
3. `fireExposureIfNew()` checks
`sessionStorage['ph_exposed:{experimentId}']`
4. If value matches current session ID → skip (already fired)
5. If differs or missing → fire event, store session ID
## Testing
- [x] Tested locally and on preview - verified on project creation page
- [x] PostHog events sent successfully (200 responses)
- [x] Session deduplication working (no duplicate events on page refresh
or org switch)
- [x] Lint passes with 0 errors
**Quick test:**
1. Go to `/new/<org-slug>` project creation page
2. Check `sessionStorage` for `ph_exposed:project_creation_rls_option`
key
3. Refresh page - event should not fire again (same session ID)
## Linear
Resolves GROWTH-608
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* More reliable experiment exposure reporting with session-aware
deduplication, queued delivery, and consent respect to ensure events are
recorded when available.
* **Bug Fixes**
* Prevents duplicate exposure events within a session and avoids firing
exposures without a valid session.
* **Refactor**
* Consolidated disparate exposure-tracking logic into a unified
hook-based approach, simplifying tracking across the app.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
f27a2d3028 |
feat(telemetry): track security settings on project creation (#42150)
* feat(telemetry): add security settings to project creation event Add dataApiEnabled, useApiSchema, and useOrioleDb properties to project_creation_simple_version_submitted event to track user security and configuration choices during project creation. * feat(studio): track security settings on project creation Include dataApiEnabled, useApiSchema, and useOrioleDb in the project_creation_simple_version_submitted telemetry event to capture user's security and configuration choices. * feat(telemetry): add security settings to confirm modal event Include dataApiEnabled, useApiSchema, and useOrioleDb in the project_creation_simple_version_confirm_modal_opened event for consistency with the submitted event. * refactor(telemetry): remove extra properties from confirm modal event Remove dataApiEnabled, useApiSchema, and useOrioleDb from project_creation_simple_version_confirm_modal_opened as these are not needed for this event. |
||
|
|
faa2588392 |
chore(studio): remove homeNew feature flag helper (#41984)
PostHog migration from boolean to string variants is complete. The helper was only useful during migration - now it's just `value === 'new-home'` which is trivial to inline. - Delete apps/studio/lib/featureFlags/ folder - Inline flag check at 3 call sites |
||
|
|
0d4fc54fb3 |
docs(studio): clarify free project limit message (#42007)
docs: clarify free project limit across organizations |
||
|
|
90fbf041ce |
feat(studio): add cached egress quota violation label (#42000)
feat(usage): add cached egress quota violation label |
||
|
|
2346baf51c |
chore(telemetry): standardize event naming conventions (#41786)
* docs(telemetry-constants): add naming conventions and review note * refactor(telemetry): rename event interfaces to include Event suffix * refactor(telemetry): rename branch merge success event to completed * refactor(telemetry): rename cron job deleted event to removed * refactor(telemetry): rename events to submitted/completed * docs: add previous event names to telemetry comments |
||
|
|
3336b22451 |
chore(flags): migrate homeNew flag from configcat to posthog (#41101)
Switch homeNew flag to usePHFlag. |
||
|
|
9f79037a8b | feat(telemetry): add tracking for request upgrade modal (#41049) |