Skip to content

feat: pass cache headers to setAll to prevent CDN caching of auth responses#176

Merged
mandarini merged 1 commit into
mainfrom
feat/set-cache-headers-on-setall
Mar 19, 2026
Merged

feat: pass cache headers to setAll to prevent CDN caching of auth responses#176
mandarini merged 1 commit into
mainfrom
feat/set-cache-headers-on-setall

Conversation

@mandarini
Copy link
Copy Markdown
Contributor

What

SetAllCookies now receives a required second argument headers: Record<string, string> alongside the cookies array. When applyServerStorage calls setAll after a token refresh or any auth state change, it passes the following headers:

{
  'Cache-Control': 'private, no-cache, no-store, must-revalidate, max-age=0',
  'Expires': '0',
  'Pragma': 'no-cache',
}

Users must apply these headers to their HTTP response in their setAll implementation:

// Next.js middleware
cookies: {
  setAll(cookiesToSet, headers) {
    cookiesToSet.forEach(({ name, value, options }) =>
      response.cookies.set(name, value, options)
    )
    Object.entries(headers).forEach(([key, value]) =>
      response.headers.set(key, value)
    )
  }
}
// Nuxt server middleware
cookies: {
  setAll(cookiesToSet, headers) {
    cookiesToSet.forEach(({ name, value, options }) =>
      setCookie(event, name, value, options)
    )
    Object.entries(headers).forEach(([key, value]) =>
      setHeader(event, key, value)
    )
  }
}

Why

See: supabase/supabase-js#1682

When @supabase/ssr refreshes a session server-side, the new JWT is written to the response via Set-Cookie. If a CDN (CloudFront, Vercel Edge, Cloudflare, etc.) caches that response and serves it to a different user, that user's browser stores the cached token and is signed in as the wrong person. This has been confirmed in production by multiple users.

The library knows exactly when this happens, which is inside applyServerStorage, triggered by the TOKEN_REFRESHED event, but previously gave the user no way to know they needed to set cache headers. The fix surfaces that information directly in the setAll callback.

Breaking change

SetAllCookies now has a required second argument. Existing setAll implementations that do not declare the second parameter will not receive a TypeScript error (TypeScript allows functions with fewer parameters to satisfy a type expecting more), but they will silently miss applying the headers. All official quickstart examples and docs will be updated to include the headers.

