Commit Graph
20878 Commits
Author SHA1 Message Date
Claude c37af25c4c fix(storage): don't let a purge round delete a file that went live
The drain loop fed each listing straight back into the next delete. Deleting
a version id is unconditional, so a restore or a new upload to the same path
while the purge was running would be picked up by the following round and
destroyed.

Deleting the delete marker promotes nothing in Storage, so mid-drain every
surviving row is still archived. A current version appearing in a later
round therefore means the path was written again, and the purge now stops
instead of claiming it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 10:08:41 +00:00
Claude f307278fc4 fix(storage): drain the whole history when purging an archived file
The version list endpoint caps a page at 1000 rows. Purging an archived file
deleted that one page plus the marker and called it done, so a longer history
kept its older versions — and with the marker gone one of them became current
again, putting the "permanently deleted" file back in the bucket. It now lists
and deletes in rounds until nothing is left, the way the live object purge does.

It also deletes versions by id, the current one included, so a restore between
opening the dialog and confirming would hand it a live file to destroy. It now
re-reads the marker immediately before the first delete and aborts if the path
is no longer archived. That narrows the window rather than closing it; only
Storage can make the check and the delete one operation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 07:38:37 +00:00
Claude 8ec731aaac fix(storage): report a truncated archived listing instead of guessing
Two problems with the page cap. It stopped at 20 pages without telling the
caller the bucket had more, and because the listing is ordered by name the cut
could land mid-path, leaving `toArchivedObjects` to classify a fragment: a lost
delete marker reads as a live object, lost retained versions drop an archived
one. The query now returns `isTruncated` and discards the path it stopped on.

The archived purge also refetched its version list through the client's
one-minute staleTime, so a version uploaded after that list was cached could
survive the purge. It now bypasses the cache, like the live object purge does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 07:38:37 +00:00
Claude 9a9c011a29 test(storage): type the archived objects fixture instead of casting to any
Same treatment as the object versions fixture: the row type comes from
the function under test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 07:38:37 +00:00
Claude 1eff70075f feat(storage): carry an archived version's mime type through the query
The API returns it and the mapper dropped it, so nothing downstream could
tell an image from a PDF. The archived preview needs it to render.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 07:38:37 +00:00
Claude d5a380d988 fix(storage): hide the empty folder placeholder from archived rows
The live listing filters it out of every folder, but the archived overlay
was rendering it as a file. It still rolls its folder up, so an archived
empty folder stays visible rather than exposing the placeholder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 07:38:37 +00:00
Claude a6d96b1c72 refactor(storage): trim comments to one line where they earn their place
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 07:38:35 +00:00
Claude 390c57b8db feat(storage): fetch and mutate archived objects against Storage
An archived object is one whose top row is a delete marker: gone from the
live listing, versions still retained. `list-v2` reports those rows once
`deleteMarkers` and `noncurrentVersions` are included, so the archived
list is a grouping of that listing rather than a dedicated endpoint.

The flat, undelimited listing is deliberate — the inline overlay
synthesizes folders from full paths, so it needs the whole bucket, not one
level at a time. Paging is bounded.

- Restoring deletes the delete marker, which promotes the version beneath
  it back to current; nothing is copied
- Purging deletes every retained version and the marker, since leaving the
  marker would keep the object listed as archived with nothing under it
- Deleting one retained version addresses it by `{ path, versionId }`

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-09 07:38:35 +00:00
Francesco SansalvadoreandClaude Sonnet 5 096ff66779 feat(storage): add archived objects data layer
On a versioned bucket a delete is a soft delete, and the file preview panel
already calls that action Archive and promises the versions stay recoverable.
Nothing in the dashboard lets a user see or restore an archived file yet. This is
the data layer for that, with no UI: query and mutation shapes written to the
studio conventions, endpoints stubbed, returning empty.

- `archived-objects-query.ts` — `ArchivedObject` / `ArchivedObjectVersion` types
  and `archivedObjectsQueryOptions`
- `archived-object-restore-mutation.ts` — bring an archived object back
- `archived-object-purge-mutation.ts` — delete it and every version, permanently
- `archived-object-version-delete-mutation.ts` — remove one retained version
- `archivedOverlay.utils.ts` — synthesizes the explorer rows for one folder from
  the archived list
- `archivedVersions.utils.ts` — an archived object's history as one flat list

Two prototype problems fixed rather than carried over:

The prototype identified the "was current when archived" row by the sentinel
`versionId === objectId`, which was load-bearing across three files and would
break the moment real version ids arrived. `ArchivedObject` now carries a
`currentVersion` record, so merging invents no fields and the distinction is an
explicit `wasCurrentAtArchive` flag.

The prototype's object had both `name` and `originalPath`, inconsistently — the
path normalization existed mostly to strip a duplicated leaf folder that
inconsistency produced. There is now a single `path`, and `getArchivedSegments`
is the only function that interprets it, so a different API shape is a one-place
change.

Also drops `deletedBy` and `expiresAt`, which the prototype never rendered.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 07:38:34 +00:00
Francesco SansalvadoreandClaude Sonnet 5 1b27761bec feat(storage): add version history to the file preview panel (FE-4163) (#49208)
## [5/10] Storage object versioning: version history in the file preview
panel

**Base:** `feat/storage-versioning/004-object-versions-data` #49207 

### This PR

Adds all the versioning ui to the file preview panel.

- polished file preview panel (header, buttons, nits)
- added "Upload file" to the action buttons
- "Delete permanently" now defaults to "Archive" as primary action, and
"Delete permanently" via a dropdown menu as a more destructive action
- added "Versions" toggle section to the file preview panel
- with lifecycle policy "summary"
  - with additional explanation on hover
- versions can be restored or deleted permanently 

### How to test

- in a File storage bucket, open a file preview panel
- when the "Storage Versioning" feature preview flag is true, the
preview panel should show
    - a "versions" section
    - a new "upload file" button next to the "download file" button
- the "delete permanently" button should show "Archive" + a dropdown
with the "Delete permanently" option
- if the feature preview flag is toggled _off_, the preview panel should
looks as before (but with some minor polish)


https://github.com/user-attachments/assets/56f6b9aa-b3a0-45bd-b3e7-5788967d196a

With the feature preview toggled _on_:
- the ui should show the lifecycle policy "summary" that will affect
this file
- hovering the lifecycle policy summary should explain how the policy
will affect/clean up the noncurrent files
- the lifecycle policy summary hover card button should link and open
the "edit bucket modal"
- create a new version of a file by using the "Upload file" (next to
"download file")
  - the file should have a new "Current" version
  - the previous version should become a "noncurrent" version
- the noncurrent version should reflect the lifecycle policy that will
eventually affect it with some additional context on hover via a tooltip
- clicking on a noncurrent version should open a "comparison widget"
that provides a way to restore it and re-promote it to "current" or
delete it permanently
- these actions are also available via dropdown menu from the version
item
- a file should be "archiveable" (please note that showing/managing
archived files will come in the next pr in the stack)
- confirmation modals should prompt to confirm to proceed to restoring
or permanently deleting a noncurrent version


https://github.com/user-attachments/assets/803a6f15-7f32-44ae-9e24-1c201aab178a

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 09:36:41 +02:00
Danny White 0b85e0d9c8 fix(design-system): give source panel breathing room before prose (#51468)
## Problem

After the shadcn source row started rendering, body prose sat flush
under the source stack. The first MDX paragraph has no top margin
(`not-first:mt-6`), and `SourcePanel` had no bottom margin. Pages with
no Radix used to fall through to the separator’s spacing; once shadcn
showed, that gap disappeared.

## Solution

When any source row is present, `SourcePanel` adds `mb-6` (same as the
separator above). When none are present, it returns `null` so we do not
double the separator gap.

| Before | After |
| --- | --- |
| <img width="1650" height="716" alt="CleanShot 2026-10-09 at 14 14
31@2x"
src="https://github.com/user-attachments/assets/f1c6b7bd-ad08-4540-8b20-1951a1c3e49f"
/> | <img width="1646" height="770" alt="CleanShot 2026-10-09 at 14 15
35@2x"
src="https://github.com/user-attachments/assets/bd0f2971-25a2-4468-8164-ceeb60263bf1"
/> |

## Review instructions

1. Open a component with both Radix and shadcn (e.g. Alert), one with
only shadcn (e.g. Skeleton), one with only Radix if available, and one
with neither (e.g. Collapsible).
2. Confirm consistent space between the source rows (or the separator,
when there are none) and the first body paragraph.
2026-10-09 18:19:44 +11:00
Danny WhiteandJoshen Lim d5341f37f9 chore(studio): use standard Badge for deprecated extensions (#51461)
## Problem

The deprecated indicator next to extensions like `pgjwt` on Database →
Extensions was a `ButtonTooltip` styled to look like a badge
(`variant="warning"`, triangle icon, `rounded-full`). That adds another
one-off badge-like control instead of using the shared `Badge`.

## Solution

Replace it with a warning `Badge` plus the same tooltip content (when
the extension will be removed).

| Before | After |
| --- | --- |
| <img width="356" height="114" alt="CleanShot 2026-10-09 at 10 38
08@2x"
src="https://github.com/user-attachments/assets/98a2356e-203e-4946-b399-63a53ea9804f"
/> | <img width="312" height="116" alt="CleanShot 2026-10-09 at 10 43
08@2x"
src="https://github.com/user-attachments/assets/ee9ff91d-79f6-4a50-b508-ebe602497cfe"
/> |

## Review instructions

1. Open Studio → Database → Extensions.
2. Find `pgjwt` (or another deprecated extension).
3. Confirm a warning `Deprecated` badge appears next to the name (no
triangle icon / pill button).
4. Hover (or focus) the badge and confirm the removal tooltip still
shows.

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-10-09 14:23:37 +08:00
Saxon FletcherandDanny White 692b14d47b fix(ui): opaque sticky table column hover (#47668)
## Problem

On tables with a sticky last column (Database Extensions, OAuth Apps,
Custom Auth Providers), hovering a row lets scrolled cell content show
through the sticky column.

Root cause: row hover uses `bg-surface-200`, which maps to `--muted`
(alpha). That works for normal cells (nothing behind them) but sticky
cells sit above horizontally scrolled content, so the tint is
see-through.

## Solution

In `ShadowScrollArea` when `stickyLastColumn` is set:

- Keep an opaque `bg-surface-100` base on sticky cells
- Composite the muted hover tint via `background-image` so hover matches
`TableRow` without becoming transparent

Also:

- Extensions: use `Table containerProps={{ stickyLastColumn: true }}`
instead of nesting a second `ShadowScrollArea` (same pattern as OAuth /
Custom Auth)
- OAuth Apps and Custom Auth Providers: drop redundant per-cell
`bg-surface-100` / `hover:bg-surface-200` so sticky hover comes only
from `ShadowScrollArea`

| Before | After |
| --- | --- |
| <img width="1042" height="98" alt="27097"
src="https://github.com/user-attachments/assets/204a7dc4-d5ac-40b7-87fb-fdb03982eaad"
/> | <img width="1084" height="101" alt="94456"
src="https://github.com/user-attachments/assets/c6db3607-6bbe-4488-a0a5-81f5a0436d66"
/> |

## Review instructions

1. Open Database Extensions. Narrow the viewport or scroll the table
horizontally so the Enabled column is sticky.
2. Hover a row while Links or description content sits under the Enabled
column. The sticky cell should stay opaque and match the row hover tint
(light and dark).
3. Optionally repeat on Auth → OAuth Apps and Auth → Custom Providers
(actions column).

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
2026-10-09 17:12:47 +11:00
Joshen Lim a76b861b50 Remove schema selector in DB extensions enable dialog (#51440)
## Context

We've had a number of users run into issues after installing database
extensions in schemas outside of the `extensions` schema - in particular
for the `public` schema, which issues that normally surface are either:
- Inability to enable RLS because the extensions are owned by
`supabase_admin` when created
  - Security advisor hence constantly flags this as an issue
- Inability to move the extension for the same reason
- There is a solution for but it requires a lengthy resolution as
outlined here in [our
docs](https://supabase.com/docs/guides/database/extensions/postgis#moving-postgis-to-a-different-schema)

While the option to select a schema to enable the database extension was
for convenience, this is typically more for power users which seems to
be more of a footgun for most other users.

Hence we're opting to remove the schema selection option in the enable
extension modal to also streamline the UX for enabling an extension by
removing that additional cognitive load.

## Changes involved

### Before:
<img width="430" height="248" alt="image"
src="https://github.com/user-attachments/assets/e99ce86c-b56c-4149-86ca-2457da65b179"
/>

### After:
<img width="438" height="328" alt="image"
src="https://github.com/user-attachments/assets/6be599d5-185b-4efe-9718-b50ac0ed4948"
/>

Clicking on the foot note expands a note as such with an alternative for
power users:
<img width="425" height="434" alt="image"
src="https://github.com/user-attachments/assets/7cbf0b59-0ddc-4933-8c6b-4e30a32115b5"
/>

Extensions will be installed in the `extensions` by default unless
otherwise specified by the extension itself within
`default_version_schema`
2026-10-09 13:15:48 +08:00
Danny White cd00775dd7 feat(studio): add the pipeline creation wizard preview foundation (#51425)
## Problem

The redesigned pipeline creation flow (mega branch being worked on in
#49243) needs an incremental rollout while the existing creation sheet
remains the default.

## Solution

- Register **Pipeline creation wizard** in Feature Previews, with
visibility controlled by the `pipelineCreationWizard` ConfigCat flag.
- Require the flag and an explicit user opt-in (via Feature previews
dialog) before enabling the preview.

This adds the preview registration, copy, storage key, and gate. It does
not change creation routing or ship the wizard. The complete gated flow
can be tested on #49243.

The ConfigCat flag is configured as follows:

- **Local/staging:** true for everyone.
- **Production:** false by default, true for the **Supabase Team Email**
segment.

The flag makes the preview visible; it does not opt anyone in. Users
enable it once in Feature Previews. No dev toolbar override is needed in
local/staging.

## Review instructions

1. In local or staging hosted Studio, open **Feature previews**. Find
**Pipeline creation wizard**. With no saved preference, it should be off
by default.
2. Toggle it on and reload. The opt-in should persist. Toggle it off and
reload to confirm opt-out persists. **Add pipeline** still opens the
sheet in this foundation PR.
2026-10-09 14:55:38 +11:00
Danny White 600c48840f chore(studio): remove outdated Pipelines feedback button (#51424)
## Problem

The Pipelines header links to an older feedback discussion. The
dashboard's existing Feedback control already captures the current
route.

## Solution

Remove the “Leave feedback” button and its unused URL constant.

| Before | After |
| --- | --- |
| <img width="1932" height="340" alt="32431"
src="https://github.com/user-attachments/assets/ef5f919c-503b-4fcc-9137-90ddeb6c07dd"
/> | <img width="1930" height="350" alt="CleanShot 2026-10-08 at 15 47
38@2x"
src="https://github.com/user-attachments/assets/d44df2a0-8681-4594-9679-148d639d08fd"
/> |

## Review instructions

1. Open a project's Database > Pipelines page at
`/project/<ref>/database/pipelines`.
2. Confirm the header shows the documentation button without “Leave
feedback”.
3. Confirm the diagram and the usage and enable/disable dropdown actions
are unchanged.
2026-10-09 14:24:08 +11:00
Ping-Min Lin 1ee88e0fee feat(studio): show Realtime on High Availability projects in staging (#51372)
## What kind of change does this PR introduce?

Feature, limited to staging and local.

## What is the current behavior?

On High Availability projects, Studio always disables Realtime: the
Realtime pages and the Publications pages show an "unavailable" message,
the table editor's "Enable Realtime" checkbox is disabled, and the
project home shows Realtime as disabled.

## What is the new behavior?

A new `useIsRealtimeUnavailable()` hook treats Realtime as unavailable
only on High Availability projects outside staging and local (via
`IS_STAGING_OR_LOCAL`). Those four places use it instead of checking
`high_availability` directly.

- **Production:** unchanged.
- **Staging and local:** Realtime behaves as on standard projects.

## Additional context

Whether a High Availability project is actually registered as a Realtime
tenant is gated separately on the platform side. Until then, the pages
render but Realtime connections for that project fail.

Tests: a new `resolveRealtimeUnavailable` test, and the
`TableRealtimeToggle` and `resolveRealtimeServiceStatus` tests updated
for the renamed parameter.
2026-10-08 14:05:37 -07:00
Tina HaandClaude Sonnet 5.5 94f0a0ab1e fix(docs): remove outdated migrations endpoint access note from supabase-for-platforms (#51399)
## 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?

Docs update

## What is the current behavior?

The "Make database changes" section of the Supabase for Platforms guide
says only select customers can access `POST
/v1/projects/{ref}/database/migrations` and points to a partnerships
form. The endpoint is no longer gated:

- https://github.com/supabase/platform/pull/31058 (merged 2026-03-27)
made the migrations endpoint GA for everyone.
- https://github.com/supabase/platform/pull/36265 (merged 2026-07-28)
removed the outdated beta/partner-only descriptions from the API
reference.

This guide page was not updated alongside them.

## What is the new behavior?

Removes the outdated access admonition. The endpoint description and
example are unchanged.

## Additional context

Confirmed with the Control Plane team that the endpoint is GA and this
page is outdated. Related support ticket: SU-491909.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 16:18:23 -04:00
Ali Waseem 4205c4103d fix(studio): make detail panel close buttons clickable (#51455)
The close button on the Auth users detail panel (and the queue message
panel) is absolutely positioned, but `TabsList` is `position: relative`
and comes later in tree order — so it painted on top of the button and
swallowed every click. The button looked fine and did nothing.

Stacks the button above the tab list, adds an accessible name to the
users panel button, and covers it with an E2E test (Playwright does real
hit-testing, so it fails without the fix).

Fixes FE-4519
2026-10-08 13:43:02 -06:00
Mike Donnalley f918b8ebd6 Add Mike Donnalley to humans.txt (#51451)
## Problem

humans.txt doesn't have my name listed in it.

## Solution

Adds my name to the list of humans.


## Checklist

Check all before review:

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
2026-10-08 10:44:04 -06:00
Jordi Enric dbfa35ddb3 fix(studio): remove unsupported log drain form variants (#51449)
## Problem

The log drain form still includes Postgres, ClickHouse, and BigQuery
placeholder variants even though the dashboard does not offer these
destinations. Production Platform API create/update types now exclude
them, causing Studio type errors when the API declarations are
regenerated.

## Fix

Remove the unused variants from the form and submission schemas. Accept
the broader response type for incoming defaults, resolve defaults
through the selectable destination list, and use the form schema type
for the selector.

## How to test

- Run `pnpm --filter studio typecheck` with committed API types and with
types generated from production. Both should pass.
- Run Prettier and ESLint on `LogDrainDestinationSheetForm.tsx`.
- Open project or organization audit log drain settings and add a
supported destination. Available destinations remain unchanged.

Validation: Studio typecheck passes with both committed API types and
freshly generated production API types. Prettier passes; ESLint reports
only existing warnings. Generated declarations are not included in this
PR.
2026-10-08 15:25:44 +00:00
claude[bot]andClaude 514938f2a6 Revert "chore(www): update Subprocessor List to October 8, 2026 (#51445)" (#51448)
<!-- claude-slack-attribution -->
_Requested by **Ali Waseem, Nicole Kramer** · [Slack
thread](https://supabase.slack.com/archives/C0161K73J1J/p1791467710430139)_

## Problem

Before: the Subprocessor List page
(`/legal/customer-resources/subprocessor-list`) links to the "Updated
October 8, 2026" PDF, added in #51445.

## Solution

After: the page links to the "Updated June 1, 2026" PDF again, until a
page listing all historical lists is built.

How: reverts #51445. Removes `October-8-2026.pdf` from
`apps/www/public/legal/subprocessor-list/` and restores the
`CURRENT_PDF` constant in
`apps/www/pages/legal/customer-resources/subprocessor-list.tsx` to
`June-1-2026.pdf` / `June 1, 2026`. `June-1-2026.pdf` was never removed,
so it is still in place. This is reverted pending a different page
structure.

## Review instructions

1. Open the preview of `/legal/customer-resources/subprocessor-list`.
2. Confirm the button reads "Subprocessor List - Updated June 1, 2026"
and downloads the June 1, 2026 PDF.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill (not applicable)

🤖 Generated with [Claude Code](https://claude.ai/code)

https://claude.ai/code/session_01HVnxEAvXusb7JCGthHfMW7


---
_Generated by [Claude
Code](https://claude.ai/code/session_01HVnxEAvXusb7JCGthHfMW7)_

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-08 15:03:44 +00:00
claude[bot]andClaude a259302f36 chore(www): update Subprocessor List to October 8, 2026 (#51445)
<!-- claude-slack-attribution -->
_Requested by **Sofia Calado** · [Slack
thread](https://supabase.slack.com/archives/C0161K73J1J/p1791467710430139)_

## Problem

Before: the Subprocessor List page
(`/legal/customer-resources/subprocessor-list`) links to the "Updated
June 1, 2026" PDF.

## Solution

After: the page links to the new "Updated October 8, 2026" PDF.

How: added `October-8-2026.pdf` to
`apps/www/public/legal/subprocessor-list/` and updated the single
`CURRENT_PDF` constant (`file` and `displayDate`) in
`apps/www/pages/legal/customer-resources/subprocessor-list.tsx`.

Old PDF: `June-1-2026.pdf` is kept in place so any existing links to it
keep working. The directory had no history of removing old files, so I
left it alone; it can be deleted in a follow-up if preferred.

## Review instructions

1. Open the preview of `/legal/customer-resources/subprocessor-list`.
2. Confirm the button reads "Subprocessor List - Updated October 8,
2026" and downloads the new PDF (header "As of October 8, 2026").

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill (not applicable, no docs
change)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01HVnxEAvXusb7JCGthHfMW7


---
_Generated by [Claude
Code](https://claude.ai/code/session_01HVnxEAvXusb7JCGthHfMW7)_

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-08 08:41:11 -06:00
690ef5e7f7 feat(studio): scoped oauth apps, admins can see an app grants (#50979)
## Problem

Admins need to see the grants associated to an approved OAuth
application that uses scoped tokens.

## Solution

- Add a menu to the org settings oauth approved apps rows to see the
grant list
- Update the react query hook to use infinite query for the grant list

<img width="1149" height="356" alt="image"
src="https://github.com/user-attachments/assets/2e6cfc76-5584-4447-aa19-a0bf103e2218"
/>

<img width="429" height="395" alt="image"
src="https://github.com/user-attachments/assets/dd811d33-2c0a-46df-b9e9-3cbf8ad2fa7f"
/>

<img width="434" height="267" alt="image"
src="https://github.com/user-attachments/assets/108905ba-4dcb-4dca-abe2-1a4e1fc3dbd2"
/>

## Review instructions

In Org Settings/OAuth apps, you should see a menu button for each app
that contains a _View grants_ item. Clicking this item should open a
dialog with the grants

---------

Co-authored-by: kemal <hello@kemal.earth>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-08 15:48:51 +02:00
Kody Jackson ff98ac6452 chore(www) - fix redirects with 404 target destinations (#51413)
## Problem

A subset of redirects were sending folks over to 404's via incorrect /
outdated destinations.

## Solution

Updates redirect targets to be accurate.

## Preview links

TBD (though only the ones attached to the www app itself will be
testable here).
2026-10-08 08:04:45 -05:00
Joshen Lim 18080d88f7 Joshenlim/fe 4512 make GitHub connection setup obvious on empty branching page (#51393)
## Context

Adds an empty state for the branch management page, if no preview
branches have yet to be created (overview wont be shown) - mainly visual
changes here. The main intention here is to surface the GH connection
setup a bit more (otherwise the only CTA for that is in the side nav
which is easily missed + its not clear up front what the benefit of the
GH connection is in the context of branching)

This is how it looked like before for reference:
<img width="1346" height="956" alt="image"
src="https://github.com/user-attachments/assets/776ce3f4-e12b-42e4-8e90-0d5ca15dc58f"
/>

And this is what I'm thinking for the empty state:
<img width="1037" height="431" alt="image"
src="https://github.com/user-attachments/assets/d3a8871e-74d2-499a-b317-f739ed925668"
/>

GH connection will flip its badge and hide the CTA if there already is a
GH connection
<img width="1038" height="443" alt="image"
src="https://github.com/user-attachments/assets/4bf2d34d-6c74-4206-b83c-38ba84b99fb3"
/>
2026-10-08 19:54:41 +08:00
Bobbie Soedirgo 536fbd5470 fix: make auto rls SQL Multigres-compatible (#51428)
Part of resolving
[INC-868](https://supabase.slack.com/archives/C0C8EBVUX2L/p1791443825740829?thread_ts=1791411302.921479&cid=C0C8EBVUX2L)

Auto RLS SQL isn't Multigres-compatible since it uses `EXECUTE` with a
freeform `%s` parameter

Tested locally
2026-10-08 11:59:35 +02:00
12ab771e86 feat(studio): authorized apps table in org settings (#50529)
<img width="1150" height="669" alt="image"
src="https://github.com/user-attachments/assets/0b324577-bed5-4272-a7e0-f4444a0c1230"
/>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
2026-10-08 11:25:07 +02:00
Julian Domke af3e397e7f feat(credit-codes): show that credit codes were reduced by partner deals (#51173) 2026-10-08 07:38:53 +00:00
Danny WhiteandJoshen Lim 78dc739901 fix(studio): show support inline and honour AlertError layouts (#51377)
## Problem

AlertError forces a vertical layout whenever additional actions are
supplied, even when the caller explicitly requests horizontal or
responsive layout. Stripe Sync Engine's uninstall error is one affected
call site, the other was Pipelines as demoed in #51311.

## Solution

Honour an explicit layout. Preserve the existing defaults: vertical with
additional actions, responsive otherwise.

Replace the standalone Contact support action with an InlineLink in the
contact support prose, preserving support form context and breadcrumb
capture. Keep custom actions such as Retry. If instructions are hidden
or custom prose omits contact support, retain a separate inline support
link. With `hideContactSupport`, show no support link and shorten the
default instructions to “Try refreshing your browser.” Custom
descriptions remain unchanged. The local Pipelines configuration error
explicitly hides support. Add the explicit responsive layout at the
Pipelines call site.

| Before | After |
| --- | --- |
| <img width="914" height="426" alt="CleanShot 2026-10-07 at 17 51
20@2x"
src="https://github.com/user-attachments/assets/6cbddfee-97f5-4a7a-bafa-7b5dfd6ab8bf"
/> | <img width="916" height="422" alt="CleanShot 2026-10-07 at 18 26
06@2x"
src="https://github.com/user-attachments/assets/a21952a6-1c1a-437c-af73-c5736f33fd98"
/> |
| <img width="1566" height="384" alt="CleanShot 2026-10-07 at 18 28
07@2x"
src="https://github.com/user-attachments/assets/bdc2a043-421f-4c22-9bd3-37859c6e85c7"
/> | <img width="1568" height="308" alt="CleanShot 2026-10-07 at 18 26
58@2x"
src="https://github.com/user-attachments/assets/da0f2c6a-6254-4196-944b-5665e87b3c3c"
/> |

## Review instructions

1. In a fresh local test project with no existing `stripe` schema, run
this in SQL Editor:

```sql
begin;
create schema stripe;
comment on schema stripe is
  '{"status":"uninstall error","errorMessage":"Local layout test: uninstallation failed"}';
commit;
```

2. Open **Integrations → Stripe Sync Engine → Overview** and reload.
Check **Failed to uninstall Stripe Sync Engine** at wide and narrow
widths, including **Retry uninstallation** and the inline **contact
support** link. Do not click Retry: it invokes the real uninstall
operation.
3. Remove the empty fixture with `drop schema stripe restrict;`.
4. Block the Pipelines source-status request and resize the page: Retry
uses the responsive layout.
5. AlertError callers without an explicit layout should retain their
existing presentation. Confirm default and custom contact support prose
use an inline link, with no standalone support action. Hidden
instructions and custom prose without contact support retain an inline
fallback. With `hideContactSupport`, the default prose is “Try
refreshing your browser.” and no support link appears; custom
descriptions remain unchanged.
6. Automated regression coverage checks that an explicit responsive
layout survives additional actions, and that hiding support removes the
default support wording while preserving custom descriptions.

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-10-08 16:04:32 +11:00
Joshen Lim 26c838a433 Joshenlim/fe 4590 studio sql export can silently swap values for numeric (#51382)
## Context

Resolves https://github.com/supabase/supabase/issues/51330

Odd bug on the Table Editor where "Copy as SQL" CTA would misalign
column names to values if the columns had numerical like names such as
`2024` for example.

Also added an unrelated fix for "Copy as JSON":
- Was adding an `idx` column to the output even if the table didn't have
(was a react data grid internal detail)
- Column name ordering didn't match the table

## To reproduce:

1. Create and populate a table:
```
create table public.yearly_totals (id bigint, "2024" bigint, "2023" bigint);
insert into public.yearly_totals values (7, 99, 42);
```

2. Select the row in the Table Editor, then "Copy as SQL" -> The output
will turn out to be
```INSERT INTO public.yearly_totals (id, "2024", "2023") VALUES (42, 99, 7);```
instead of 
```INSERT INTO public.yearly_totals (id, "2024", "2023") VALUES (7,
99,42);```

## To test
- [ ] Verify that the Copy to SQL output matches the intended as per the
set up above
2026-10-08 11:41:16 +08:00
Brad Deam 33c5f91e6f chore(docs): Add Brad Deam to humans.txt (#51418) 2026-10-08 11:30:04 +10:30
Danny White 6c829b32da fix(studio): enable Pipelines before opening creation (#51311)
## Problem

Add pipeline opens the creation sheet before users enable Pipelines. A
pending or failed source lookup also lets creation open with an unknown
enablement state.

## Solution

Show the existing enablement dialog first when required, then open
creation after successful enablement. Cancellation and failed enablement
keep creation closed; enabling through the page menu does not open
creation.

Disable both Add pipeline buttons and their keyboard shortcut until the
source lookup succeeds. Failed lookups show an error with Retry,
including the local replication configuration message. Analytics Bucket
keeps its existing creation path.

| Before | After |
| --- | --- |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/cf328732-4342-4758-bde2-98b393341d6a"
/> | _No longer in sheet; dialog is shown conditionally before sheet._ |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/f593e271-d29c-4955-8849-0bb64946d4cc"
/> | <img width="1275" height="919" alt="Pipelines Database Shears
Toolshed Supabase"
src="https://github.com/user-attachments/assets/3257d391-b892-44df-85d1-5a2108072312"
/>|
| _“Enable Pipelines”_ | _“Enable”_ |

| After |
| --- |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/b11770a1-4785-49ee-950d-d821892b3245"
/> |
| _Loading_ |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/710f82be-ca6f-4337-a5ce-b65f9d7f6a32"
/> |
| _Lookup failed_ |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/68486ca8-37e8-4b27-89ae-8004f159c38a"
/> |
| _Plan-access loading, throttled_ |

## Review instructions

Use a project with Pipelines access and a working replication API (which
should work on [deploy
preview](https://studio-staging-git-dnywh-fixpipeline-enable-create-supabase.vercel.app/)).
Test the PR preview or locally
([instructions](https://app.notion.com/p/supabase/Danny-s-Local-ETL-Pipelines-Setup-3b25004b775f8058a108f8f67fc813e9?source=copy_link)).
In DevTools Network, enable **Disable cache** before each reload.
Analytics Bucket intentionally bypasses the source-status guard.

1. **Loading:** select **Slow 3G**, reload, and watch the request ending
in `/replication/<ref>/sources`. While it is pending, both **Add
pipeline** buttons must be disabled and **Shift+N** must open nothing.
Both buttons replace the plus with a loading spinner and have no loading
tooltip. Restore **No throttling** afterwards.
2. **Lookup failed:** right-click that source request and choose **Block
request URL**, then reload. After retries finish, expect **Failed to
retrieve pipeline enablement status** with **Retry**, disabled Add
buttons whose tooltip matches the error title, and no sheet/dialog from
**Shift+N**. An unconfigured local replication API instead shows
**Replication unavailable locally**. Unblock the request before clicking
**Retry**.
3. **Lookup succeeded:** on a disposable project with Pipelines
disabled, successful Retry restores Add pipeline. Clicking it opens
**Enable Pipelines**. Cancel stays on the list; **Enable** opens the
sheet after successful enablement. On an already enabled project, Add
pipeline opens the sheet directly. Enabling through the page's three-dot
menu stays on the list. Analytics Bucket opens its sheet without ETL
enablement.
4. **Plan-access loading is separate:** in Chrome 145 or newer, find
`/organizations/<slug>/entitlements` in Network, right-click it and
choose **Throttle request**. In the **Request conditions** drawer,
select Slow 3G for that request only, leaving global throttling off.
Reload on a Pro organisation with Pipelines disabled and open Add
pipeline after the source lookup succeeds. While entitlements remain
pending, expect body shimmers, the accessible “Checking Pipelines
access…” status, a disabled loading **Enable** button, and no upgrade
prompt. Remove the request condition afterwards. If your DevTools lacks
per-request throttling, use the component tests for deterministic
coverage.
2026-10-08 10:42:39 +11:00
shaziya 5056af35e7 chore(www): add on-demand webinar with AWS on securing and scaling vibe-coded apps (#51416)
## 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?

Content: new on-demand webinar event page.

## What is the current behavior?

There is no event page for the AWS AI-Native Dev Stack webinar.

## What is the new behavior?

Adds an on-demand event, "Secure and scale vibe-coded applications with
Supabase and AWS", in the same format as the Datadog on-demand webinar:

- `main_cta` ("Watch the recording") links to the recording hosted by
The Register
- Speakers: Nick Littman (Supabase) and Mrinali Umashankar (AWS)
- Adds Mrinali Umashankar and her avatar to `authors.json` and
`public/images/blog/avatars`
- Listed under the On-demand filter on `/events`

## Additional context

Event date is set to Sept 9, 2026.

Pre-flight checks (per CONTRIBUTING.md): Prettier passes on the changed
files, and `pnpm build --filter=www` completes successfully locally.
2026-10-07 16:12:38 -07:00
claude[bot]andClaude 7776b1f7ee docs(www): add Data Residency and Transfers FAQ legal page and Privacy Resources hub section (#51318)
<!-- ccr-slack-attribution -->
_Requested by **Sofia Calado** · [Slack
thread](https://supabase.slack.com/archives/C0161K73J1J/p1791273150504749?thread_ts=1791273150.504749&cid=C0161K73J1J)_

**Before:** The Data Residency and Transfers FAQ only existed as a PDF.
The Legal Hub (`/legal`) listed the Data Processing Addendum and the
Subprocessor List under "Customer Legal Resources", with no
privacy-specific grouping.

**After:** The FAQ is a native, indexable page at
`/legal/privacy-resources/data-residency-and-transfers-faq`. The Legal
Hub has a new "Privacy Resources" section with the Data Processing
Addendum, the Subprocessor List and the FAQ. The DPA and Subprocessor
List URLs are unchanged.

This publishes the FAQ as a web page and groups it with the other
privacy documents in the Legal Hub.

## Problem

The FAQ needs to be discoverable on supabase.com (including search) and
linked from the Legal Hub next to the DPA and Subprocessor List.

## Solution

How: the page mirrors the DPA shell (`DefaultLayout` > `NextSeo` >
`PageHeader` with `PageBreadcrumb` > `MDXProvider` > `LegalDocVersions`)
with a single `v1` entry. The body is
`data/legal/privacy-resources/data-residency-and-transfers-faq/v1.mdx`,
transcribed verbatim from the source PDF (17 questions in 9 sections),
with one correction confirmed by the requester: the marketplace Note
names "Supabase, Inc.", and the "plan documentation" link points to
`/docs/guides/platform/backups`. The Transfer Impact Assessment has no
direct link, so it and the Trust Center link go to
`https://trust.supabase.io`, as confirmed by the requester. The version
date reads "October 6, 2026", per the requester (the source PDF gave
only the month), and the closing "Last updated / Owner" line from the
PDF is dropped. Further edits made at the requester's direction: the
Usage Information bullet in the retention answer no longer states a log
purge period, and the Usage Information category in the data-location
answer now says "processors". PDF hyperlinks are mapped to real routes.
The hub gets a `privacy-resources` section, and the DPA and Subprocessor
List breadcrumbs now point to it (text and anchor only). The sitemap is
generated from `pages/**/*.tsx`, so the new route is included without
changes.

## Review instructions

1. Open `/legal` and check the "Privacy Resources" section lists the
three documents, and "Customer Legal Resources" still lists Terms of
Service, Support Policy and Service Level Agreement.
2. Open `/legal/privacy-resources/data-residency-and-transfers-faq` and
compare the text with the source PDF.
3. Click through the links in the page, and the breadcrumbs on the DPA
and Subprocessor List pages.

## Open questions for Legal

None remaining. The requester's edits are applied: the closing "Last
updated" line is removed, the ISO 27001 / SOC 2 link goes to
`/security`, the "Legal Hub" link in the subprocessor question goes to
`/legal`, and the meta description wording is confirmed.

## Checks

- `pnpm install --filter www...` worked. Prettier check passes on the
touched files, MDX compiles, and `next dev` renders both `/legal` and
the new page (200, indexable, canonical set).
- Full `tsc`/lint/`next build` were not run as CI-equivalent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_012JHSu7iLpQ3XPfmfQzFraw

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-08 10:01:57 +11:00
supabase-vercel-tedd[bot]andAli Waseem d72211556b feat(studio): link overview Machine size card to Infrastructure settings (#51402)
## Problem

The project overview shows the project's compute size in a card labeled
"Compute", but the card is not clickable. Compute changes are managed on
Infrastructure settings, so users have to find that page on their own.
The adjacent cards (GitHub, Recent branch, Last migration, Last backup)
already link to where you manage them.

## Solution

- Rename the card label from "Compute" to "Machine size" and pass
`href={getInfrastructurePath(ref)}` to `SingleStat`. That renders the
same `Link` wrapper as the adjacent cards, so it gets the same link
semantics, keyboard focus and hover style. The value (compute badge or
"Unknown", plus the High Availability badge) is unchanged.
- Add `onClick={(e) => e.stopPropagation()}` to the
`HighAvailabilityBadge` hover card content. This is the same pattern
`ComputeBadgeWrapper` already uses. The hover card is portaled, but
React still bubbles its clicks to the new card link. Without this, the
link's `onClick` would intercept clicks inside the HA hover card.
Clicking "Read more" would then go to Infrastructure instead of opening
the docs in a new tab.

Out of scope: "Compute" labels elsewhere, the Infrastructure page and
compute provisioning behavior.

Notes:
- The compute badge's hover card trigger already calls `stopPropagation`
on click, because it also sits inside clickable project cards and table
rows. When you click the badge itself, the browser still follows the
native anchor to Infrastructure settings, but it does a full page load
instead of a client-side navigation. This PR leaves that shared
component unchanged.
- While the project is resizing, the overview is replaced by the
resizing state, so the card is not shown then. Settings routes stay
reachable while a project is building, so the link doesn't bounce users
back home.
- No new component test: rendering `ActivityStats` needs mocks for about
eight queries, including `ServiceStatus`. The change only sets an `href`
on an existing component, so a browser check is a better fit.

## Review instructions

1. Open a platform project's overview (`/project/<ref>`).
2. The card next to Status reads "Machine size" and still shows the
compute badge (and the HA badge on HA projects).
3. Click the card, or Tab to it and press Enter. You land on
`/project/<ref>/settings/infrastructure`.
4. Hover the compute badge: its hover card still opens, and "Upgrade
compute" still goes to Infrastructure.
5. On an HA project, hover the High Availability badge and click "Read
more". The docs open in a new tab and the page does not navigate.
6. GitHub, Recent branch, Last migration and Last backup keep their
labels and destinations.

Checks: `tsc --noEmit` for Studio (no new errors; one unrelated error in
`packages/ui-patterns/.../InstructionBlocks.tsx` was already there),
`eslint` on the touched files (no new warnings), Prettier check, `vitest
components/interfaces/ProjectHome` (34 passed).

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)

---------

Co-authored-by: supabase-vercel-tedd[bot] <336548405+supabase-vercel-tedd[bot]@users.noreply.github.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
2026-10-07 14:37:52 -06:00
Francesco SansalvadoreandClaude Sonnet 5 2d0bd7af69 feat(storage): add object versions data layer (#49207)
| # | Branch | Base |
| - | ------ | ---- |
| 1 | `feat/storage-versioning-private-alpha` — merged | `master` |
| 2 | `feat/storage-versioning/002-bucket-form-fields` | `master` |
| 3 | `feat/storage-versioning/003-bucket-modals` | 2 |
| 4 | `feat/storage-versioning/004-object-versions-data` ◀ | 3 |
| 5 | `feat/storage-versioning/005-file-preview-versions` | 4 |
| 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 |
| 7 | `feat/storage-versioning/007-archived-objects-data` | 6 |
| 8 | `feat/storage-versioning/008-archived-rows` | 7 |
| 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 |
| 10 | `feat/storage-versioning/010-replace-file` | 9 |

## [4/10] Storage object versioning: object versions data layer

**Base:** `feat/storage-versioning/003-bucket-modals` (PR 3)

### This PR

The query and mutation hooks for the version history UI, written to
`queryOptions` using the real Storage endpoints.

- `object-versions-query.ts` — the version list, plus `ObjectVersion`
and `LifecyclePolicy`
- `object-version-restore-mutation.ts` — promote a noncurrent version to
current
- `object-version-delete-mutation.ts` — remove one specific version
- `object-purge-mutation.ts` — delete an object and every version,
bypassing versioning
- `VersionHistory.utils.ts` — `computeVersionFate`, the pure rule
deciding what removal outlook each version row shows
- `BroomSparklesIcon.tsx` — inline SVG for a glyph absent from
lucide-react 0.436

Easier to test directly from next PR in the stack #49208 which wires the
queries to the real file preview panel ui.

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-07 22:12:19 +02:00
Miranda Limonczenko 5c68e24faf fix(docs): keep collapsed sidebar group links in the server HTML (#51373)
Part of
[DOCS-1432](https://linear.app/supabase/issue/DOCS-1432/discovery-gather-information-and-data)

## Problem

While doing Discovery for Information Architecture, I found that my
agents were taking 10+ clicks to find a page. This was _really_ bad. But
it turns out it's an accessibility issue; the agent can't _see_ any
nested subnav item.

Allowing the dom to access the subnav without toggling an accordion
allows an agent to freely navigate, completely resolving some of my
failing evals.


## Solution

- **Render a hidden list of each group's links.** A `<ul hidden>` next
to each group holds its links, nested groups included, so they are in
the HTML. The accordion is unchanged, so the sidebar looks and animates
the same as production.
- Checked for accessibility.




## Manual testing

1. Open the [Platform hub on the deploy
preview](https://docs-git-docs-nav-render-collapsed-links-supabase.vercel.app/docs/guides/platform).
In the sidebar, **Single Sign-On** and **Multi-factor Authentication**
are collapsed.
2. Run `curl -s
https://docs-git-docs-nav-render-collapsed-links-supabase.vercel.app/docs/guides/platform
| grep -o 'href="/docs/guides/platform/sso"'`. It prints the link. The
same command against supabase.com prints nothing.
3. Click **Single Sign-On**. It opens and shows its guides, including
SSO with Google Workspace.
4. Click **Single Sign-On** again. It closes. Notice animation is
intact.
2026-10-07 11:57:03 -07:00
kemal.earth 329b0a0156 fix(studio): animation gradient on log drains empty state (#51403)
## Problem

Animation had values that became deprecated after colour migration.

## Solution

Updated to use the same background colour as everywhere else. To test,
on a free organization navigate to Settings > Log Drains.

| Before | After |
|--------|--------|
| <img width="764" height="356" alt="Screenshot 2026-10-07 at 18 20 21"
src="https://github.com/user-attachments/assets/eb07c4fa-e089-4558-83fe-01ff189eeae4"
/> | <img width="760" height="342" alt="Screenshot 2026-10-07 at 18 24
06"
src="https://github.com/user-attachments/assets/dc7b3998-df1c-4df0-ae15-e23286787cf4"
/> |
2026-10-07 18:42:05 +01:00
Alaister YoungandAlaister Young a8b2f23091 fix(studio): restore deployment update metadata (#51048)
Deployment update checks now receive a timestamp from GitHub’s
documented commit API instead of silently falling back to `unknown` when
its website response changes. Valid deployment metadata is cached for
ten minutes; development and failed lookups are uncached so they can
recover.

**Changed:**

- Validate and normalize the committer date in the shared Next/TanStack
handler, preserving the existing response shape and `unknown` fallback.
- Exclude only the exact deployment-metadata endpoint from TanStack’s
private API cache default; authenticated APIs and server functions
retain it.
- Keep the client update query unpinned and its existing toast threshold
unchanged.

**Added:**

- Shared-handler tests through both Next and the TanStack adapter for
dates, malformed payloads, upstream failures, and recovery.
- Tests using the installed Vercel route compiler for root and
`/dashboard` cache rules, security headers, and empty Next
configuration.

## To test

- On the TanStack preview, request
`/dashboard/api/get-deployment-commit`; compare its SHA and UTC
timestamp with the deployed commit’s GitHub committer date.
- Repeat the request to check CDN caching. Vercel consumes `s-maxage`,
so use cache-hit/age evidence as well as client-visible headers.
- Confirm another API route and a server-function path retain `private,
no-store`.
- Open an existing project, reload it while clean, and inspect the
untouched support form for metadata-related errors.

Validation: 60 focused tests, Studio typecheck, scoped ESLint,
formatting, knip, and both framework production builds passed. Live
unauthenticated GitHub lookup with the configured API version returned
the expected committer date. Full source lint ratchet also passed,
excluding only generated build directories. Local TanStack browser
checks passed for clean project/reload, general settings, untouched
support form, and naturally emitted development metadata formatting; no
errors or unexpected update toast appeared. The positive two-deployment
toast and submitted support-version formatting were not exercised.
Deployed native Next previews returned the exact commit SHA and expected
UTC timestamp; repeated metadata requests produced CDN HIT responses
(ages 26 and 100 seconds). Build logs establish that the staging preview
also ran Next, so it does not validate TanStack edge header behavior. A
separate preview-only redeployment with a deployment-scoped TanStack
override was confirmed to run Vite. It returned the same correct
SHA/timestamp, a repeat CDN cache HIT, and private, no-store on the
neighboring UTC API. No project settings or production aliases were
changed. Installed Vercel compiler tests cover the remaining
root/base-path and server-function rules. No new environment variable,
token, or dependency is required.

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-10-07 19:31:02 +02:00
Alaister YoungandAlaister Young 8d08198cb4 fix(studio): preserve TanStack server source maps (#51051)
TanStack server maps now preserve original TypeScript locations through
both Nitro build stages. Sentry can upload those maps before final
server and public maps are removed; intermediate SSR maps remain
available for composition.

**Changed:**

- Enable Nitro server maps with original source content and compose
adjacent SSR maps through a build-only Vite loader scoped to the Nitro
environment.
- Keep intermediate maps until Nitro consumes them. Apply Sentry’s
bundler plugin to client and final server outputs, preventing
intermediate SSR IDs from overriding the final uploaded map ID. Remove
final server maps after eligible uploads; skipped or unauthenticated
uploads retain private maps.
- Preserve the existing Sentry release, project, credentials,
middleware, and separate Next configuration.

**Added:**

- Real two-stage Node and Vercel build regressions for original
TypeScript positions, runtime debug-ID matching against the installed
Sentry SDK, map cleanup timing, malformed maps, and loader boundaries.

## To test

- Build an actual TanStack preview with the existing Sentry upload
settings. Confirm client and final server uploads include maps,
intermediate SSR maps remain available for composition, final/public
maps are removed, and the deployed function remains within its size
limit.
- Inspect a public JavaScript asset's map URL; it should not expose a
source map.
- Build with uploads skipped or credentials absent; private server maps
should remain, with no public maps.
- Reload an existing project and navigate through general settings and
an existing Edge Function without editing or submitting anything.
- Before production rollout, confirm browser and server events resolve
to useful original source locations in the intended Sentry project.

Validation: 22 focused tests, Studio typecheck, scoped ESLint,
formatting, knip, full source lint ratchet, and both framework
production builds passed. Full-app artifact tracing resolves a compiled
handler to its original TypeScript line with exact source content. The
skipped-upload Vercel build contains zero public maps and no function
symlinks. Local TanStack browser checks passed for project/reload,
untouched general settings and support, and an existing function
editor/reload; a final retest after the runtime-ID fix passed with no
module or Sentry errors. A previously observed devtools
support-lifecycle warning remains unattributed. An explicit TanStack
preview of this commit reached READY: client and final Nitro uploads
succeeded, with no intermediate SSR upload. A shell-referenced
JavaScript asset returned 200 with immutable caching and its map URL
returned 404. Upload reports still contain unmapped files; original app
TypeScript mapping and runtime-ID association are established by build
artifacts/fixtures, while real event deobfuscation remains rollout QA.
Deployment acceptance confirms this preview fits its configured function
limits; exact deployed size and post-upload server contents were not
independently downloaded. No package patch, dependency, or new
environment variable is required.

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-10-07 19:30:08 +02:00
Chris Gwilliams ccb1c60166 fix: show dialog to prevent restoring larger projects to nano compute (#51309) 2026-10-07 20:04:04 +03:00
Alaister YoungandAlaister Young 690d67e372 fix(studio): hash TanStack deployment pins in build cache (#51054)
Studio builds now hash `VERCEL_DEPLOYMENT_ID` because TanStack embeds it
in server-function request pins. Cached output cannot retain an earlier
deployment ID when redeploying the same commit.

**Changed:**

- Move the ID from `passThroughEnv` into `build.env`, keeping runtime
pinning and Skew Protection settings intact.
- Turbo-managed Next builds with a new ID also invalidate the Studio
cache; upstream package caches remain reusable.

## To test

- Run strict Turbo dry runs with two deployment IDs for each framework:
Studio hashes should differ, an unchanged ID should remain stable, and
upstream hashes should remain unchanged.
- Build Next and TanStack. With skew protection enabled, confirm the
TanStack client/server output includes the current deployment pin.
- Reload an existing project and function editor and navigate through
settings without edits.

Validation: actual Turbo dry runs reproduced identical hashes before the
fix and verified distinct/stable hashes afterward for both frameworks.
Studio typecheck, full source lint ratchet, knip, formatting, and both
production builds passed. Emitted TanStack client/server artifacts
contain the synthetic validation ID and deployment header. Local
TanStack checks passed for overview/reload, untouched settings plus
Back, functions list, existing function source/reload, and final clean
return; no console errors or backend changes. Cache-key and genuine
server-function routing claims are separate from this UI coverage. No
new environment variable, dependency, or package patch is required.

Current Vercel app builds invoke the framework directly through the
Studio dispatcher; this change applies when Studio builds run through
Turbo.

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-10-07 18:47:17 +02:00
Alaister YoungandAlaister Young 55c297655e fix(studio): keep project sidebar navigation accessible (#51055)
Project sidebars now expose enabled navigation on fixed-width pages, and
the mobile project menu has an accessible dialog name. Fixed sidebar
sizing and editor resize controls remain intact.

**Changed:**

- Remove the disabled state from the shared sidebar panel while
retaining 256px fixed sizing, the disabled resize handle, and existing
editor resize limits.
- Add a screen-reader-only Project menu title inside the mobile project
sheet without changing other sheets or their titles.

**Added:**

- Tests using the actual resizable wrappers for enabled navigation and
handle semantics, and the actual mobile sheet for its accessible name
through menu navigation.

## To test

- Navigate through Database and Settings sidebar links with ordinary
clicks and Tab/Enter. Enabled links should have no disabled ancestor.
- Confirm fixed sidebars remain 256px wide, including an ordinary drag
of their disabled handle.
- In SQL Editor, verify keyboard resizing stays within 256–512px and
collapse/expand still works; restore the original width and visibility
without editing or executing anything.
- Open the mobile project menu. Confirm its dialog name is Project menu,
navigate through Tables, and close it without a missing-title warning.
Restore the viewport.

Validation: real regressions reproduce disabled navigation inheritance
and the unnamed mobile dialog before their fixes. All 26 focused
layout/menu/utility tests and source checks passed. Initial full browser
checks passed normal mouse/keyboard navigation, fixed-handle drag
resistance, editor resize limits, collapse/expand, and mobile
navigation, with viewport/state restored and no backend writes. Both
Next and TanStack production rebuilds passed. The focused mobile retest
passed the linked Project menu title through sections, closing,
navigation, and reopening with no new missing-title warnings, followed
by desktop sidebar and editor controls; viewport, width, and visibility
were restored. Unrelated development React mount/ref warnings remain
separately recorded; native Next local browser and catalog data were not
verified.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Project sidebar links remain available when sidebar resizing is turned
off; the resize handle stays unavailable in that setting.
* The mobile project menu retains the “Project menu” name as you
navigate between sections.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-10-07 18:45:58 +02:00
Alaister YoungandAlaister Young 46b7382d50 fix(studio): serve Deno declarations as TypeScript (#51057)
## Problem

TanStack does not preserve Next's `text/typescript` content type for the
public Deno declarations. This change restores MIME parity; Monaco
already rendered before the fix.

## Solution

Add the TypeScript header for `/deno/*.ts` at root and configured
base-path URLs. Dynamic API and server-function responses keep their own
content types, security headers and cache rules. Next retains its
existing configuration.

Tests use the installed Vercel routing compiler to cover both
declarations, suffix boundaries, dynamic routes, security headers, the
flags endpoint and the empty Next configuration.

## Review instructions

1. On a TanStack deployment, request `/deno/edge-runtime.d.ts` and
`/deno/lib.deno.d.ts` at root and `/dashboard`. Expect 200,
`text/typescript` and unchanged file contents.
2. Check missing `.ts.map`, `.tsx` and similar paths return 404 without
the TypeScript override. API and server-function responses should retain
their own content types.
3. Open an existing function editor and reload it without editing or
deploying. Confirm the file tree and source still render.
4. Build Next and TanStack. Confirm Next retains its existing
configuration.

Validation: the routing compiler reproduces four declaration-header
failures before the fix; all 30 content-type cases and 10 SPA route
tests pass afterward. Typecheck, knip, the source lint ratchet, scoped
lint, formatting and both production builds passed with uploads
disabled. Built declarations are byte-identical to their public sources.
Local TanStack checks passed the function list, existing source view,
exact editor reload and clean project return without edits or backend
writes. Native Next UI was not tested locally.

The combined TanStack preview at QA commit
`f35b3c42d8ba6a714299b148d797b09107a7a6fe` returned 200,
`text/typescript`, nosniff and byte-identical contents for both
declarations at root and `/dashboard`. Missing `.ts.map` and `.tsx`
paths returned 404 without the override; neighboring dynamic API
responses retained JSON content type and private caching. This preview
evidence covers the combined original fixes, not the subsequent
dependency upgrade.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md).
- [x] No docs content changed; docs authoring skills are not required.

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-10-07 18:42:41 +02:00
Alaister YoungandAlaister Young 820ff0f2d2 fix(studio): handle invalid TanStack API request bodies (#51046)
Malformed JSON now returns `400 Invalid JSON` from the TanStack API
adapter instead of an unhandled 500. Empty JSON bodies and undecodable
cookies follow the existing Next parser behavior.

**Changed:**

- Handle JSON parsing failures before invoking the API handler, while
preserving body-read and downstream handler errors.
- Recognize exact JSON and JSON-LD media types, including case and
charset parameters.
- Keep raw cookie values when URI decoding fails.

**Added:**

- 43 adapter tests covering valid and invalid bodies, cookies, handler
isolation, response headers, streaming, and abort events.

## To test

- POST malformed JSON to `/api/parse-query` with `Content-Type:
application/json`; expect 400 with `Invalid JSON`. Repeat with JSON-LD.
- POST `{"sql":"select 1"}` to the same endpoint; expect 200. This
parses SQL without executing it against a database.
- Repeat the valid request with an undecodable cookie value; expect the
same successful result.
- Open an existing project and database settings; verify normal API
consumers remain usable.

Validation: 59 focused tests, 29 differential body cases plus malformed
cookies against installed Next parsers, Studio typecheck, lint ratchet,
scoped ESLint, formatting, knip, and both framework production builds
passed. Direct local HTTP checks passed for malformed, empty, valid, and
malformed-cookie requests. Next handlers, routes, environment files, and
dependencies are unchanged. Local TanStack browser checks passed for
signed-in project rendering, clean reload, and the untouched support
form. Database settings rendered its error state, but the banned-IP
service returned an upstream connection-timeout payload, so successful
list coverage remains unverified. Chrome blocked direct API-document
navigation; malformed-input behavior is covered by the separate HTTP and
parser checks.

The automatic staging preview ran native Next (confirmed from its build
output), providing an additional Next regression check: malformed JSON
and JSON-LD returned exact `400 Invalid JSON`; valid SQL parsing
returned 200; an undecodable cookie preserved the same valid response.
No SQL was executed. The local TanStack runtime and in-process adapter
checks passed; the combined TanStack deployment results follow.

The combined rollout preview (TanStack, QA commit
`f35b3c42d8ba6a714299b148d797b09107a7a6fe`) also passes deployed
malformed JSON/JSON-LD 400, valid SQL parsing 200, and
undecodable-cookie 200 with the same valid response. No SQL is executed
against a database.

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-10-07 18:31:34 +02:00
Shane AdamsandClaude Sonnet 5.5 6c150d26d9 docs(mgmt-api): clarify rate limits are per endpoint (#51379)
## Problem

The Management API rate limits docs said limits are per user and per
project or organization. They didn't say that each endpoint also has its
own limit, or how requests without a project or organization, and tokens
versus OAuth apps, are counted. The partial also had a typo, a repeated
example, and a tracking-key sentence that listed endpoint as a scope.

## Solution

Edits to `apps/docs/content/_partials/api_rate_limits.mdx`, in one
commit per change type:

1. `docs(mgmt-api): clarify rate limits are per endpoint`: the content
change. It adds the per-endpoint model, the user scope, the `POST
/v1/projects` scoping, and who the limit applies to.
2. **Style**: fixes the `enpoint` typo and the table alignment, removes
the repeated Project A/B paragraph and the `database/context` note that
restated its table rows, unwraps hard-wrapped lines, and aligns bold
labels and wording with the style guide.
3. **Structure**: moves "Rate limit response headers" after "Who the
limit applies to", so the concept sections come before the reference
sections. No headings were renamed, and no inbound anchors to them exist
in `apps` or `packages`.
4. **Technical**: the tracking-key sentence now reads "scope (project or
organization) and the endpoint". It previously listed endpoint as a
scope, which contradicted the scope list.

Not verified against the rate limiter, which isn't in this repo. A
reviewer with access should confirm:

- Requests without a project or organization count against the user.
- `POST /v1/projects` is organization-scoped via `organization_slug`.
- Personal access tokens share the user's limit, and OAuth apps share
the app's limit.
- The tracking-key wording in commit 4 is inferred from the page, not
from code.

## Preview links

| Site | Live | Preview | Search for |
| ---- |
-------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------
| ------------------------------ |
| Docs |
[/docs/reference/api/introduction](https://supabase.com/docs/reference/api/introduction)
|
[/docs/reference/api/introduction](https://docs-git-docs-mgmt-api-rate-limits-per-endpoint-supabase.vercel.app/docs/reference/api/introduction)
| `Who the limit applies to` |

## Review instructions

1. Open the live and preview links side by side and scroll to "Rate
limits".
2. Check that the section order is: Standard rate limit, Rate limit
scope, How rate limits are tracked, Who the limit applies to, Rate limit
response headers, Endpoint exceptions, Best practices.
3. Check that the scope table and the endpoint exceptions tables render
correctly.
4. Review commit by commit, since each commit is one change type.
5. If you can read the rate limiter code, check the unverified claims
listed above.

## Checklist

Check all before review:

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 10:22:44 -06:00
6ef729cb5c feat(storage): versioning bucket modals (FE-4161) (#49205)
| # | Branch | Base |
| - | ------ | ---- |
| 1 | `feat/storage-versioning-private-alpha` — merged | `master` |
| 2 | `feat/storage-versioning/002-bucket-form-fields` | `master` |
| 3 | `feat/storage-versioning/003-bucket-modals` ◀ | 2 |
| 4 | `feat/storage-versioning/004-object-versions-data` | 3 |
| 5 | `feat/storage-versioning/005-file-preview-versions` | 4 |
| 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 |
| 7 | `feat/storage-versioning/007-archived-objects-data` | 6 |
| 8 | `feat/storage-versioning/008-archived-rows` | 7 |
| 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 |
| 10 | `feat/storage-versioning/010-replace-file` | 9 |

## [3/10] Storage object versioning: wire into the bucket modals

**Base:** `feat/storage-versioning/002-bucket-form-fields` (PR 2)

### This PR

Mounts the object-versioning form section in the create and edit bucket
modals behind the feature preview, and saves it.

- create and edit bucket modals spread `bucketVersioningFormFields` into
their existing form schema
- lifecycle defaults  to 30 days / 10 versions
- edit adds a confirmation before suspending an actively versioned
bucket

## Enabling object versioning on a new bucket and setting lifecycle
policies


https://github.com/user-attachments/assets/194f8319-4929-432e-8a50-206f180a77a8

## Edit and suspend object-versioning


https://github.com/user-attachments/assets/f31e1d34-9840-4f5a-a269-6a911214742d

## To reproduce

1. Make sure storage versioning is enabled under feature previews >
Storage Versioning
2. Open Storage Bucket File explorer
3. create new bucket and enable Object Versioning
4. set lifecycle policy
- Noncurrent version expiration: can be either empty or >1
- Retained noncurrent versions: can be either empty or between 1 and 100
and can't exist without "Noncurrent version expiration"
5. Open new bucket with object versioning and test changing lifecycle
policies
6. Disabling object-versioning shows proper warning and updates
`versioning_status` to SUSPENDED (it can never go back to DISABLED once
it has been enabled on a bucket)

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
2026-10-07 16:38:35 +02:00
Ivan VasilovandClaude Sonnet 5.5 075c611463 chore: bump vulnerable dependencies (#51381)
## Summary

- Bumps vulnerable transitive dependencies flagged by `pnpm audit`, one
commit per dependency (lockfile only, no permanent overrides):
proxy-addr, shell-quote, @fastify/busboy,
@graphql-tools/executor-legacy-ws, @modelcontextprotocol/sdk,
compression, http-cache-semantics, source-map-js, smol-toml, dompurify.
- Updates `scripts/fix-audit-vulnerability.ts` to be agent-friendly:
accepts a dependency name argument, adds `--json` (single JSON object on
stdout, logs on stderr, never prompts) and `--help`.

## Not fixed

The remaining audit findings could not be resolved by this script. Some
are blocked by `minimumReleaseAge` (braces, node-forge, sprintf-js);
others stay vulnerable even with an override and need a parent
dependency update or scoped override.

## Test plan

- [ ] CI passes (typecheck, lint, prettier)
- [ ] `pnpm audit` shows fewer findings than on master

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 16:23:43 +02:00
claude[bot]andClaude 6c6b19c6c6 chore(studio): report empty-body GET 200s to Sentry with a no-store probe (#51123)
<!-- ccr-slack-attribution -->
_Requested by **Ivan Vasilov** · [Slack
thread](https://supabase.slack.com/archives/C063LNYJJKS/p1790710416069689?thread_ts=1790710416.069689&cid=C063LNYJJKS)_

**Before:** When a Studio API GET comes back as a 200 with an empty
body, openapi-fetch hands the caller `{}` and we only see the downstream
crash, with no record of the response that caused it.

**After:** The first time this happens for an endpoint in a page
session, Studio sends one Sentry warning, `Empty response body on
successful API request`. It carries the response metadata, browser
state, resource timing, and the result of a single `cache: 'no-store'`
refetch. What the caller receives is unchanged.

## Problem

Studio crashes trace back to GET requests that return 200 with an empty
body, which openapi-fetch turns into `{}`. They are heavily skewed to
Firefox and Safari. The leading hypothesis is browser cache revalidation
(Express weak ETags, no `Cache-Control` on api.supabase.com), but
nothing confirms it yet. The `no-store` probe tells the two cases apart:
if the refetch has a body, the browser cache is the likely culprit; if
it is also empty, the server or the edge is sending empty bodies. This
data should show whether the fix belongs on the API side or the
Cloudflare side.

Context: #51041 (closed) tried to guard the crashing call sites instead.

## Needs API-side change to be fully useful

Cross-origin, Studio can only read CORS-safelisted response headers, and
resource timing sizes read as zero. If api.supabase.com sends
`Access-Control-Expose-Headers: ETag, cf-ray, cf-cache-status,
x-request-id` and `Timing-Allow-Origin: <studio origin>`, this event
will also carry the ETag, cf-ray, and cache status, plus the real
transfer and body sizes and the negotiated protocol. Until then, those
fields read as `null` or `0`.

## Solution

- `data/empty-body-diagnostics.ts` (new): `reportEmptyBodyResponse({
request, response, schemaPath })`.
- Runs only for `GET` and only when `IS_PLATFORM`. Empty POST/201 bodies
are legitimate.
- Reports at most once per templated endpoint per page session
(module-level `Set`).
- Endpoint: openapi-fetch's `schemaPath` (e.g.
`/platform/projects/{ref}/settings`), passed through
`templateEndpointPath`. That function drops the query string and hash,
replaces the segment after `projects`/`organizations`/`branches` with
`{ref}`/`{slug}`/`{branch}`, and replaces UUIDs, numeric IDs, and 20+
character alphanumeric IDs with `{id}`. I used `schemaPath` rather than
the request URL so user-chosen names (bucket names, function slugs)
never end up in tags or fingerprints.
- Probe: one plain `fetch(new Request(request, { cache: 'no-store', ...
}))` with a fresh `X-Request-Id` and a 10s `AbortController` timeout.
`AbortSignal.timeout` isn't available in older Safari. The probe
bypasses the openapi-fetch middleware, so it can't recurse. Only the
body's byte length is recorded, never its contents.
- Event: `level: 'warning'`, `fingerprint: ['empty-body-response',
endpoint]`, `tags: { endpoint, probe_has_body, empty_body_diagnostic:
'true' }`, where `probe_has_body` is `true` / `false` / `error`. `extra`
holds:
- the request: method, status, `response.type`, `redirected`, and the
original `X-Request-Id` (for API log lookup)
- response headers: `content-type`, `cache-control`, `last-modified`,
`expires`, `content-length`, `etag`, `cf-ray`, `cf-cache-status`,
`x-request-id`
- browser state: `visibilityState`, `navigator.onLine`, the navigation
type, ms since navigation start, and whether the page was restored from
bfcache
- the latest `PerformanceResourceTiming` for the URL (transfer, encoded,
and decoded size, `nextHopProtocol`, `responseStatus`)
- the probe: status, request ID, body length, `content-length`,
`content-type`, or the error name
- Fire-and-forget: everything is wrapped in a `try`/`catch`, and the
caller does not await it.
- `data/fetchers.ts`: the `onResponse` middleware passes `{ request,
schemaPath }` to `normalizeEmptyBodyResponse`, which calls the reporter
in its empty-body branch and also for a 200 that carries
`Content-Length: 0`. openapi-fetch short-circuits that case to `{}` the
same way, so it is the same symptom. The return value is unchanged in
every branch.
- `packages/common/sentry.ts`: `filterSentryEvent` normally keeps only
1% of events that aren't page crashes. It now sends events tagged
`empty_body_diagnostic` unsampled, with `codeSampleRate: '1'`. A
once-per-session warning would barely show up at 1%. Consent and
platform gating and the third-party filter still apply. www and docs
also use `filterSentryEvent`, but only Studio's reporter sets this tag,
so sampling for them and for every other Studio event is unchanged.

Sentry config: Studio's `beforeSend` doesn't otherwise drop this
message. It has no exception values, so the no-stack-trace filter
doesn't apply, and it matches no `ignoreErrors` entry.

## Review instructions

1. Check `normalizeEmptyBodyResponse` in `data/fetchers.ts`: the
reporter is `void`-called and its return value is untouched.
2. Check `probe()` in `data/empty-body-diagnostics.ts`: only
`byteLength` is read from the body. The probe reuses the original
request's headers and credentials (same auth as the original GET).
3. Check `filterSentryEvent` in `packages/common/sentry.ts`: only the
`empty_body_diagnostic` tag skips sampling.
4. Tests: `data/empty-body-diagnostics.test.ts` and
`packages/common/sentry.test.ts`.

## Verification

- Unit tests (`data/empty-body-diagnostics.test.ts`, new):
  - path templating cases
  - `probe_has_body` `true` / `false` / `error`
  - the secret body content never appears in the Sentry call
  - one report per endpoint
  - non-GET and non-platform requests are skipped
  - no throw when `fetch` or Sentry throws
- end-to-end through `client.GET`: still resolves `{}` and reports the
`schemaPath`, for both a missing `Content-Length` and `Content-Length:
0`
- `packages/common/sentry.test.ts`: tagged diagnostics are sent
unsampled, untagged or false-tagged ones are still sampled, and they're
still dropped without consent.

These tests, plus the existing `normalizeEmptyBodyResponse.test.ts`,
`handleError.test.ts`, and the rest of `sentry.test.ts`, pass (67 tests)
under vitest 5 + jsdom. I ran them in a minimal harness, not the full
`pnpm install` workspace, because the local checkout is sparse.
- I ran TypeScript 7.0.2 (`--strict`) on the five touched files against
the real `api-types`, with stubbed `common`/Sentry types. No errors in
the touched files.
- Prettier `--check` with the repo config passes, with and without
`SORT_IMPORTS=false`.
- Not run locally: the full studio typecheck, `lint:ratchet`, and knip.
CI covers them. The change adds no `any`, no default exports, and no
deps.

## Checklist

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01UefDak8XYLMi9aiEjDPXc5

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-07 16:15:57 +02:00