Commit Graph
4 Commits
Author SHA1 Message Date
Pamela Chia 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 -->
2026-08-31 16:25:06 +08:00
Pamela Chia 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 -->
2026-08-26 16:41:46 +08:00
Pamela Chia 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 -->
2026-08-24 15:19:56 +08:00
Pamela Chia 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 -->
2026-07-27 17:06:16 +08:00