What was considered and ruled out

  • serverRefresh: false option: Rejected. The server must be able to refresh an expired token before rendering auth-gated pages — skipping server-side refresh entirely breaks the core SSR auth flow.
  • Docs-only fix: Insufficient. Users who copy an outdated quickstart or don't read the changelog stay insecure without knowing it.
  • Optional headers? argument: Also insufficient for the same reason — optional typing gives users an escape hatch and TypeScript won't warn them.
  • Setting headers on the fetch request: auth-js previously added cache: no-store to outgoing fetch requests (PR #847) and had to revert it (PR #886) because it broke Cloudflare's fetch handling. That was a different mechanism (Next.js Data Cache on outgoing requests). Our change operates at the HTTP response level via the user's setAll callback and does not touch fetch options.

Files changed

  • src/types.tsSetAllCookies type updated with required headers second argument and JSDoc
  • src/cookies.tsapplyServerStorage passes cache headers; browser-only setAll call sites pass {}
  • src/cookies.spec.ts — existing tests updated; new assertion verifies applyServerStorage passes the correct headers

Related: supabase/supabase-js#1682

@mandarini mandarini marked this pull request as ready for review March 12, 2026 06:46
@mandarini mandarini requested a review from fadymak March 12, 2026 06:46
@mandarini mandarini self-assigned this Mar 12, 2026
@mandarini mandarini force-pushed the feat/set-cache-headers-on-setall branch from 5723bd6 to 5e9bb67 Compare March 12, 2026 06:57
@mandarini mandarini merged commit 14962d2 into main Mar 19, 2026
3 checks passed
@mandarini mandarini deleted the feat/set-cache-headers-on-setall branch March 19, 2026 06:31
mandarini pushed a commit that referenced this pull request Mar 30, 2026
🤖 I have created a release *beep* *boop*
---


## [0.10.0](v0.9.0...v0.10.0)
(2026-03-30)


### Features

* pass cache headers to setAll to prevent CDN caching of auth responses
([#176](#176))
([14962d2](14962d2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: supabase-releaser[bot] <223506987+supabase-releaser[bot]@users.noreply.github.com>
mandarini added a commit to supabase/supabase that referenced this pull request Mar 30, 2026
…#44240)

## What

Updates all `setAll` cookie handler implementations across docs and
examples to accept the new `headers` second argument introduced in
`@supabase/ssr` v0.10.0
([supabase/ssr#176](supabase/ssr#176)).

## Why

`@supabase/ssr` v0.10.0 introduced a breaking change: `setAll` now
receives a required second argument `headers: Record<string, string>`
alongside the cookies array. When a token refresh occurs, the library
passes cache headers (`Cache-Control`, `Expires`, `Pragma`) that must be
applied to the HTTP response to prevent CDN caching of auth responses.

Because TypeScript allows functions with fewer parameters to satisfy a
type expecting more, existing `setAll` implementations do not produce a
type error when the second argument is omitted. Users who copy an
outdated snippet will silently miss the CDN protection.

Root cause and context:
[supabase/supabase-js#1682](supabase/supabase-js#1682)

## Changes

**Proxy/middleware contexts** (where token refreshes happen) now apply
the cache headers to their response:
- Next.js proxy files: `supabaseResponse.headers.set(key, value)`
- SvelteKit hooks: `event.setHeaders(headers)`
- Hono middleware: `c.header(key, value)`
- Pages Router (Express-style): `ctx.res.setHeader(key, value)`
- Remix/React Router loaders and actions: applied to response headers
(outer `headers` variable renamed to `responseHeaders` to avoid naming
conflict with the new param)

**Server Component and API route contexts** (no response object
available) accept `_headers` without applying them.

## Files updated

- `apps/docs/content/guides/auth/server-side/creating-a-client.mdx`
(inline Astro, Remix, React Router, Express snippets)
- `apps/docs/content/_partials/oauth_pkce_flow.mdx`
- `apps/docs/content/guides/auth/oauth-server/getting-started.mdx`
- `apps/docs/content/guides/auth/passwords.mdx`
-
`apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx`
- `examples/auth/nextjs/`, `examples/auth/nextjs-full/` (proxy + server)
- `examples/auth/sveltekit/`, `examples/auth/sveltekit-full/`
- `examples/auth/hono/`, `examples/auth/hono-full/`
- `examples/user-management/nextjs-user-management/` (proxy + server)
- `examples/user-management/sveltekit-user-management/`
- `examples/realtime/nextjs-authorization-demo/` (proxy + server)
- `examples/realtime/nextjs-auth-presence/` (pages router)
- `examples/prompts/nextjs-supabase-auth.md`
mattrossman pushed a commit to supabase/supabase that referenced this pull request Mar 30, 2026
…#44240)

## What

Updates all `setAll` cookie handler implementations across docs and
examples to accept the new `headers` second argument introduced in
`@supabase/ssr` v0.10.0
([supabase/ssr#176](supabase/ssr#176)).

## Why

`@supabase/ssr` v0.10.0 introduced a breaking change: `setAll` now
receives a required second argument `headers: Record<string, string>`
alongside the cookies array. When a token refresh occurs, the library
passes cache headers (`Cache-Control`, `Expires`, `Pragma`) that must be
applied to the HTTP response to prevent CDN caching of auth responses.

Because TypeScript allows functions with fewer parameters to satisfy a
type expecting more, existing `setAll` implementations do not produce a
type error when the second argument is omitted. Users who copy an
outdated snippet will silently miss the CDN protection.

Root cause and context:
[supabase/supabase-js#1682](supabase/supabase-js#1682)

## Changes

**Proxy/middleware contexts** (where token refreshes happen) now apply
the cache headers to their response:
- Next.js proxy files: `supabaseResponse.headers.set(key, value)`
- SvelteKit hooks: `event.setHeaders(headers)`
- Hono middleware: `c.header(key, value)`
- Pages Router (Express-style): `ctx.res.setHeader(key, value)`
- Remix/React Router loaders and actions: applied to response headers
(outer `headers` variable renamed to `responseHeaders` to avoid naming
conflict with the new param)

**Server Component and API route contexts** (no response object
available) accept `_headers` without applying them.

## Files updated

- `apps/docs/content/guides/auth/server-side/creating-a-client.mdx`
(inline Astro, Remix, React Router, Express snippets)
- `apps/docs/content/_partials/oauth_pkce_flow.mdx`
- `apps/docs/content/guides/auth/oauth-server/getting-started.mdx`
- `apps/docs/content/guides/auth/passwords.mdx`
-
`apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx`
- `examples/auth/nextjs/`, `examples/auth/nextjs-full/` (proxy + server)
- `examples/auth/sveltekit/`, `examples/auth/sveltekit-full/`
- `examples/auth/hono/`, `examples/auth/hono-full/`
- `examples/user-management/nextjs-user-management/` (proxy + server)
- `examples/user-management/sveltekit-user-management/`
- `examples/realtime/nextjs-authorization-demo/` (proxy + server)
- `examples/realtime/nextjs-auth-presence/` (pages router)
- `examples/prompts/nextjs-supabase-auth.md`
@jonzhep
Copy link
Copy Markdown

jonzhep commented Mar 30, 2026

amazing work

ivasilov pushed a commit to supabase/supabase that referenced this pull request Apr 15, 2026
stable StorageFilePicker

StorageFilePicker in oauth app logo_uri

StorageFilePicker accepted extentions

StorageFilePicker preview then select

update logo_uri field description

prettier

fix: append semicolons to sql statements in migration view (#32814)

fix: copy cell for record peak portal on forgien relationship (#44144)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES/NO

fixes: #37454

The context menu on the FK row is a lie, its just exposing the cell
underneath. Ideally this should have its own context menu, right now we
only support copy operations.

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>

fix(docs): initialize FormBuilder in constructor in Angular tutorial (#42600)

Bug fix — corrects a TypeScript initialization error in the Angular user
management tutorial example.

In the Angular tutorial's `AuthComponent`, `signInForm` is declared with
a non-null assertion (`signInForm!: FormGroup`) and initialized inside
`ngOnInit()`. This causes a TypeScript strict-mode error because
`formBuilder` is used before the constructor runs. Developers following
the tutorial encounter:

> Property 'formBuilder' is used before being initialized.

`signInForm` is now initialized in the constructor, matching the pattern
already used by `AccountComponent` in the same example project. The
non-null assertion is removed since the property is properly assigned
during construction.

**`examples/user-management/angular-user-management/src/app/auth/auth.component.ts`**
- Moved `signInForm` initialization from `ngOnInit()` into the
constructor
- Removed non-null assertion operator (`!`) from `signInForm`
declaration
- Reordered property declarations for consistency (`loading` before
`signInForm`)

The sibling `AccountComponent` already follows the correct pattern —
initializing `updateProfileForm` inside the constructor (line 35). This
PR aligns `AuthComponent` with that established convention.

Closes #34392

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

* **New Features**
  * Added a loading indicator state to the authentication form.

* **Refactor**
  * Restructured form initialization logic for improved component setup.

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

fix: cannot create nor edit foreign keys from schema visualiser (#44190)

Users cannot create nor edit foreign keys when editing a table or a
column from the schema visualiser

The foreign key edition component is always reading the current table
from the URL parameters. However, in the context of the schema
visualiser, the parameter does not exist.

Allows to override it from the side panel editor context

- Create two tables
- Go to the schema visualiser and edit one of the table
- Click the _Add foreign key relation_ and add a foreign key to the
other table

- Edit one of the foreign key column
- You should be able to edit the relation

fix: remove SMS validation of fields for Twilio Verify (#44198)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Investigation by claude, validated!

- Before (Formik): The old form used `<Form
initialValues={INITIAL_VALUES} validationSchema={...}>` Formik does not
unregister hidden fields — all fields from initialValues stay in form
state with their initial values, so hidden required fields still pass
validation because they retain their default values (e.g., the OTP
expiry/length numbers from the config).

- After (react-hook-form): The new form uses useForm({ shouldUnregister:
true }). This explicitly removes fields from form state when their
components unmount. When Twilio Verify is selected, the three hidden
fields are unmounted, their values become undefined, and yup's
unconditional .required() fails silently.

this bug was introduced today by PR #44095, the
Formik-to-react-hook-form migration.

chore(studio): webhooks UI cleanup (#43797)

UI updates to platform webhooks.

- `Webhooks` icon on empty state
- Better text wrapping/overflow handling on table
- Clearer endpoint deletion dialog

| After |
| --- |
| <img width="1024" height="563" alt="Webhooks Toolshed
Supabase-A3AF1CE1-B489-450F-9FD7-AE1B1BC0C999"
src="https://github.com/user-attachments/assets/6606922d-fd39-400e-bc2d-99806755fc09"
/> |
| <img width="1024" height="563" alt="Webhooks Toolshed
Supabase-00EF8F02-A3F2-477C-ADDB-68996D6C16E3"
src="https://github.com/user-attachments/assets/705a1d78-4366-4ea4-b538-99eb368a3997"
/> |
| <img width="1024" height="563" alt="Webhooks Toolshed
Supabase-902AC0EC-D0D2-471A-B241-C90FA8F50883"
src="https://github.com/user-attachments/assets/97ff1267-b31b-442f-ad14-62b0bd24ac46"
/> |

chore(studio): database tables UI improvements (#44163)

UI improvements

- The database tables list and columns list use inconsistent page shells
and table primitives
- The child columns page has weaker information hierarchy and row
actions than the parent tables page
- Responsive column priority on the tables list does not reflect the
most important data on smaller breakpoints
- Table actions and counts are harder to scan than they should be

- Both pages now use `PageLayout` with matching large-width content
containers
- `ColumnList` now uses the latest `ui` Table primitives instead of the
legacy cleaned-up-later table
- Both pages now show totals in a table footer
- `ColumnList` now uses a tiny filter input, case-insensitive filtering,
inline descriptions under the name, and a primary `Edit` button with
overflow actions
- `TableList` now has improved responsive column priority:
  - smallest breakpoint keeps `Rows`
  - `Columns` appears from `sm`
  - `Size` appears from `lg`
  - `Realtime Enabled` appears from `2xl`
- `TableList` now uses `View columns` as the CTA, removes the ambiguous
icon from that CTA, restores the entity icon from `sm` upwards only, and
tightens the name column on the smallest breakpoint only
- Boolean icon columns are right-aligned consistently, with the same
Realtime icon tones applied to both `Realtime Enabled` and `Nullable`
- The columns detail page now uses breadcrumbs for navigation back to
Tables instead of an inline back button

| Before | After |
| --- | --- |
| <img width="1728" height="997" alt="Tables Database Mallet Toolshed
Supabase-0E0E3DE0-4EA1-407F-88D4-B85664D26D8E"
src="https://github.com/user-attachments/assets/3a2e265c-394e-432c-8c29-12317b60fda8"
/> | <img width="1728" height="997" alt="Tables Database Mallet Toolshed
Supabase-C8FC339C-E9DA-4ADB-8458-C7EFF55F2AEC"
src="https://github.com/user-attachments/assets/50c83a3f-a70c-4d09-a8c3-1eeaed68b68b"
/> |
| <img width="1728" height="997" alt="Tables Database Mallet Toolshed
Supabase-FE9196A0-BEAF-4BA5-8A2C-06F934A62C38"
src="https://github.com/user-attachments/assets/707a564a-e764-45ac-8470-8532e22d39bc"
/> | <img width="1728" height="997" alt="Tables Database Mallet Toolshed
Supabase-36E93C1E-7943-4C98-8119-CAF48E2FE5BA"
src="https://github.com/user-attachments/assets/4cba5791-a4d7-4f43-aea0-8277b2ec5d28"
/> |

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>

fix: hide tax id banner for billing partner orgs (#44195)

- (shouldFetch): `!org.billing_partner` - prevents the unnecessary API
call to fetch the customer profile for partner orgs.
- (early return guard): `org.billing_partner` - returns null (hides the
banner) when the org has a billing partner.

Added Navigating Regional Network Blocks blog post (#44203)

feat(assistant): upgrade default models to gpt-5.4-nano and gpt-5.3-codex (#44107)

Replaces `gpt-5-mini` and `gpt-5` with `gpt-5.4-nano` and
`gpt-5.3-codex` respectively. Clients with stale model IDs in IndexedDB
will gracefully reset to the new defaults. While we can technically keep
the existing models around, we've
[opted](https://supabase.slack.com/archives/C051L8U2EJF/p1774283070517609?thread_ts=1773771991.871669&cid=C051L8U2EJF)
to replace them w/ the newer models for simplicity. Basic completion
endpoints use `'none'` reasoning level for optimal speed.

Rationale for these models is they provide they best balance of
intelligence/speed and cost. GPT-5.4-nano is less expensive (0.8x
price), faster, and smarter than GPT-5-mini. GPT-5.4-mini would be even
smarter but is 3x the price. GPT-5.3-Codex is ~1.4x the price of GPT-5,
while GPT-5.4 would be 2x price, but 5.3-Codex is still a big
intelligence boost from GPT-5.

See [eval
comparison](https://www.braintrust.dev/app/supabase.io/p/Assistant/experiments/mattrossman%2Fai-509-v2-upgrade-assistant-models-beyond-gpt-5-family-1774468619?c=master-1774458837&diff=between_experiments),
scores are relatively stable and conciseness naturally improves on
gpt-5.4-nano.

Other change:
- Fixed an eval test case to clarify that https://supabase.help is also
a correct URL for submitting support ticket, which was unfairly scored
as incorrect
[here](https://www.braintrust.dev/app/supabase.io/p/Assistant/trace?object_type=experiment&object_id=5244cccd-23b2-4f79-9dd2-287f1b40ebad&r=bac9b903-8bde-4c21-99dd-e0ed141c4f9e&s=f248fbf5-75bf-4aab-be0a-87a4298e6d11)

I sanity checked the Assistant, natural language filters, and SQL Editor
completions on staging preview.

References:
- https://openai.com/index/introducing-gpt-5-4-mini-and-nano/
- https://openai.com/index/introducing-gpt-5-3-codex/
- https://developers.openai.com/api/docs/pricing

Closes AI-509

Refactor ColumnEditor to use latest UI components + fix some UI oddities (#44214)

Refactors the Table Editor's `ColumnEditor` to use the latest UI
components, and fix some UI oddities along the way

- Fix header text vertical alignment
  - Before:
<img width="325" height="59" alt="image"
src="https://github.com/user-attachments/assets/e4bc07d4-2630-4a86-a87c-4bbbf94e2f52"
/>
  - After:
<img width="351" height="74" alt="image"
src="https://github.com/user-attachments/assets/d0a0a246-59b6-4d19-8674-8cc5eb33772c"
/>
- Fix closing a toast would close the panel as well
- Can verify by creating a new column, then hitting save without
entering anything. Will trigger some error toasts and closing them will
close the panel too
- Fix tooltips on "is nullable" and "is unique" showing up irregardless
if "is primary key" is toggled on or off

fix: revert changes when the value is the same as old (#44196)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Revert changes when the value is the same as the old value

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>

chore: pin all actions (#42068)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

chore

We will require all actions to be pinned going forward

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

* **Chores**
* Updated CI/CD workflows to use pinned action versions for improved
reliability and consistency.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

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

chore(self-hosted): remove backticks in headers in self-hosted how-to (#44184)

feat(studio): improve database column list scanning (#44208)

UI improvements. Resolves DEPR-419.

- `ColumnList` splits type information across separate `Data Type` and
`Format` columns, which makes scanning less efficient
- The list only surfaces a limited subset of column metadata up front,
so key structural information like primary keys, foreign keys,
uniqueness, and identity is not easy to scan
- Constraint and nullability affordances are not aligned with the visual
language already used in the Table Editor and Schema Visualiser

- `ColumnList` now shows a leading type affordance icon, while keeping
the exact Postgres type visible in the `Type` column
- The standalone `Data Type` column is removed, and `format` is now the
primary textual type shown in the table
- The `Constraints` column now surfaces explicit icon-plus-label tokens
for:
  - `Primary`
  - `Foreign key`
  - `Unique`
  - `Identity`
  - `Nullable` / `Non-nullable`
- Constraint tokens use a purpose-built segmented style based on
`ComputeBadge`, including a green primary variant for primary keys
- Nullability now matches Schema Visualiser semantics exactly: outlined
diamond for nullable, filled diamond for non-nullable
- Existing search, descriptions, row actions, and permission gating are
preserved

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>

docs(self-host): custom email templates (#42832)

chore: update database role form to use `react-hook-form` (#44178)

- The database role form still uses `formik` and we want to remove it in
favour of `react-hook-form` to keep only one form library
- The database role form does not follow the design system guidelines

- Migrate to `react-hook-form`
- Apply the design system guidelines
- Make sure you can toggle switches by clicking their labels too
- Fix accessibility issues

In the new design system, labels for disabled inputs have the same style
as those for enabled inputs

Before:
<img width="1325" height="288" alt="image"
src="https://github.com/user-attachments/assets/5e558618-3227-42be-b085-4ee388c0aff6"
/>
<img width="1328" height="402" alt="image"
src="https://github.com/user-attachments/assets/9e41a4c2-ab38-4772-b619-548a1a0b9556"
/>

After:
<img width="1281" height="325" alt="image"
src="https://github.com/user-attachments/assets/698e526c-5ae3-4e89-bcb5-0bfee1b70f72"
/>
<img width="1285" height="428" alt="image"
src="https://github.com/user-attachments/assets/65b30dc2-9724-4609-9fd0-f32171e37abd"
/>

chore: update cli reference doc (#44202)

Co-authored-by: supabase-cli-releaser[bot] <246109035+supabase-cli-releaser[bot]@users.noreply.github.com>

docs: reports link (#44186)

small fix 😅

<img width="737" height="291" alt="image"
src="https://github.com/user-attachments/assets/2041e729-2d15-4b99-b0b9-e591673861f2"
/>

<img width="875" height="474" alt="image"
src="https://github.com/user-attachments/assets/3260b0b4-6582-489d-84d1-a72db1e50bc4"
/>

docs: Update Ionic React guide with correct auth methods, updated dependencies, and code sample component (#43858)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

docs: Update branching.mdx | fixed typo (#44112)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

docs update

Uses the phrase: "For e.g." instead of "For instance,"

Substituted out the abbreviation "e.g." for the word "instance"

---------

Co-authored-by: Chris Chinchilla <chris.ward@supabase.io>

chore(docs): add Jordan McQueen to humans.txt (#44218)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Docs update to `humans.txt` to add `Jordan McQueen`

`Jordan McQueen` does not yet exist in `humans.txt`

`Jordan McQueen` exists in `humans.txt`

chore: migrate observability report modals to use `react-hook-form` (#44227)

- observability report modals still uses `formik` and we want to remove
it in favour of `react-hook-form` to keep only one form library

- Migrate to `react-hook-form`

docs: move the rest of the docs to the js repo (#44096)

Ref PR: https://github.com/supabase/supabase-js/pull/2165

Remove entries from yaml. Use `tsdoc` only.

fix: hooks form (#44231)

fixes the scroll behavior for the schema and function dropdowns in the
auth Hooks form for sms & email

https://github.com/user-attachments/assets/dd930a4d-da48-4b75-94ba-22f37973fa0e

https://github.com/user-attachments/assets/c08d4aff-d17d-4a8d-8a90-c6c111f28695

- closes https://github.com/supabase/supabase/issues/44222
- Same pattern as https://github.com/supabase/supabase/pull/44181

chore: Bump Typescript to v6 (#44204)

chore: use `react-hook-form` in disk size configuration modal (#44233)

- Disk size configuration modal still uses `formik` and we want to
remove it in favour of `react-hook-form` to keep only one form library

- Migrate to `react-hook-form`

Haven't found a way to test locally except by temporarily inverting
conditions in `apps/studio/pages/project/[ref]/database/settings.tsx` at
L57 and checking the API request payload in the network tab.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

chore: use `react-hook-form` in SQL snippets modals (#44232)

- SQL snippets modals still uses `formik` and we want to remove it in
favour of `react-hook-form` to keep only one form library

- Migrate to `react-hook-form`

chore: use `react-hook-form` in network restrictions modal (#44238)

- Network restrictions modal still uses `formik` and we want to remove
it in favour of `react-hook-form` to keep only one form library

- Migrate to `react-hook-form`

<img width="546" height="478" alt="image"
src="https://github.com/user-attachments/assets/457e59c0-e4cf-4bcb-b941-68cdb8876b45"
/>

<img width="568" height="511" alt="image"
src="https://github.com/user-attachments/assets/b118468a-bbe0-4c59-a63c-4bb33fe548eb"
/>

chore: `react-hook-form` best practices (#44221)

The design system documentation and examples promote an invalid usage of
react-hook-form state.

- Fix the documentation and examples
- Update Claude skills for future components

- Fix current code across the repository

feat(docs): docs archive (#44206)

Adds a `https://supabase.com/docs/docs.tar.gz` which bundles the
generated markdown files and is served from `./public`. This archive is
needed by the supabase-ssh project.

> Note: clicking this will download the archive (~936KB)

https://docs-git-feat-docs-archive-supabase.vercel.app/docs/docs.tar.gz

Revert "Revert "blog: Supabase joins the Stripe Projects developer preview"" (#44115)

Reverts supabase/supabase#44109

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

New blog post for Stripe Projects

N/A

Adds a new blog post: "Supabase joins the Stripe Projects Developer
Preview" and adds Gregor Vand and Ana Mogul to authors.json.

chore(docs): add David Alvarez to list of contributors (#44209)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

docs update to add David Alvarez to humans.txt

David Alvarez is not currently in the humans.txt file

David Alvarez is now in the humans.txt file!

fix: suppress misleading toast when navigating away from a deleted project (#44032)

Bug fix

When a user deletes a project, the `useProjectDetailQuery` in
`RouteValidationWrapper` returns an error (HTTP 404) because the project
no longer exists. This triggers the `isErrorProject` effect, which shows
a "You do not have access to this project" toast and redirects to the
home page — even though the user intentionally deleted the project.

The error's HTTP status code is checked before showing the toast. A 404
response (project not found / deleted) silently redirects without
showing the misleading access-denied toast. The toast is still shown for
other error codes (e.g. 403 Forbidden), where the "no access" message is
accurate.

Resolves FE-2831

Added Stripe Projects go page (#43968)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Added a new landing page to support the Stripe Projects launch.

---------

Co-authored-by: Ana <ana1337x@users.noreply.github.com>

Update humans.txt (#44230)

Adding myself to the Supabase team version 2 (alphabetical order)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Update - Adding myself to the Supabase team version 2 (alphabetical
order)

chore(studio): add pagination to webhook deliveries table (#43847)

UI update.

The webhook endpoint details view showed all mock deliveries in a single
table with a Studio-specific sortable header treatment, no pagination,
and uneven row heights when the retry action was absent.

Moves the deliveries table onto TanStack table state, adopts the shared
`TableHeadSort` header UI, keeps the existing delivery search, and adds
simple previous/next pagination controls at the bottom. The mock
deliveries are expanded so pagination and sorting can be exercised in
both organisation and project flows, and the actions column now reserves
a consistent button footprint so every row keeps the same minimum
height.

| Before | After |
| --- | --- |
| <img width="1728" height="997" alt="Webhooks Settings Chisel Toolshed
Supabase-A4712323-6FD9-471B-B1F4-234B20686018"
src="https://github.com/user-attachments/assets/2745e5fd-1ef7-4f3a-872c-1fd8d10dce41"
/> | <img width="1728" height="997" alt="Webhooks Settings Chisel
Toolshed Supabase-D482241B-F324-4EA2-9B89-133AD5E10F17"
src="https://github.com/user-attachments/assets/5bfcefd8-cb58-46c0-a863-dcf80e4da4a3"
/> |

This keeps the view on the Data Table path now, while aligning the
sortable headers with the design-system table pattern instead of the
older Studio-local `DataTableColumnHeader` helper.

Connect iteration (#43949)

<img width="1670" height="1030" alt="image"
src="https://github.com/user-attachments/assets/fa2e4f9a-2287-402c-89cc-7f05d04f57e5"
/>

- Connect button becomes primary on new projects
- Switch from HoverCard to a "Copy" button on project url
- Adds "API Keys" item to the "Get Connected" section on project home

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: SaxonF <1072756+SaxonF@users.noreply.github.com>

Update standard-uploads.mdx (#44225)

Updated standard uploads documentation to include curl examples

fix: restore consent from ucData when GTM overwrites SDK storage (#44252)

After PR #43221 gated `TelemetryTagManager` behind consent, the EU
cookie consent banner started reappearing on every page load and when
navigating between apps (www, studio, docs).

Back in late February we changed `TelemetryTagManager` to only load when
the user has accepted consent. This was the right call for GDPR — don't
load tracking scripts before consent. But it created a chicken-and-egg
problem with how the Usercentrics SDK stores consent.

When a user clicks Accept, the SDK writes `uc_settings` +
`uc_user_interaction: true` to localStorage. Then the GTM script loads
(now that consent is granted), and its Usercentrics integration
immediately replaces those keys with a compressed `ucString` + `ucData`
format — deleting the originals.

On the next page load, `UC.init()` only knows how to read `uc_settings`.
It can't find it (GTM deleted it), so it treats the user as brand new
and shows the banner again. Before #43221, GTM loaded on every page
unconditionally, so its integration was already present during
`UC.init()` and could interpret the compressed format.

Confirmed via production console monitoring — the exact sequence after
clicking Accept:

```
setItem("uc_settings", ...)           // SDK writes consent
setItem("uc_user_interaction", "true") // SDK marks interaction
removeItem("uc_settings")             // GTM deletes SDK format
removeItem("uc_user_interaction")     // GTM deletes SDK format
setItem("ucString", ...)              // GTM writes compressed format
setItem("ucData", ...)                // GTM writes compressed format
```

- Read `ucData` from localStorage **before** `UC.init()` to detect prior
consent in the compressed format
- If the SDK wants to show the banner but `ucData` shows all services
were previously accepted, silently re-accept instead of re-prompting
- Added try/catch around the SDK initialization (was fire-and-forget
with no error handling, any failure was completely silent)
- Error fallback also honors prior `ucData` consent if the SDK fails to
initialize

Can't fully reproduce on staging previews because CSP blocks the GTM
script there (so the storage migration never fires). Verified the root
cause via production console monitoring with localStorage
monkey-patching, and confirmed the `ucData` format persists across page
loads on production.

Closes FE-2648

docs: update js sdk docs (2.100.1) (#44236)

Updates JS sdk documentation following stable release.
Ran `make` in apps/docs/spec to regenerate tsdoc files.

**Details:**
- **Version:** `2.100.1`
- **Source:** `supabase-js-stable-release`
- **Changes:** Regenerated tsdoc files from latest spec files

🤖 Auto-generated from @supabase/supabase-js stable release.

Co-authored-by: supabase-releaser[bot] <223506987+supabase-releaser[bot]@users.noreply.github.com>

docs(self-hosted): session token usage with s3 client (#43394)

fix(Docs): Migrate the rest of the raw.githubcontent calls to new util (#44274)

chore: updated copilot instructions (#44247)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Improve code review guidelines for copilot

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

chore: use `react-hook-form` in Storage custom expiry modal (#44245)

- Storage custom expiry modal still uses `formik` and we want to remove
it in favour of `react-hook-form` to keep only one form library

- Migrate to `react-hook-form`

Before:
<img width="443" height="235" alt="image"
src="https://github.com/user-attachments/assets/c9e10fa5-a618-4287-bc13-ca40ce41af10"
/>
<img width="438" height="335" alt="image"
src="https://github.com/user-attachments/assets/bf903eaf-0c31-4150-a17d-32a616dde7cc"
/>

After:
<img width="417" height="290" alt="image"
src="https://github.com/user-attachments/assets/be12f39e-0f1f-46cb-a442-82553b0a77e6"
/>
<img width="443" height="342" alt="image"
src="https://github.com/user-attachments/assets/4f537259-d3f6-4664-a1a1-f4a921c6a26c"
/>

refactor(pricing): remove A/B experiment logic and consolidate pricing components (#44205)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Removes the control variant and replaces it with the test variant as
winner.

-

-

Add any other context or screenshots.

ci: add release notes on update js libs pr (#44241)

Include release notes in the automated PR that updates supabase-js
across the repo.

* Looks at what version is installed
* It includes all changelogs between installed version and
to-be-installed version

chore(Docs): Bump radix dependency to avoid ref error (#44276)

fix: storage explorer column infinite loading loads the wrong column (#44275)

When scrolling down in a column that has more than 200 items to trigger
its lazy loading, while other columns are displayed and the targeted
column isn't the last one, it doesn't load its items.

Fixes #43799

- Don't rely on the storage state for opened folders for the lazy
loading
- Add the item path information to the column and use it to define the
API request path

- create a bucket
- create a folder and upload more than 200 items in it
- in this folder, create a sub folder and upload more than 200 items in
it (make sure its name position it _before_ the files)
- refresh the page
- without scrolling the first column, select the subfolder
- now scroll down on the first column past the last of its items and
verify it loads more items
- do the same on the sub folder column

To easily create many files to upload, use the following commands:

For root folder:
```sh
for i in {0..300}
do
    echo hello >"root_$(printf "%03d" "$i").txt"
done
```

For sub folder:
```sh
for i in {0..300}
do
    echo hello >"sub_$(printf "%03d" "$i").txt"
done
```

fix: detect prior consent via uc_user_interaction for fast cross-app navigation (#44284)

Follow-up to #44252. The previous fix handled the case where the user
waits on the page after accepting (GTM writes `ucData`), but there's a
second scenario: if the user accepts and navigates to another app
quickly (before GTM finishes), `ucData` hasn't been written yet. The SDK
on the new app overwrites `uc_user_interaction: "true"` with `"false"`
and shows the banner again.

`hasPreviousConsentInUcData` is now `detectPriorConsent` and checks two
signals before `UC.init()` overwrites them:

1. **Slow navigation** (existing): `ucData` contains all services with
`consent: true`
2. **Fast navigation** (new): `uc_user_interaction` is `"true"` — the
SDK on the previous app wrote this but GTM hasn't had time to replace it
yet

Also adds unit tests covering both scenarios, edge cases (empty
services, malformed JSON, mixed signals), and the combined behavior.

- 13 unit tests for `detectPriorConsent()` covering all localStorage
state combinations
- Can't fully reproduce on staging previews (CSP blocks GTM), verified
root cause via production console monitoring

Closes FE-2648

chore: use `react-hook-form` in logs query modals (#44243)

- Logs query modals still use `formik` and we want to remove it in
favour of `react-hook-form` to keep only one form library

- Migrate to `react-hook-form`

New query:
<img width="566" height="387" alt="image"
src="https://github.com/user-attachments/assets/f300a292-88f6-454e-8f86-84a27a8c4892"
/>

Existing query:
<img width="554" height="378" alt="image"
src="https://github.com/user-attachments/assets/2333f041-7185-47f6-8fb0-5176b5a0c77b"
/>

chore(studio): storage file explorer breadcrumbs (#43844)

UI update that resolves DEPR-114. Also resolves DEPR-113.

- The breadcrumbs on the file explorer have some rough edges in column
view
  - Fancy hide/show behavior
  - Hidden tap targets
- `FileExplorerHeader` actions can overflow on the x-axis
- The Navigate button is only shown on hover
- The inline Navigate flow does not work well on smaller screens

- Column view now shows the same in-explorer breadcrumb trail as list
view
- The active breadcrumb is visually emphasized, while inactive
breadcrumbs remain clickable
- The back affordance now uses a clearer arrow treatment with a stronger
separator from the breadcrumb trail
- The Navigate button is permanently visible and moved to the right-side
action group before Reload
- Navigate now opens a dialog on both desktop and mobile
- Added typed telemetry so we can measure `Navigate` usage before
deciding whether to keep or remove it
- Fixed header overflow by letting the full header contents scroll
horizontally together instead of visibly spilling out

| Before | After |
| --- | --- |
| <img width="947" height="997" alt="Buckets Storage AWS Healthy
Toolshed Supabase"
src="https://github.com/user-attachments/assets/fa53fdd4-954c-4832-bf9b-210b63ae020b"
/> | <img width="947" height="997" alt="Buckets Storage AWS Healthy
Toolshed Supabase"
src="https://github.com/user-attachments/assets/3689a0e5-97d1-4b36-a2dd-7adce23add5d"
/> |
| <img width="864" height="997" alt="Buckets Storage AWS Healthy
Toolshed Supabase"
src="https://github.com/user-attachments/assets/ad559118-205f-40e2-b3c5-97cef462d5f5"
/> | <img width="864" height="997" alt="Buckets Storage AWS Healthy
Toolshed Supabase"
src="https://github.com/user-attachments/assets/9c569b29-7c58-4a33-b809-34d6ed919008"
/> |

Also added a link to the `Buckets` portion of the `PageHeader`
breadcrumb:

```text
Files > Buckets > MyBucketName
```

It goes to the same place as Files because the root Files page lists
buckets, but having both links there feels more ergonomic in practice.

---------

Co-authored-by: Ali Waseem <waseema393@gmail.com>

Joshen/fe 2854 migrate queues integration to new UI (#44270)

Related to marketplace related work, just moves the Queues integration
to the new UI (Changes are feature flagged)
<img width="1145" height="584" alt="image"
src="https://github.com/user-attachments/assets/d3245889-597d-44e2-9850-f20907e42056"
/>

Installation is now in a side panel with the intention that it'll just
be a single click to install integrations that involve multiple parts
<img width="400" height="955" alt="image"
src="https://github.com/user-attachments/assets/71903b61-6bd2-486c-903e-b48ae2133887"
/>

- Verify that you can install the integration and everything else should
be status quo
- Verify that everything should be status quo if the flag is off

Bring Vault, Cron, Data API and GraphiQL integrations to the new UI (#44271)

Just brings more integrations over to the new UI bit by bit
- Vault
- Cron
- Data API
- GraphiQL

Will be tackling webhooks next which is a bit different as its not just
a database extension

feat(studio): add connection_string_copied tracking to ConnectSheet (#44327)

chore: Clean the `ui` package from `next` imports (#44278)

This PR moves several components which rely on `next` out of the `ui`
package to the `ui-patterns` package.

`ui-patterns` package is intented to be imported with specific imports
so it's ok if there are components reliant on `next` in there.

The `SonnerToaster` component has removed its dependency by requiring a
prop for `theme`.

refactor(ui-patterns): Standardise TanStack sort headers (#44212)

Component update.

TanStack tables in the repo are split between the shared `TableHeadSort`
primitive and the older Studio-local `DataTableColumnHeader` helper,
which makes the sorting UI and integration path inconsistent.

If you were to just use `DataTableColumnHeader` in `ui-patterns/Table`,
you’d get a very different visual result to the `TableHeadSort` UI you
see in most other tables.

Adds a shared `TanStackTableHeadSort` adapter in `ui-patterns/Table`,
backed by the existing `TableHeadSort` primitive, and switches the
webhook table plus the design-system TanStack demo to that canonical
path. `DataTableColumnHeader` stays as a deprecated wrapper for now,
Studio gets a lint guard to block new imports of it, and the table docs
now point TanStack tables at the shared adapter explicitly.

Check out column sorting on the Platform Webhook endpoint deliveries
table.

Migrate Database Webhook integrations to new integration sUI (#44277)

Related to marketplace integrations

Shifts Database Webhooks integration to the new Integrations UI. This
one's a bit different from the previous PRs as this involves a full SQL
installation query instead of only just a database extension. So am
tweaking `Integrations.constants` a little.

For context eventually all the integrations will be pulled remotely from
a database, so am still trying to figure out an optimal data structure,
but requirements will be clearer as we build out the UI

RE installing integrations:
- For now, if the integration has a provided SQL installation command,
that'll take precedence
- Else, if the integration has a provided SQL installation query, we'll
use that on the /query endpoint
- Otherwise, if the integration only requires database extensions,
dashboard will generate the queries to install the extensions
- In the case of the former tho, we won't allow users to choose which
schema to install the extension in too

Just ping me if any clarification's required!

- [ ] Verify that you can install the database webhooks with the new
integration UI
- [ ] Verify that behaviour is status quo without the new integration UI

fix: Set the default branch if it's not already set on Github integration (#44332)

Previously, the POST to `/v1/projects/{ref}/branches` didn't work on
free projects, which left some Supabase projects setup without a default
branch. Easy fix for those projects would be to disable/enable the whole
integration.

This PR fixes that so that when the user wants to setup a production
sync, the code checks if there's branches already setup and if not, sets
the selected branch as default.

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>

chore(studio): ship connect section, remove getting started and experiment plumbing (#44329)

The `connectSection` A/B experiment concluded as a true null (no effect
on activation or any downstream metric after 13 days at 50/50, ~153K
mature orgs). Saxon decided to ship the Connect section as the permanent
experience. This PR removes the Getting Started control variant, the old
Connect modal, all experiment flag gating, and related telemetry types.

- Delete `GettingStarted/` directory (5 files: section component, types,
utils, progress hook)
- Delete old `Connect.tsx` dialog modal (replaced by ConnectSheet)
- Remove `connectSection` PostHog flag reads from `Home.tsx` and
`LayoutHeader.tsx`
- Remove `getSectionVisibility()` experiment logic and
`ConnectSectionVariant` type
- Remove `getting-started` from `DEFAULT_SECTION_ORDER`
- Always render `<ConnectSheet />` in header (no more conditional with
old `<Connect />` modal)
- Remove `variant` prop from `ConnectSection` component
- Remove 4 getting-started telemetry event interfaces from
`telemetry-constants.ts`
- Update `mergeSectionOrder` tests to reflect new section order

Tested on Vercel preview:
- [x] Project homepage shows Connect section for new projects (< 10 days
old)
- [x] Connect section hidden for mature projects (> 10 days old)
- [x] Header Connect button opens ConnectSheet (not old modal)
- [x] Connect tiles open ConnectSheet with correct tab
- [x] Section drag-and-drop still works without getting-started in the
order
- [x] Existing users with `getting-started` in localStorage order don't
break (mergeSectionOrder strips it)

- fixes GROWTH-730

---------

Co-authored-by: Alaister Young <alaister@users.noreply.github.com>

docs: update setAll callbacks to accept cache headers second argument (#44240)

Updates all `setAll` cookie handler implementations across docs and
examples to accept the new `headers` second argument introduced in
`@supabase/ssr` v0.10.0
([supabase/ssr#176](https://github.com/supabase/ssr/pull/176)).

`@supabase/ssr` v0.10.0 introduced a breaking change: `setAll` now
receives a required second argument `headers: Record<string, string>`
alongside the cookies array. When a token refresh occurs, the library
passes cache headers (`Cache-Control`, `Expires`, `Pragma`) that must be
applied to the HTTP response to prevent CDN caching of auth responses.

Because TypeScript allows functions with fewer parameters to satisfy a
type expecting more, existing `setAll` implementations do not produce a
type error when the second argument is omitted. Users who copy an
outdated snippet will silently miss the CDN protection.

Root cause and context:
[supabase/supabase-js#1682](https://github.com/supabase/supabase-js/issues/1682)

**Proxy/middleware contexts** (where token refreshes happen) now apply
the cache headers to their response:
- Next.js proxy files: `supabaseResponse.headers.set(key, value)`
- SvelteKit hooks: `event.setHeaders(headers)`
- Hono middleware: `c.header(key, value)`
- Pages Router (Express-style): `ctx.res.setHeader(key, value)`
- Remix/React Router loaders and actions: applied to response headers
(outer `headers` variable renamed to `responseHeaders` to avoid naming
conflict with the new param)

**Server Component and API route contexts** (no response object
available) accept `_headers` without applying them.

- `apps/docs/content/guides/auth/server-side/creating-a-client.mdx`
(inline Astro, Remix, React Router, Express snippets)
- `apps/docs/content/_partials/oauth_pkce_flow.mdx`
- `apps/docs/content/guides/auth/oauth-server/getting-started.mdx`
- `apps/docs/content/guides/auth/passwords.mdx`
-
`apps/docs/content/troubleshooting/how-to-migrate-from-supabase-auth-helpers-to-ssr-package-5NRunM.mdx`
- `examples/auth/nextjs/`, `examples/auth/nextjs-full/` (proxy + server)
- `examples/auth/sveltekit/`, `examples/auth/sveltekit-full/`
- `examples/auth/hono/`, `examples/auth/hono-full/`
- `examples/user-management/nextjs-user-management/` (proxy + server)
- `examples/user-management/sveltekit-user-management/`
- `examples/realtime/nextjs-authorization-demo/` (proxy + server)
- `examples/realtime/nextjs-auth-presence/` (pages router)
- `examples/prompts/nextjs-supabase-auth.md`

fix: preview bool (#44339)

fixes boolean values rendering as blank in the referencing record
preview popover

| Before | After |
| --- | --- |
| <img width="348" height="114" alt="Before"
src="https://github.com/user-attachments/assets/de0b6399-996c-416c-921a-f251239435aa"
/> | <img width="385" height="137" alt="After"
src="https://github.com/user-attachments/assets/c0a14b8f-19ca-4052-baac-e7a2a1fa8246"
/> |

- closes https://github.com/supabase/supabase/issues/44333
- ig was introduced in https://github.com/supabase/supabase/pull/44144

feat: update @supabase/*-js libraries to v2.100.1 (#44235)

This PR updates @supabase/*-js libraries to version 2.100.1.

**Source**: supabase-js-stable-release

**Changes**:
- Updated @supabase/supabase-js to 2.100.1
- Updated @supabase/auth-js to 2.100.1
- Updated @supabase/realtime-js to 2.100.1
- Updated @supabase/postgest-js to 2.100.1
- Refreshed pnpm-lock.yaml

This PR was created automatically.

Edit by @mandarini:
Fix type error in `lib/events.ts` caused by `supabase-js` `v2.100.1`
adding strict column name validation to `.eq()`. Updated the `meetups`
table definition in `database.types.ts` to include missing columns
(`is_published`, `country`, `start_at`, `launch_week`).

---------

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
Co-authored-by: Katerina Skroumpelou <sk.katherine@gmail.com>

[bot] Decrease ESLint ratchet baselines (#44311)

Automated weekly decrease of ESLint ratchet baselines.

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

fix auth providers string fields are all considered sensitive (#44345)

All Auth Providers string fields are considered sensitive

Restore check of the `sensitive` property that was mistakenly removed in

<img width="815" height="949" alt="image"
src="https://github.com/user-attachments/assets/5eab2db9-7023-4351-ab64-f09f5b54d1b8"
/>

fix: browser back navigation on observability page (#43807)

The observability pages has a browser navigation issue where pressing
the back button would get stuck in a redirect loop between
`/observability` and `/observability/api-overview`, making it difficult
to navigate away using browser controls

<details>
<summary>vid:</summary>

https://github.com/user-attachments/assets/4b48651b-143d-4857-831b-67d1b98e237d

</details>

Changed automatic redirects from `router.push()` to `router.replace()`
in the routing logic.

Same pattern as
https://github.com/supabase/supabase/commit/f8419ef9ae7c43ea033617e5544c565dfaaf87acc

feat: update @supabase/*-js libraries to v2.101.0 (#44347)

This PR updates @supabase/*-js libraries to version 2.101.0.

**Source**: supabase-js-stable-release

**Changes**:
- Updated @supabase/supabase-js to 2.101.0
- Updated @supabase/auth-js to 2.101.0
- Updated @supabase/realtime-js to 2.101.0
- Updated @supabase/postgest-js to 2.101.0
- Refreshed pnpm-lock.yaml

---

- **realtime:** add `copyBindings` functionality
([#2197](https://github.com/supabase/supabase-js/pull/2197))
- **realtime:** block setting `postgres_changes` event listener after
joining ([#2201](https://github.com/supabase/supabase-js/pull/2201))

- Dominik Pilipczuk @snickerdoodle2

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>

feat: promote new banner endpoint to prod (#44346)

New incident banner workflow should now be the standard in prod as well
as preview.

chore(studio): migrate cursor rules to claude skills + add CLAUDE.md (#44343)

Migrates all studio-related Cursor rules to Claude skills and adds a
top-level `.claude/CLAUDE.md` for project context. Docs rules left in
place.

**Decisions:**
- Only studio + testing rules migrated — docs rules intentionally left
in `.cursor/rules/docs/`
- Vitest skill already shared via symlink (`.claude/skills/vitest` →
`.agents/skills/vitest`) — nothing to migrate
- Grouped ~21 granular cursor rules into 5 new skills + 1 updated skill
by topic
- `studio-architecture` skill fully merged into `CLAUDE.md` and deleted
to avoid overlap
- Skills are self-contained (content inlined, not relying on sub-files)
since Claude reads SKILL.md first
- Skills cross-reference each other inline where relevant (e.g.
best-practices → testing, error-handling, queries)
- No `paths` frontmatter — would auto-inject full skill content on every
matching file. Current description-based matching is more selective and
token-efficient.

**Removed:**
- `.cursor/rules/studio/` (21 rule files covering architecture, best
practices, UI patterns, queries, styling, etc.)
- `.cursor/rules/testing/` (e2e-studio + unit-integration rules)
- `.cursor/rules/studio-useStaticEffectEvent.mdc`
- `.claude/skills/studio-architecture/` — fully merged into CLAUDE.md to
avoid duplication
- `.claude/skills/studio-testing/rules/` — orphaned sub-files after
inlining content into SKILL.md

**Added:**
- `.claude/CLAUDE.md` — concise monorepo overview with structure,
commands, and conventions. Absorbs studio-architecture content.
References `studio-*` skills for detail.
- `.claude/skills/studio-best-practices/` — boolean naming, component
structure, loading/error/success patterns, state management, hooks,
TypeScript conventions. Cross-references `vercel-composition-patterns`,
`studio-ui-patterns`, `studio-queries`, `studio-error-handling`, and
`studio-testing` inline where relevant.
- `.claude/skills/studio-ui-patterns/` — layout, forms, tables, charts,
empty states, navigation, cards, alerts, sheets. Grouped from ~10
separate cursor rules into one cohesive skill.
- `.claude/skills/studio-queries/` — React Query `queryOptions` pattern,
`keys.ts` structure, mutation hook template, imperative fetching.
- `.claude/skills/use-static-effect-event/` — the `useStaticEffectEvent`
hook: when to use, when not to, patterns, implementation.

**Changed:**
- `.claude/skills/studio-e2e-tests/` — renamed from `e2e-studio-tests`
for `studio-*` naming consistency. Merged race condition, waiting
strategy, test structure, assertion, and cleanup patterns from the
cursor e2e rule.
- `.claude/skills/studio-testing/` — inlined key content from sub-rule
files directly into SKILL.md so it's self-contained. Removed broken
`AGENTS.md` reference. Deleted orphaned `rules/` sub-files.
- `.claude/skills/vercel-composition-patterns/` — added note that Studio
uses React 18, so React 19 patterns should be skipped.
- `.gitignore` — added `!.claude/CLAUDE.md` exception so it's tracked.

- Open Claude Code in the repo, verify `.claude/CLAUDE.md` loads as
project context
- Ask Claude about Studio conventions and verify it references the right
skills
- Check that `studio-*` skills appear in the skill list

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Refactor table editor logic for handling null and undefined values (#44331)

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

There's currently an issue with the Table Editor where if you have, for
example, a nullable `text` column with a default value, inserting a new
row and selecting "Set to NULL" doesn't do anything, and saving will
insert the row with the default value
<img width="700" height="258" alt="image"
src="https://github.com/user-attachments/assets/6a284ebb-c346-40a6-9a30-793118844084"
/>

This stems from a legacy logic in the Table Editor whereby we treat
`null` values as "no input" - which is incorrect as `null` values are
also valid values. So the PR here changes a few things to resolve this
properly:

Main fix:
- `undefined` will be the "no input" value instead, and it'll be the
default value when generating the row object for inserting a new row
- `NULL` or even empty string like `''` will be treated as they are
(valid inputs)

Secondary adjustments:
- (Queue operations) Queueing an insert with no value but default value
is NULL, will show the placeholder as `DEFAULT` instead of `NULL` for
better accuracy in representation
<img width="892" height="96" alt="image"
src="https://github.com/user-attachments/assets/02cf86bf-c17b-4e25-9a8f-17960b1d2575"
/>
- Added a `Set to Default` CTA here, but will only show up if adding a
new row or updating a queued insert row operation, which will set the
value of the input field back to `undefined` for PG to handle it as the
default value
<img width="734" height="208" alt="image"
src="https://github.com/user-attachments/assets/23887c0c-533e-4494-acbe-61309ff5d7c5"
/>

Verify within the Table Editor (along with queue operation feature
preview)
- For inserting a new row, setting value to NULL and setting value to
Default works
- For updating a row, setting value to NULL works

feat(studio): clarify public bucket access message in dashboard (#44287)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Adds tooltip and a small modification to the `public` badge on on public
buckets.

docs: update js sdk docs (2.101.0) (#44348)

Updates JS sdk documentation following stable release.
Ran `make` in apps/docs/spec to regenerate tsdoc files.

**Details:**
- **Version:** `2.101.0`
- **Source:** `supabase-js-stable-release`
- **Changes:** Regenerated tsdoc files from latest spec files

🤖 Auto-generated from @supabase/supabase-js stable release.

Co-authored-by: supabase-releaser[bot] <223506987+supabase-releaser[bot]@users.noreply.github.com>

chore: update supabase wordmark in Nav.tsx (#44306)

Previously, blurry PNGs images were used as logos. Updated it to use a
single svg instead of two png versions.

| Before | After |
|--------|--------|
| <img width="1463" height="652" alt="Screenshot 2026-03-28 at 12 23 13"
src="https://github.com/user-attachments/assets/d816bd16-09f3-4bc9-8dbb-d1e5839531d7"
/> | <img width="1317" height="547" alt="Screenshot 2026-03-28 at 12 22
53"
src="https://github.com/user-attachments/assets/e83c9e4e-fad0-4225-9493-c755f8bc5e6d"
/> |

/go pages: adding meeting scheduler widget (#43999)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

<img width="624" height="924" alt="Screenshot 2026-03-19 at 15 21 31"
src="https://github.com/user-attachments/assets/4f63f62c-169f-443c-b92c-7b4208bb111c"
/>

feat(growth): in-memory first-touch attribution store (#43570)

Introduces two new files in `packages/common`:

- **`telemetry-first-touch-store.ts`** — a module-scoped singleton that
holds first-touch attribution data (referrer, UTM params, page URL) in
memory. Writes once on first load, cleared after the initial pageview
event fires or on opt-out. No device storage involved.
- **`useFirstTouchStore.tsx`** — a React hook that captures attribution
data on initial page load and writes it into the store, gated on the
`enabled` flag so it only runs where consent has been handled.

Trade-off: data is lost on a hard reload before consent is granted —
accepted edge case per GROWTH-656.

Follows the same module-scope pattern already used by `posthogClient`
and `consentState`.

- Verify first-touch data is captured on initial load and readable by
`PageTelemetry` after consent
- Verify no cookie is set before consent
- Verify data is cleared after initial pageview fires

GROWTH-656

Add Jared Patterson to humans.txt (#44367)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Update - Adding myself to the Supabase team

Added a meeting scheduler form (#44368)

chore(docs): add Samir Ketema to humans.txt (#44374)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Adding myself to humans.txt as I joined the team 🎉

chore: add shadcn input-group components (#44282)

On a number input with units:
<img width="660" height="162" alt="image"
src="https://github.com/user-attachments/assets/1758a6d9-0836-4d41-80d1-97a03292db91"
/>

focused state:
<img width="651" height="71" alt="image"
src="https://github.com/user-attachments/assets/a92a5c39-2c7e-4c5f-9e4b-eb89810cc45c"
/>

On a textarea:
<img width="989" height="294" alt="image"
src="https://github.com/user-attachments/assets/cc696cb9-3671-4719-bdd8-daa1aea4f041"
/>

Fixes Typo in Spend Cap Modal (#44319)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Fixes typo in Dashboard

In the [Billing
Dashboard](https://supabase.com/dashboard/org/_/billing), when one
updates the spend cap:
<img width="2376" height="502" alt="CleanShot 2026-03-29 at 14 52 01@2x"
src="https://github.com/user-attachments/assets/5169fc01-cf78-4450-9184-7d7d6a28b000"
/>

The pop-out modal has a typo (states "includedthen" instead of
"included, then"):
<img width="1182" height="1408" alt="CleanShot 2026-03-29 at 14 52
17@2x"
src="https://github.com/user-attachments/assets/53622f29-cc02-4138-aa83-4e098d31d1a2"
/>

Updated the line:
```jsx
<p className="text-xs pl-4">{item.plans['pro']}</p>
```

to
```jsx
<p className="text-xs pl-4">
  {Array.isArray(item.plans['pro'])
    ? item.plans['pro']?.join(', ')
    : item.plans['pro']}
</p>
```

Fixed the issue:
<img width="1302" height="1072" alt="CleanShot 2026-03-29 at 15 27
30@2x"
src="https://github.com/user-attachments/assets/722056c8-7b62-4993-83bb-a2ed4cbc5264"
/>

[A user reported the
error](https://supabase.frontapp.com/open/msg_32qr4hpa?key=RjpHZRymA4HTNLMFa-PQ6l_Ne7L66x8A)

The file containing pricing data
"[packages/shared-data/pricing.ts](https://github.com/supabase/supabase/blob/7d7de475ecf31f95d37199456482b665c80aadc8/packages/shared-data/pricing.ts#L125)"
has the following JS object:
```js
// example problematic entry
plans: {
    //rest of object
    pro: ['8 GB disk size per project included', 'then $0.125 per GB'],
    //rest of object
},
...
// some other entry
plans: {
    //rest of object
    pro: false,
    //rest of object
}
...
```

The relevant content in the Dashboard is an array with two entries:
> ['8 GB disk size per project included', 'then $0.125 per GB']

The [modal
file](https://github.com/supabase/supabase/blob/7d7de475ecf31f95d37199456482b665c80aadc8/apps/studio/components/interfaces/Organization/BillingSettings/CostControl/SpendCapSidePanel.tsx#L174)
deconstructs it within the following element:
```jsx
{usageItems.map((item: any) => {
  return (
    <Table.tr key={item.title}>
      <Table.td>
        <p className="text-xs pl-4">{item.title}</p>
      </Table.td>
      <Table.td>
        <p className="text-xs pl-4">{item.plans['pro']}</p> <!--------RELEVANT LINE-->
      </Table.td>
    </Table.tr>
  )
})}
```

It doesn't bother to separate entries in the array, so the element just
concatenates the two entries without any spacing.

---------

Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>

Joshen/fe 2899 migrate wrappers integration to new UI (#44349)

Related to marketplace integrations, now shifting wrappers over to the
new UI (feature flagged)

- Added a "Installed" indicator if the integration is installed
<img width="1155" height="171" alt="image"
src="https://github.com/user-attachments/assets/6de2ea49-64bb-46af-ba04-db6629ec7546"
/>
- New UI will only show the "Add new wrapper" + "Recent wrappers" table
only if the required extensions have been installed (Current UI shows
them both irregardless)
<img width="1147" height="518" alt="image"
src="https://github.com/user-attachments/assets/c428ff70-4a52-401e-a369-6948100d1cef"
/>
- Once required extensions are installed, the wrappers tab will also
show up
  - Currently the wrappers tab only shows up after a wrapper is created
- This change will affect the existing behaviour too (reckon it makes
more sense this UX)
<img width="1143" height="611" alt="image"
src="https://github.com/user-attachments/assets/b05f5196-854e-41c1-8a01-7a5e5cca9d4e"
/>
- In the install integration sheet, only extensions that have yet to be
installed will users be allowed to adjust schema selection for
<img width="553" height="583" alt="image"
src="https://github.com/user-attachments/assets/045e185a-6235-4dc5-a984-b039b64cd7fd"
/>
- Likewise, if the extension has already been installed, it will be
omitted from the SQL code output
<img width="575" height="267" alt="image"
src="https://github.com/user-attachments/assets/5e6c0a75-5738-4a0d-9e33-ff19aed074d3"
/>
- Updated docs URL for some of the integrations (vault, database
webhooks, graphql)
- Nit: This one's bugged me for the longest time but remove extra border
bottom in "Recent wrappers" table
<img width="1068" height="177" alt="image"
src="https://github.com/user-attachments/assets/1d6e24cf-072a-4605-8eca-ee0f37406d74"
/>

FWIW i think the integrations UI for wrappers is due for an update, but
we'll leave things as they are for now - at least until we're bit more
clearer on the requirements for marketplace integrations (less we make
changes now which then have to be redone again later)

- [ ] Verify that you can install + create wrappers still with the new
UI
- [ ] Verify that behaviour is status quo with flag off (except the ones
mentioned explicitly)

chore: Clean up the flag for enabling Github integration on free plan (#44352)

fix(studio): fix infinite pagination loop in queue messages (#44292)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Bug fix

The `getNextPageParam` function in
`database-queue-messages-infinite-query.ts` uses `<=` instead of `>=`,
which means pagination never stops. Since the result length is always
less than or equal to the page size, `hasNextPage` is always `true`,
causing infinite API requests when viewing queue messages in Studio.

Resolves #44291

Uses `>=` to correctly detect when there are more pages, consistent with
every other infinite query in the codebase:

- `database-cron-jobs-infinite-query.ts` uses `>=`
- `users-infinite-query.ts` uses `>=`
- `database-cron-jobs-runs-infinite-query.ts` uses `< PAGE_SIZE` to
return `undefined` (equivalent logic)

One character change: `<=` to `>=` on line 112.

resolve merge slop

Fix create s3 access keys (#44388)

Resolves FE-2923

There's an issue with creating s3 keys in the storage settings at the
moment, where naming your key throws a form field validation error. This
PR fixes that - not sure when this was introduced though, but the main
fix was just using `field` from `render` instead of calling `register`

- [ ] Verify that you can create an s3 access key via the storage
settings

chore(studio): remove privacy policy update notification banner (#44380)

Remove the privacy policy update notification banner that was added for
the March 2026 policy update. The effective date has passed and users
have had sufficient notice.

- Remove `PrivacyUpdateBanner` component from `AnalyticsSettings.tsx`
and its unused imports
- Remove banner usage from the organizations list page and org projects
page
- Remove `PRIVACY_NOTICE_ACKNOWLEDGED` localStorage key

No setup needed - this is a removal of UI elements.

Tested on Vercel preview:
- [x] Organizations page loads without the banner
- [x] Org projects page loads without the banner
- [ ] No console errors on either page
- [x] Analytics settings page still renders correctly

- fixes GROWTH-692

fix inputs width and spacing (#44387)

layout such as search inputs on the project list page

Fix padding and spacing

fix: Dont fetch databases and migrations for coming up projects (#44383)

Those queries will always error out anyway as there's no data in place
yet.

enforce public bucket

chore: migrate vault new secret form to react-hook-form (#44389)

- The vault new secret form still uses `formik` and we want to remove it
in favour of `react-hook-form` to keep only one form library
- The vault new secret form does not follow the design system guidelines
(`Modal` instead of `Dialog`)

- Migrate to `react-hook-form`
- Apply the design system guidelines

Before:
<img width="539" height="396" alt="image"
src="https://github.com/user-attachments/assets/c1beda65-9bad-4ff6-9f5c-64b2bea615e5"
/>

After:
<img width="452" height="384" alt="image"
src="https://github.com/user-attachments/assets/86bfff23-924c-4e1d-9b28-66e131a5db1d"
/>

fix: invalid react-hook-form usage (#44391)

Some forms register their inputs incorrectly by calling
`control.register` and spreading the `field`.
This makes the form unusable.

Design of the new analytics buckets table

Before:
<img width="554" height="564" alt="image"
src="https://github.com/user-attachments/assets/6ddf7ead-f576-44c4-b184-8102a52a22e2"
/>

After:
<img width="395" height="791" alt="image"
src="https://github.com/user-attachments/assets/ee85fcdd-b2a5-410d-9296-8bd6f1aa0b2f"
/>

allow empty string in saml metadata url (#44392)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Bug fix

SAML metadata URL cannot be saved with an empty string; it works through
the management API

<img width="781" height="412" alt="image"
src="https://github.com/user-attachments/assets/586d3de2-30bc-4e8a-9d4b-b039685cb455"
/>

Allow saving SAML 2.0 config with an empty SAML metadata URL

fix(studio): add option to treat empty CSV cells as NULL on import (#43281)

When importing a CSV file, empty cells were always imported as empty
strings with no way to import NULL values instead. This adds a "Treat
empty cells as NULL" checkbox to the import configuration panel.

When enabled, PapaParse's transform option converts empty strings to
null before the data is parsed and previewed, so the imported rows
correctly contain NULL rather than empty strings.

Fixes #43258.

Bug fix

Empty cells in a CSV file are always imported as empty strings with no
way to import NULL values instead. Fixes #43258.

"Treat empty cells as NULL" checkbox is added to the import
configuration panel. When enabled, empty cells are imported as NULL
instead of empty strings. Toggling the checkbox instantly re-parses the
preview.

The fix uses PapaParse's transform option to convert empty strings to
null before parsing. Applies to both file uploads and pasted text.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com>

feat: pg_partman support for Queues through dashboard (#44359)

[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

New UI to help users create partitioned queues when supported with
pg_partman. Updated the existing UI from 2024 to be a bit more user
friendly

**Extension management page**
<img width="1575" height="155" alt="image"
src="https://github.com/user-attachments/assets/4b1895cf-4555-40c5-8a11-54208748b169"
/>

**pg partman call out in queues**
<img width="664" height="771" alt="image"
src="https://github.com/user-attachments/assets/92feff48-72bb-4816-b0aa-e24e70fa148e"
/>

**Updated recommended section with sane defaults**

<img width="663" height="918" alt="image"
src="https://github.com/user-attachments/assets/716d9411-f708-4b4d-8027-7ca7a41062c8"
/>

**Warning on disabling extension**
<img width="431" height="392" alt="image"
src="https://github.com/user-attachments/assets/129ab1eb-2bcc-49ca-a20c-72422460c60e"
/>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

fix(observability): guard pg_stat_statements queries against missing extension FE-2843 (#44357)

On self-hosted Supabase instances where the `pg_stat_statements`
extension is not installed, the Observability Overview page
automatically queries the extension on every page load. This produces
"relation pg_stat_statements does not exist" errors in Postgres logs for
all projects without the extension. Additionally, if a user navigated to
the Query Performance page, they received a generic error with no
actionable guidance. A secondary issue allowed malformed sort URL params
(e.g. `?sort=created_at:asc&order=asc`) to be interpolated directly into
SQL ORDER BY clauses.

- Wrapped the `useSlowQueriesCount` SQL in a `CASE WHEN EXISTS (SELECT 1
FROM pg_extension WHERE extname = 'pg_stat_statements')` guard. The
query now returns 0 silently instead of erroring when the extension is
absent.
- Added a `VALID_SORT_COLUMNS` whitelist in
`generateQueryPerformanceSql`. Invalid column names from URL params are
rejected and the query falls back to the preset default ORDER BY.
- When the Query Performance page fails because `pg_stat_statements`
does not exist, a `warning` admonition now appears with "Enable it in
Database -> Extensions" guidance instead of a generic destructive error.
The Sentry capture is skipped for this expected configuration state.
- Extracted `buildSlowQueriesCountSql` as a testable function and added
unit tests for both fixes.

**Extension not installed (self-hosted):**
1. Run a self-hosted Supabase instance without the `pg_stat_statements`
extension enabled.
2. Navigate to the Observability Overview page.
3. Check Postgres logs -- no "relation pg_stat_statements does not
exist" errors should appear.
4. Navigate to the Query Performance page.
5. Expected: a yellow warning admonition appears saying the extension is
not enabled, with a link to Database -> Extensions. No red error.

**Extension installed (normal flow):**
1. With `pg_stat_statements` installed, navigate to Observability
Overview.
2. Expected: slow queries count loads as normal.
3. Navigate to Query Performance -- data loads as normal.

**Invalid sort URL param:**
1. Navigate to
`/project/<ref>/observability/query-performance?sort=created_at:asc&order=asc…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants