diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx index fa84e200b06..328e91d112a 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx @@ -58,6 +58,7 @@ const FunctionLink = memo(function FunctionLink({ */ onClick={(e) => { e.preventDefault() + menuState.setMenuActiveRefId(id) history.pushState({}, '', url) const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches document.getElementById(slug)?.scrollIntoView({ diff --git a/apps/docs/content/guides/api/rest/client-libs.mdx b/apps/docs/content/guides/api/rest/client-libs.mdx index a40cc247fb6..49a5b0c1fbc 100644 --- a/apps/docs/content/guides/api/rest/client-libs.mdx +++ b/apps/docs/content/guides/api/rest/client-libs.mdx @@ -18,6 +18,8 @@ Supabase provides client libraries for the REST and Realtime APIs. Some librarie ## Community libraries +{/* supa-mdx-lint-disable Rule003Spelling */} + | `Language` | `Source Code` | `Documentation` | | ----------------------- | -------------------------------------------------------------------------------- | ------------------------------------------- | | C# | [supabase-csharp](https://github.com/supabase-community/supabase-csharp) | [Docs](/docs/reference/csharp/introduction) | @@ -25,3 +27,4 @@ Supabase provides client libraries for the REST and Realtime APIs. Some librarie | Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | [Docs](/docs/reference/kotlin/introduction) | | Ruby | [supabase-rb](https://github.com/supabase-community/supabase-rb) | | | Godot Engine (GDScript) | [supabase-gdscript](https://github.com/supabase-community/godot-engine.supabase) | | +| Elixir | [supabase-elixir](https://github.com/supabase-community/supabase-ex) | | diff --git a/apps/docs/content/guides/auth/quickstarts/react-native.mdx b/apps/docs/content/guides/auth/quickstarts/react-native.mdx index 4dd3230e4cb..2d4469b2c97 100644 --- a/apps/docs/content/guides/auth/quickstarts/react-native.mdx +++ b/apps/docs/content/guides/auth/quickstarts/react-native.mdx @@ -66,7 +66,7 @@ hideToc: true Create a helper file `lib/supabase.ts` that exports a Supabase client using your Project URL and key. - Create a `.env` file and populate with your Supabase connection variables: + Rename `.env.example` to `.env` and populate with your Supabase connection variables: @@ -90,7 +90,7 @@ hideToc: true - Let's set up a React Native component to manage logins and sign ups. + Create a React Native component to manage logins and sign ups. The app later uses the [`getClaims`](/docs/reference/javascript/auth-getclaims) method in `App.tsx` to validate the local JWT before showing the signed-in user. diff --git a/apps/docs/content/guides/auth/quickstarts/react.mdx b/apps/docs/content/guides/auth/quickstarts/react.mdx index c506e6ff282..b3a47630ada 100644 --- a/apps/docs/content/guides/auth/quickstarts/react.mdx +++ b/apps/docs/content/guides/auth/quickstarts/react.mdx @@ -74,10 +74,11 @@ hideToc: true - ```text name=.env.local - VITE_SUPABASE_URL=your-project-url - VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY=sb_publishable_... or anon key - ``` + <$CodeSample + path="/auth/quickstarts/react/.env.example" + lines={[[1, -1]]} + meta="name=.env.local" + /> <$Partial path="api_settings_steps.mdx" variables={{ "framework": "react", "tab": "frameworks" }} /> @@ -90,166 +91,16 @@ hideToc: true In `App.jsx`, create a Supabase client using your Project URL and key. - You can configure the Auth component to display whenever there is no session inside `supabase.auth.getSession()` + The code uses the [`getClaims`](/docs/reference/javascript/auth-getclaims) method in `App.jsx` to validate the local JWT before showing the signed-in user. - ```jsx name=src/App.jsx - import "./index.css"; - import { useState, useEffect } from "react"; - import { createClient } from "@supabase/supabase-js"; - - const supabase = createClient(import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY); - - export default function App() { - const [loading, setLoading] = useState(false); - const [email, setEmail] = useState(""); - const [session, setSession] = useState(null); - - // Check URL params on initial render - const params = new URLSearchParams(window.location.search); - const hasTokenHash = params.get("token_hash"); - - const [verifying, setVerifying] = useState(!!hasTokenHash); - const [authError, setAuthError] = useState(null); - const [authSuccess, setAuthSuccess] = useState(false); - - useEffect(() => { - // Check if we have token_hash in URL (magic link callback) - const params = new URLSearchParams(window.location.search); - const token_hash = params.get("token_hash"); - const type = params.get("type"); - - if (token_hash) { - // Verify the OTP token - supabase.auth.verifyOtp({ - token_hash, - type: type || "email", - }).then(({ error }) => { - if (error) { - setAuthError(error.message); - } else { - setAuthSuccess(true); - // Clear URL params - window.history.replaceState({}, document.title, "/"); - } - setVerifying(false); - }); - } - - // Check for existing session - supabase.auth.getSession().then(({ data: { session } }) => { - setSession(session); - }); - - // Listen for auth changes - const { - data: { subscription }, - } = supabase.auth.onAuthStateChange((_event, session) => { - setSession(session); - }); - - return () => subscription.unsubscribe(); - }, []); - - const handleLogin = async (event) => { - event.preventDefault(); - setLoading(true); - const { error } = await supabase.auth.signInWithOtp({ - email, - options: { - emailRedirectTo: window.location.origin, - } - }); - if (error) { - alert(error.error_description || error.message); - } else { - alert("Check your email for the login link!"); - } - setLoading(false); - }; - - const handleLogout = async () => { - await supabase.auth.signOut(); - setSession(null); - }; - - // Show verification state - if (verifying) { - return ( -
-

Authentication

-

Confirming your magic link...

-

Loading...

-
- ); - } - - // Show auth error - if (authError) { - return ( -
-

Authentication

-

✗ Authentication failed

-

{authError}

- -
- ); - } - - // Show auth success (briefly before session loads) - if (authSuccess && !session) { - return ( -
-

Authentication

-

✓ Authentication successful!

-

Loading your account...

-
- ); - } - - // If user is logged in, show welcome screen - if (session) { - return ( -
-

Welcome!

-

You are logged in as: {session.user.email}

- -
- ); - } - - // Show login form - return ( -
-

Supabase + React

-

Sign in via magic link with your email below

-
- setEmail(e.target.value)} - /> - -
-
- ); - } - ``` + <$CodeSample + path="/auth/quickstarts/react/src/App.jsx" + lines={[[1, -1]]} + meta="name=src/App.jsx" + />
diff --git a/apps/docs/content/guides/deployment/branching.mdx b/apps/docs/content/guides/deployment/branching.mdx index 74782ceb455..ab6c6dff990 100644 --- a/apps/docs/content/guides/deployment/branching.mdx +++ b/apps/docs/content/guides/deployment/branching.mdx @@ -10,8 +10,8 @@ Supabase branches create separate environments that spin off from your main proj ## How branching works - **Separate Environments**: Each branch is a separate environment with its own Supabase instance and API credentials. -- **Preview Branches**: You can create multiple Preview Branches for testing. -- **Persistent Branches**: Persistent branches are long-lived branches. They aren't automatically paused or deleted due to non-inactivity or merging. +- **Preview Branches**: Preview branches are ephemeral and best suited for focused testing. They are automatically paused after inactivity or deleted when a PR is merged or closed. +- **Persistent Branches**: Persistent branches are long-lived and recommended for environments like staging, QA, or development. Unlike preview branches, they aren't automatically paused or deleted due to inactivity or when a PR is merged or closed. - **Managing Branches**: You can create, review, and merge branches either automatically via our [GitHub integration](/docs/guides/deployment/branching/github-integration) or directly [through the dashboard](/docs/guides/deployment/branching/dashboard) (currently in beta). All branches show up in the branches page in the dashboard, regardless of how they were created. - **Data-less**: New branches do not start with any data from your main project. This is meant to better protect your sensitive production data. To start your branches with data, you can use a [seed file](/docs/guides/deployment/branching/github-integration#seeding) if using the GitHub integration. diff --git a/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx b/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx index 0735cb1801d..5bfa9c9faf3 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx @@ -46,7 +46,9 @@ lines={[[1, -1]]} meta="name=src/environments/environment.ts" /> -Now you have the API credentials in place, create a `SupabaseService` with `ng g s supabase` and add the following code to initialize the Supabase client and implement functions to communicate with the Supabase API. +With the API credentials in place, create a `SupabaseService` with `ng g s supabase` and add the following code to initialize the Supabase client and implement functions to communicate with the Supabase API. + +This uses the [`getUser`](/docs/reference/javascript/auth-getuser) method to get the current user details if there is an existing session. This method performs a network request to the Supabase Auth server. <$CodeSample path="/user-management/angular-user-management/src/app/supabase.service.ts" diff --git a/apps/docs/content/guides/platform/compute-and-disk.mdx b/apps/docs/content/guides/platform/compute-and-disk.mdx index e6b15b8e3ee..f9bb168413a 100644 --- a/apps/docs/content/guides/platform/compute-and-disk.mdx +++ b/apps/docs/content/guides/platform/compute-and-disk.mdx @@ -173,5 +173,5 @@ As mentioned in the Postgres [documentation](https://postgresqlco.nf/doc/en/para ### Constraints -- You can modify disk attributes up to **four times** within a rolling 24-hour window. A new modification can be initiated as soon as the previous one completes. If you reach this limit, you will encounter throttling and must wait for the rolling 24-hour window to permit further adjustments. +- After **any** disk attribute change, there is a cooldown period of approximately six hours before you can make further adjustments. During this time, no changes are allowed. If you encounter throttling, you’ll need to wait until the cooldown period concludes before making additional modifications. - You can increase disk size but cannot decrease it. diff --git a/apps/docs/content/guides/platform/database-size.mdx b/apps/docs/content/guides/platform/database-size.mdx index eb7e760e22b..6379f087f0a 100644 --- a/apps/docs/content/guides/platform/database-size.mdx +++ b/apps/docs/content/guides/platform/database-size.mdx @@ -71,7 +71,6 @@ Vacuum operations can temporarily increase resource utilization, which may adver Supabase projects have automatic vacuuming enabled, which ensures that these operations are performed regularly to keep the database healthy and performant. - It is possible to [fine-tune](https://www.percona.com/blog/2018/08/10/tuning-autovacuum-in-postgresql-and-autovacuum-internals/) the [autovacuum parameters](https://www.enterprisedb.com/blog/postgresql-vacuum-and-analyze-best-practice-tips), or [manually initiate](https://www.postgresql.org/docs/current/sql-vacuum.html) vacuum operations. Running a manual vacuum after deleting large amounts of data from your DB could help reduce the database size reported by Postgres. @@ -87,9 +86,7 @@ Supabase uses network-attached storage to balance performance with scalability. Projects on the Pro Plan and higher have auto-scaling disks. -Disk size expands automatically when the database reaches 90% of the allocated disk size. The disk is expanded to be 50% larger (for example, 8 GB -> 12 GB). - -Auto-scaling is limited to four modifications within a rolling 24-hour window. While a new modification can be initiated immediately after the previous one completes, reaching the daily quota of four resizes will prevent further scaling until the rolling window allows it. If you reach 95% disk utilization and have exhausted your modification quota, your project will enter read-only mode. +Disk size expands automatically when the database reaches 90% of the allocated disk size. The disk is expanded to be 50% larger (for example, 8 GB -> 12 GB). Auto-scaling can only take place once every 6 hours. If within those 6 hours you reach 95% of the disk space, your project will enter read-only mode. @@ -103,7 +100,7 @@ Disk size can also be manually expanded on the [Database Settings page](/dashboa You may want to import a lot of data into your database which requires multiple disk expansions. for example, uploading more than 1.5x the current size of your database storage will put your database into [read-only mode](#read-only-mode). If so, it is highly recommended you increase the disk size manually on the [Database Settings page](/dashboard/project/_/database/settings). -Due to restrictions on the underlying cloud provider, disk modifications are limited to four operations within a rolling 24-hour window. While a new modification can be initiated as soon as the previous one completes, you will be unable to make further adjustments if you reach this daily quota until the rolling 24-hour window permits it. +Due to restrictions on the underlying cloud provider, disk expansions can occur only once every six hours. During the six hour cool down window, the disk cannot be resized again. diff --git a/apps/docs/content/guides/platform/ssl-enforcement.mdx b/apps/docs/content/guides/platform/ssl-enforcement.mdx index 2557347c6c6..e0cae00ce67 100644 --- a/apps/docs/content/guides/platform/ssl-enforcement.mdx +++ b/apps/docs/content/guides/platform/ssl-enforcement.mdx @@ -7,9 +7,9 @@ Your Supabase project supports connecting to the Postgres DB without SSL enabled Disabling SSL enforcement only applies to connections to Postgres and Supavisor ("Connection Pooler"); all HTTP APIs offered by Supabase (e.g., PostgREST, Storage, Auth) automatically enforce SSL on all incoming connections. - + -Projects need to be at least on Postgres 13.3.0 to enable SSL enforcement. You can find the Postgres version of your project in the [Infrastructure Settings page](/dashboard/project/_/settings/infrastructure). If your project is on an older version, you will need to [upgrade](/docs/guides/platform/migrating-and-upgrading-projects#upgrade-your-project) to use this feature. +Applying or updating SSL enforcement triggers a fast database reboot. On small projects this usually completes in a few seconds, but larger databases may see a longer interruption. diff --git a/apps/docs/content/troubleshooting/high-cpu-and-slow-queries-with-error-must-be-a-superuser-to-terminate-superuser-process.mdx b/apps/docs/content/troubleshooting/high-cpu-and-slow-queries-with-error-must-be-a-superuser-to-terminate-superuser-process.mdx index 1cda79cd074..7b1203a22e2 100644 --- a/apps/docs/content/troubleshooting/high-cpu-and-slow-queries-with-error-must-be-a-superuser-to-terminate-superuser-process.mdx +++ b/apps/docs/content/troubleshooting/high-cpu-and-slow-queries-with-error-must-be-a-superuser-to-terminate-superuser-process.mdx @@ -51,7 +51,7 @@ Since the wraparound prevention autovacuum cannot be stopped, the best approach 2. **Increase Disk Throughput/IOPS:** - **Action:** If disk I/O utilization is also consistently high (e.g., near 100%), consider temporarily increasing your disk's provisioned IOPS and throughput. - **Why it helps:** Autovacuum is an I/O-intensive operation, involving a lot of reading and writing. Higher disk performance can significantly speed up the process. - - **Considerations:** Cloud providers may limit the number of disk modifications (e.g., up to four in 24 hours). You can start a new modification immediately after the previous one finishes, provided you have not exceeded the daily quota. + - **Considerations:** Cloud providers often have limitations, such as a cooldown period (e.g., 6 hours) between disk modification operations. ### **Monitoring progress and future prevention** diff --git a/apps/docs/content/troubleshooting/how-to-bypass-cooldown-period.mdx b/apps/docs/content/troubleshooting/how-to-bypass-cooldown-period.mdx index c4ab2fcaa93..f666c533d68 100644 --- a/apps/docs/content/troubleshooting/how-to-bypass-cooldown-period.mdx +++ b/apps/docs/content/troubleshooting/how-to-bypass-cooldown-period.mdx @@ -1,23 +1,23 @@ --- -title = "How to bypass modification limits" +title = "How to bypass cooldown period" topics = [ "platform" ] -keywords = ["modification limits", "disk resize"] +keywords = ["cooldown", "disk resize"] --- -This limit is not a Supabase limitation. It is rooted in how Amazon EBS (the underlying storage for our databases) manages volume modifications. AWS allows up to four modifications per volume within a rolling 24-hour window. While you can start a new modification immediately after a previous one completes, AWS enforces a quota that prevents a fifth modification within that 24-hour period to ensure data integrity and volume stability. +This cooldown period isn't a Supabase limitation. It's rooted in how Amazon EBS (the underlying storage instance for our databases) manages volume modifications. After modifying a volume (e.g. increasing size, changing type, or IOPS), AWS enforces a mandatory 6-hour cooldown before allowing another modification on the same volume. This is to ensure data integrity and stability of the volume under load. From the [**AWS docs**](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyVolume.html): -> After you initiate a volume modification, you must wait for that modification to reach the completed state before you can initiate another modification for the same volume. You can modify a volume up to four times within a rolling 24-hour period, as long as the volume is in the in-use or available state, and all previous modifications for that volume are completed. If you exceed this limit, you get an error message that indicates when you can perform your next modification. +> “After modifying a volume, you must wait at least six hours and ensure that the volume is in the in-use or available state before you can modify the same volume. This is sometimes referred to as a cooldown period.” -There are a few options to work around this limit if you have already used your four modifications and need to make further adjustments: +There are a few options to work around the cooldown, depending on the state of your database: -1. **Restore to a new project**: This spins up a new instance with a new disk, bypassing the the 24-hour quota entirely. It’s a great option if you're okay with a new project and project refactoring. [**Docs: restoring to a new project**](/docs/guides/platform/backups#restore-to-a-new-project). -2. **pg_upgrade**: Our [**pg_upgrade**](/docs/guides/platform/upgrading) implementation migrates your data to a new disk, which resets the modification count. The main requirement here is that the database must be operational - it can't run it if your DB is in a degraded or inaccessible state. +1. **Restore to a new project**: This spins up a new instance with a new disk, bypassing the cooldown entirely. It’s a great option if you're okay with a new project and project refactoring. [**Docs: restoring to a new project**](/docs/guides/platform/backups#restore-to-a-new-project). +2. **pg_upgrade**: Our [**pg_upgrade**](/docs/guides/platform/upgrading) implementation migrates your data to a new disk, which skips the cooldown. The main requirement here is that the database must be operational - it can't run it if your DB is in a degraded or inaccessible state. 3. **Pause and Restore**: This also migrates to a new disk but is only available for projects on the Free plan. If you're not on the Free plan, you'd need to [**transfer your project to an organization on the Free plan**](/docs/guides/platform/project-transfer) first. -If the database is down or locked in a bad state (e.g corrupted or stuck during resize), and you have hit the modification limit, the only path forward is to wait until the rolling 24-hour window allows for another modification. +If the database is down or locked in a bad state (e.g corrupted or stuck during resize), the only path forward is to wait until the cooldown expires and the disk resize job completes in the queue. More on this in our doc here: [**https://supabase.com/docs/guides/platform/database-size#disk-size**](/docs/guides/platform/database-size#disk-size). diff --git a/apps/docs/features/docs/Reference.navigation.client.tsx b/apps/docs/features/docs/Reference.navigation.client.tsx index 485f6542c78..5df531fc063 100644 --- a/apps/docs/features/docs/Reference.navigation.client.tsx +++ b/apps/docs/features/docs/Reference.navigation.client.tsx @@ -7,7 +7,16 @@ import { ChevronUp } from 'lucide-react' import Link from 'next/link' import { usePathname } from 'next/navigation' import type { HTMLAttributes, MouseEvent, PropsWithChildren } from 'react' -import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react' +import { + createContext, + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, + useSyncExternalStore, +} from 'react' import { cn } from 'ui' @@ -17,6 +26,63 @@ import { BASE_PATH } from '~/lib/constants' export const ReferenceContentInitiallyScrolledContext = createContext(false) +let patchCount = 0 +let originalPushState: typeof history.pushState | null = null +let originalReplaceState: typeof history.replaceState | null = null +const pathnameListeners = new Set<() => void>() + +function notifyPathnameListeners() { + pathnameListeners.forEach((callback) => callback()) +} + +function subscribeToPathname(callback: () => void) { + pathnameListeners.add(callback) + + if (patchCount === 0) { + window.addEventListener('popstate', notifyPathnameListeners) + + originalPushState = history.pushState.bind(history) + history.pushState = (...args) => { + originalPushState!(...args) + notifyPathnameListeners() + } + + originalReplaceState = history.replaceState.bind(history) + history.replaceState = (...args) => { + originalReplaceState!(...args) + notifyPathnameListeners() + } + } + patchCount++ + + return () => { + pathnameListeners.delete(callback) + patchCount-- + + if (patchCount === 0) { + window.removeEventListener('popstate', notifyPathnameListeners) + history.pushState = originalPushState! + history.replaceState = originalReplaceState! + originalPushState = null + originalReplaceState = null + } + } +} + +function getPathname() { + if (typeof window === 'undefined') return '' + const pathname = window.location.pathname + return pathname.startsWith(BASE_PATH) ? pathname.slice(BASE_PATH.length) : pathname +} + +function getServerPathname() { + return '' +} + +function useCurrentPathname() { + return useSyncExternalStore(subscribeToPathname, getPathname, getServerPathname) +} + export function ReferenceContentScrollHandler({ libPath, version, @@ -184,7 +250,7 @@ export function RefLink({ }) { const ref = useRef(null) - const pathname = usePathname() + const pathname = useCurrentPathname() const href = deriveHref(basePath, section) const isActive = pathname === href || (pathname === basePath && href.replace(basePath, '') === '/introduction') @@ -230,7 +296,7 @@ export function RefLink({ function useCompoundRefLinkActive(basePath: string, section: AbbrevApiReferenceSection) { const [open, _setOpen] = useState(false) - const pathname = usePathname() + const pathname = useCurrentPathname() const parentHref = deriveHref(basePath, section) const isParentActive = pathname === parentHref diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt index 8a16b89d8aa..7abaa39f985 100644 --- a/apps/docs/public/humans.txt +++ b/apps/docs/public/humans.txt @@ -5,6 +5,7 @@ Supabase is 100% remote. Aaron Byrne Ada Wong Adam Mokan +AJ Matias Akash Manimaran Alaister Young Alan De Los Santos @@ -66,6 +67,7 @@ Eliot Whalan Emmett Folger Eric Kharitonashvili Etienne Stalmans +Eyal Ehrlich Fabrizio Cataldo Fabrizio Fenoglio Fady A @@ -84,6 +86,7 @@ Hardik Maheshwari Haritabh Gupta Haydn Maley Hieu Pham +Hunter Luckow Ignacio Dobronich Illia Basalaiev Inian P diff --git a/apps/docs/public/img/supabase-auth-cover.png b/apps/docs/public/img/supabase-auth-cover.png deleted file mode 100644 index 627e77779f7..00000000000 Binary files a/apps/docs/public/img/supabase-auth-cover.png and /dev/null differ diff --git a/apps/docs/spec/cli_v1_commands.yaml b/apps/docs/spec/cli_v1_commands.yaml index 6ca173aa802..71fd6cf327c 100644 --- a/apps/docs/spec/cli_v1_commands.yaml +++ b/apps/docs/spec/cli_v1_commands.yaml @@ -1917,9 +1917,9 @@ commands: summary: Show statistics related to vacuum operations per table description: |2 - This shows you stats about the vacuum activities for each table. Due to Postgres' [MVCC](https://www.postgresql.org/docs/current/mvcc.html) when data is updated or deleted new rows are created and old rows are made invisible and marked as "dead tuples". Usually the [autovaccum](https://supabase.com/docs/guides/platform/database-size#vacuum-operations) process will aysnchronously clean the dead tuples. + This shows you stats about the vacuum activities for each table. Due to Postgres' [MVCC](https://www.postgresql.org/docs/current/mvcc.html) when data is updated or deleted new rows are created and old rows are made invisible and marked as "dead tuples". Usually the [autovacuum](https://supabase.com/docs/guides/platform/database-size#vacuum-operations) process will asynchronously clean the dead tuples. - The command lists when the last vacuum and last auto vacuum took place, the row count on the table as well as the count of dead rows and whether autovacuum is expected to run or not. If the number of dead rows is much higher than the row count, or if an autovacuum is expected but has not been performed for some time, this can indicate that autovacuum is not able to keep up and that your vacuum settings need to be tweaked or that you require more compute or disk IOPS to allow autovaccum to complete. + The command lists when the last vacuum and last auto vacuum took place, the row count on the table as well as the count of dead rows and whether autovacuum is expected to run or not. If the number of dead rows is much higher than the row count, or if an autovacuum is expected but has not been performed for some time, this can indicate that autovacuum is not able to keep up and that your vacuum settings need to be tweaked or that you require more compute or disk IOPS to allow autovacuum to complete. ``` @@ -2296,9 +2296,9 @@ commands: Estimates space allocated to a relation that is full of dead tuples description: |2 - This command displays an estimation of table "bloat" - Due to Postgres' [MVCC](https://www.postgresql.org/docs/current/mvcc.html) when data is updated or deleted new rows are created and old rows are made invisible and marked as "dead tuples". Usually the [autovaccum](https://supabase.com/docs/guides/platform/database-size#vacuum-operations) process will asynchronously clean the dead tuples. Sometimes the autovaccum is unable to work fast enough to reduce or prevent tables from becoming bloated. High bloat can slow down queries, cause excessive IOPS and waste space in your database. + This command displays an estimation of table "bloat" - Due to Postgres' [MVCC](https://www.postgresql.org/docs/current/mvcc.html) when data is updated or deleted new rows are created and old rows are made invisible and marked as "dead tuples". Usually the [autovacuum](https://supabase.com/docs/guides/platform/database-size#vacuum-operations) process will asynchronously clean the dead tuples. Sometimes the autovacuum is unable to work fast enough to reduce or prevent tables from becoming bloated. High bloat can slow down queries, cause excessive IOPS and waste space in your database. - Tables with a high bloat ratio should be investigated to see if there are vacuuming is not quick enough or there are other issues. + Tables with a high bloat ratio should be investigated to see if vacuuming is not quick enough or if there are other issues. ``` TYPE │ SCHEMA NAME │ OBJECT NAME │ BLOAT │ WASTE @@ -3258,7 +3258,7 @@ commands: By default, all schemas in the target database are diffed. Use the `--schema public,extensions` flag to restrict diffing to a subset of schemas. - While the diff command is able to capture most schema changes, there are cases where it is known to fail. Currently, this could happen if you schema contains: + While the diff command is able to capture most schema changes, there are cases where it is known to fail. Currently, this could happen if your schema contains: - Changes to publication - Changes to storage buckets diff --git a/apps/studio/.env b/apps/studio/.env index bea99a96c27..580824a0180 100644 --- a/apps/studio/.env +++ b/apps/studio/.env @@ -26,3 +26,5 @@ NEXT_PUBLIC_SUPABASE_URL=https://xguihxuzqibwxjnimxev.supabase.co DOCKER_SOCKET_LOCATION=/var/run/docker.sock +# Required to manage edge-functions via dashboard on Self-Hosting +# EDGE_FUNCTIONS_MANAGEMENT_FOLDER=/path-where-edge-functions-are-mounted diff --git a/apps/studio/components/grid/components/footer/operations/SaveQueueActionBar.tsx b/apps/studio/components/grid/components/footer/operations/SaveQueueActionBar.tsx index 65be243d127..c554f2065dc 100644 --- a/apps/studio/components/grid/components/footer/operations/SaveQueueActionBar.tsx +++ b/apps/studio/components/grid/components/footer/operations/SaveQueueActionBar.tsx @@ -1,21 +1,22 @@ -import { Eye } from 'lucide-react' +import { useOperationQueueActions } from 'components/grid/hooks/useOperationQueueActions' +import { useOperationQueueShortcuts } from 'components/grid/hooks/useOperationQueueShortcuts' +import { useIsQueueOperationsEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { AnimatePresence, motion } from 'framer-motion' +import { Eye } from 'lucide-react' import { createPortal } from 'react-dom' +import { useTableEditorStateSnapshot } from 'state/table-editor' import { Button } from 'ui' -import { - useOperationQueueShortcuts, - getModKey, -} from 'components/grid/hooks/useOperationQueueShortcuts' -import { useIsQueueOperationsEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' -import { useTableEditorStateSnapshot } from 'state/table-editor' -import { useOperationQueueActions } from 'components/grid/hooks/useOperationQueueActions' +import { getModKeyLabel } from '@/lib/helpers' export const SaveQueueActionBar = () => { + const modKey = getModKeyLabel() const snap = useTableEditorStateSnapshot() const isQueueOperationsEnabled = useIsQueueOperationsEnabled() const { handleSave } = useOperationQueueActions() + useOperationQueueShortcuts() + const operationCount = snap.operationQueue.operations.length const isSaving = snap.operationQueue.status === 'saving' const isOperationQueuePanelOpen = snap.sidePanel?.type === 'operation-queue' @@ -23,16 +24,6 @@ export const SaveQueueActionBar = () => { const isVisible = isQueueOperationsEnabled && snap.hasPendingOperations && !isOperationQueuePanelOpen - useOperationQueueShortcuts({ - enabled: isQueueOperationsEnabled && snap.hasPendingOperations, - onSave: handleSave, - onTogglePanel: () => snap.onViewOperationQueue(), - isSaving, - hasOperations: operationCount > 0, - }) - - const modKey = getModKey() - const content = ( {isVisible && ( @@ -49,7 +40,7 @@ export const SaveQueueActionBar = () => {
- diff --git a/apps/studio/components/interfaces/Database/RestoreToNewProject/PreviousRestoreItem.tsx b/apps/studio/components/interfaces/Database/RestoreToNewProject/PreviousRestoreItem.tsx index ab65a4768f9..dcd1092da3d 100644 --- a/apps/studio/components/interfaces/Database/RestoreToNewProject/PreviousRestoreItem.tsx +++ b/apps/studio/components/interfaces/Database/RestoreToNewProject/PreviousRestoreItem.tsx @@ -15,8 +15,10 @@ export const PreviousRestoreItem = ({ clone }: { clone: CloneStatus['clones'][nu
@@ -33,8 +35,10 @@ export const PreviousRestoreItem = ({ clone }: { clone: CloneStatus['clones'][nu
diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx b/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx index d3407ba1e32..c57fdbda3b2 100644 --- a/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx +++ b/apps/studio/components/interfaces/Functions/EdgeFunctionsListItem.tsx @@ -3,11 +3,12 @@ import { Check, Copy } from 'lucide-react' import { useRouter } from 'next/router' import { useState } from 'react' +import { IS_PLATFORM } from 'common' import { useParams } from 'common/hooks' import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query' import type { EdgeFunctionsResponse } from 'data/edge-functions/edge-functions-query' -import { copyToClipboard, TableCell, TableRow } from 'ui' +import { cn, copyToClipboard, TableCell, TableRow } from 'ui' import { TimestampInfo } from 'ui-patterns' import { createNavigationHandler } from 'lib/navigation' @@ -30,7 +31,9 @@ export const EdgeFunctionsListItem = ({ function: item }: EdgeFunctionsListItemP ? `https://${customDomainData.customDomain.hostname}/functions/v1/${item.slug}` : `${protocol}://${endpoint}/functions/v1/${item.slug}` - const handleNavigation = createNavigationHandler(`/project/${ref}/functions/${item.slug}`, router) + const handleNavigation = IS_PLATFORM + ? createNavigationHandler(`/project/${ref}/functions/${item.slug}`, router) + : undefined return (

{item.name}

diff --git a/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx b/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx index caee9cf88b8..86c8223c9a2 100644 --- a/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx +++ b/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx @@ -189,7 +189,7 @@ export const FunctionsEmptyState = () => { ) } -export const FunctionsEmptyStateLocal = () => { +export const FunctionsInstructionsLocal = () => { const showStripeExample = useIsFeatureEnabled('edge_functions:show_stripe_example') const templates = useMemo(() => { if (showStripeExample) { diff --git a/apps/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/apps/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index ed36761b42f..6530d5a579c 100644 --- a/apps/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/apps/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -1,8 +1,10 @@ -import { Github } from 'lucide-react' +import { Github, MoreVertical, Trash, Copy } from 'lucide-react' import InlineSVG from 'react-inlinesvg' +import { useState } from 'react' import CardButton from 'components/ui/CardButton' import { ComputeBadgeWrapper } from 'components/ui/ComputeBadgeWrapper' +import { DeleteProjectModal } from 'components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal' import type { IntegrationProjectConnection } from 'data/integrations/integrations.types' import { ProjectIndexPageLink } from 'data/prefetchers/project.$ref' import { getComputeSize, OrgProject } from 'data/projects/org-projects-infinite-query' @@ -12,10 +14,21 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { BASE_PATH } from 'lib/constants' import { inferProjectStatus } from './ProjectCard.utils' import { ProjectCardStatus } from './ProjectCardStatus' +import type { Organization } from 'types' +import { toast } from 'sonner' +import { + copyToClipboard, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + Button, +} from 'ui' export interface ProjectCardProps { slug?: string project: OrgProject + organization?: Organization rewriteHref?: string githubIntegration?: IntegrationProjectConnection vercelIntegration?: IntegrationProjectConnection @@ -25,12 +38,14 @@ export interface ProjectCardProps { export const ProjectCard = ({ slug, project, + organization, rewriteHref, githubIntegration, vercelIntegration, resourceWarnings, }: ProjectCardProps) => { const { name, ref: projectRef } = project + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) const { infraAwsNimbusLabel } = useCustomContent(['infra:aws_nimbus_label']) const providerLabel = @@ -48,52 +63,105 @@ export const ProjectCard = ({ const projectStatus = inferProjectStatus(project.status) return ( -
  • - - {/* Text */} -
    -
    {name}
    -

    {desc}

    -
    - {/* Compute and integrations */} -
    - {project.status !== 'INACTIVE' && projectHomepageShowInstanceSize && ( - - )} - {isVercelIntegrated && ( -
    - -
    - )} - {isGithubIntegrated && ( -
    -
    - + <> +
  • + +
    +
    +
    {name}
    +
    e.preventDefault()}> + + +
    -

    {githubRepository}

    - )} +

    {desc}

    +
    +
    + + {project.status !== 'INACTIVE' && projectHomepageShowInstanceSize && ( + + )} + {isVercelIntegrated && ( +
    + +
    + )} + {isGithubIntegrated && ( +
    +
    + +
    +

    {githubRepository}

    +
    + )} +
  • - - } - footer={ - - } - containerElement={} + } + footer={ + + } + containerElement={} + /> + + setIsDeleteModalOpen(false)} + project={project} + organization={organization} /> - + ) } diff --git a/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index 6e25f8fa504..f3c0c18b0d1 100644 --- a/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -148,6 +148,7 @@ export const ProjectList = ({ organization: organization_, rewriteHref }: Projec Compute Region Created + diff --git a/apps/studio/components/interfaces/Home/ProjectList/ProjectTableRow.tsx b/apps/studio/components/interfaces/Home/ProjectList/ProjectTableRow.tsx index 2c158b0d638..a6e3e94951a 100644 --- a/apps/studio/components/interfaces/Home/ProjectList/ProjectTableRow.tsx +++ b/apps/studio/components/interfaces/Home/ProjectList/ProjectTableRow.tsx @@ -1,6 +1,7 @@ -import { Github } from 'lucide-react' +import { Github, MoreVertical, Trash, Copy, Check } from 'lucide-react' import { useRouter } from 'next/router' import InlineSVG from 'react-inlinesvg' +import { useState } from 'react' import { ComputeBadgeWrapper } from 'components/ui/ComputeBadgeWrapper' import type { IntegrationProjectConnection } from 'data/integrations/integrations.types' @@ -9,10 +10,21 @@ import type { ResourceWarning } from 'data/usage/resource-warnings-query' import { BASE_PATH } from 'lib/constants' import { createNavigationHandler } from 'lib/navigation' import type { Organization } from 'types' -import { TableCell, TableRow } from 'ui' +import { + DropdownMenuTrigger, + DropdownMenuItem, + DropdownMenuContent, + DropdownMenu, + TableCell, + TableRow, + Button, +} from 'ui' import { TimestampInfo } from 'ui-patterns' import { inferProjectStatus } from './ProjectCard.utils' import { ProjectCardStatus } from './ProjectCardStatus' +import { DeleteProjectModal } from 'components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal' +import { toast } from 'sonner' +import { copyToClipboard } from 'ui' export interface ProjectTableRowProps { project: OrgProject @@ -34,93 +46,154 @@ export const ProjectTableRow = ({ const router = useRouter() const { name, ref: projectRef } = project const projectStatus = inferProjectStatus(project.status) + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) + const [isCopied, setIsCopied] = useState(false) const url = rewriteHref ?? `/project/${project.ref}` const isGithubIntegrated = githubIntegration !== undefined const isVercelIntegrated = vercelIntegration !== undefined const githubRepository = githubIntegration?.metadata.name ?? undefined - - const infraInformation = project.databases.find((x) => x.identifier === project.ref) - const handleNavigation = createNavigationHandler(url, router) + const handleCopyProjectRef = (e: React.SyntheticEvent) => { + e.stopPropagation() + copyToClipboard(projectRef) + setIsCopied(true) + toast.success('Copied project ID to clipboard') + setTimeout(() => setIsCopied(false), 2000) + } + return ( - - -
    - {/* Text */} -
    -
    {name}
    -

    ID: {projectRef}

    -
    - {/* Integrations */} - {(isGithubIntegrated || isVercelIntegrated) && ( -
    - {isVercelIntegrated && ( -
    - + + +
    +
    +
    {name}
    +
    - )} - {isGithubIntegrated && ( -
    -
    - -
    - {githubRepository && ( -

    {githubRepository}

    - )} -
    - )} + )} +
    - )} -
    - - - - - -
    - {project.status !== 'INACTIVE' ? ( - + {isVercelIntegrated && ( +
    + +
    + )} + {isGithubIntegrated && ( +
    +
    + +
    + {githubRepository && ( +

    {githubRepository}

    + )} +
    + )} +
    + )} +
    + + + + + +
    + {project.status !== 'INACTIVE' ? ( + + ) : ( + – + )} +
    +
    + + + {project.cloud_provider} | {project.region || 'N/A'} + + + + {project.inserted_at ? ( + ) : ( - – + N/A )} -
    -
    - - - {project.cloud_provider} | {project.region || 'N/A'} - - - - {project.inserted_at ? ( - - ) : ( - N/A - )} - -
    + + +
    e.stopPropagation()}> + + +
    +
    + + setIsDeleteModalOpen(false)} + project={project} + organization={organization} + /> + ) } diff --git a/apps/studio/components/interfaces/Home/ProjectUsage.tsx b/apps/studio/components/interfaces/Home/ProjectUsage.tsx index 1127da0644a..29d1645696d 100644 --- a/apps/studio/components/interfaces/Home/ProjectUsage.tsx +++ b/apps/studio/components/interfaces/Home/ProjectUsage.tsx @@ -1,11 +1,3 @@ -import dayjs from 'dayjs' -import { Auth, Database, Realtime, Storage } from 'icons' -import sumBy from 'lodash/sumBy' -import { ChevronDown } from 'lucide-react' -import Link from 'next/link' -import { useRouter } from 'next/router' -import { useState } from 'react' - import { useParams } from 'common' import BarChart from 'components/ui/Charts/BarChart' import { InlineLink } from 'components/ui/InlineLink' @@ -15,10 +7,17 @@ import { UsageApiCounts, useProjectLogStatsQuery, } from 'data/analytics/project-log-stats-query' +import dayjs from 'dayjs' import { useFillTimeseriesSorted } from 'hooks/analytics/useFillTimeseriesSorted' import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' +import { Auth, Database, Realtime, Storage } from 'icons' +import sumBy from 'lodash/sumBy' +import { ChevronDown } from 'lucide-react' +import Link from 'next/link' +import { useRouter } from 'next/router' +import { useState } from 'react' import type { ChartIntervals } from 'types' import { Button, @@ -88,20 +87,20 @@ const ProjectUsage = () => { selectedInterval.startUnit as dayjs.ManipulateType ) const endDateLocal = dayjs() - const { data: charts } = useFillTimeseriesSorted( - data?.result || [], - 'timestamp', - [ + const { data: charts } = useFillTimeseriesSorted({ + data: data?.result ?? [], + timestampKey: 'timestamp', + valueKey: [ 'total_auth_requests', 'total_rest_requests', 'total_storage_requests', 'total_realtime_requests', ], - 0, - startDateLocal.toISOString(), - endDateLocal.toISOString(), - 5 - ) + defaultValue: 0, + startDate: startDateLocal.toISOString(), + endDate: endDateLocal.toISOString(), + minPointsToFill: 5, + }) const datetimeFormat = selectedInterval.format || 'MMM D, ha' const handleBarClick = ( diff --git a/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.ts b/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.ts index 6b5b8a680b2..cb9e65c7833 100644 --- a/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.ts +++ b/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.ts @@ -8,6 +8,7 @@ import type { LogsBarChartDatum } from '../HomeNew/ProjectUsage.metrics' import { LogsTableName } from '../Settings/Logs/Logs.constants' import { genChartQuery } from '../Settings/Logs/Logs.utils' import { + type RawChartData, calculateAggregatedMetrics, calculateDateRange, calculateHealthMetrics, @@ -81,11 +82,11 @@ const fetchServiceHealthMetrics = async ( signal, }) - if (error || data?.error) { - throw error || data?.error + if (error ?? data?.error) { + throw error ?? data?.error } - return (data?.result || []) as ChartQueryResult[] + return (data?.result ?? []) as ChartQueryResult[] } /** @@ -119,18 +120,18 @@ const useServiceHealthQuery = ({ const normalizedData = useTimeseriesUnixToIso(queryResult.data ?? [], 'timestamp') // Fill gaps in timeseries - const { data: filledData } = useFillTimeseriesSorted( - normalizedData, - 'timestamp', - 'ok_count', - 0, + const { data: filledData } = useFillTimeseriesSorted({ + data: normalizedData, + timestampKey: 'timestamp', + valueKey: 'ok_count', + defaultValue: 0, startDate, - endDate - ) + endDate, + }) // Transform to LogsBarChartDatum format const eventChartData: LogsBarChartDatum[] = useMemo( - () => transformToBarChartData(filledData), + () => transformToBarChartData(filledData as RawChartData[]), [filledData] ) diff --git a/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.utils.ts b/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.utils.ts index 9baab7b5476..cb3d7c8da0b 100644 --- a/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.utils.ts +++ b/apps/studio/components/interfaces/Observability/useServiceHealthMetrics.utils.ts @@ -36,11 +36,12 @@ export const calculateDateRange = ( return { startDate: start, endDate: end } } -type RawChartData = { +export type RawChartData = { timestamp: string | number ok_count?: number | null warning_count?: number | null error_count?: number | null + [key: string]: string | number | null | undefined } /** @@ -49,9 +50,9 @@ type RawChartData = { export const transformToBarChartData = (data: RawChartData[]): LogsBarChartDatum[] => { return data.map((row) => ({ timestamp: typeof row.timestamp === 'string' ? row.timestamp : String(row.timestamp), - ok_count: row.ok_count || 0, - warning_count: row.warning_count || 0, - error_count: row.error_count || 0, + ok_count: row.ok_count ?? 0, + warning_count: row.warning_count ?? 0, + error_count: row.error_count ?? 0, })) } diff --git a/apps/studio/components/interfaces/Reports/renderers/ApiRenderers.tsx b/apps/studio/components/interfaces/Reports/renderers/ApiRenderers.tsx index 2c37fb750ba..dbbcd0eb305 100644 --- a/apps/studio/components/interfaces/Reports/renderers/ApiRenderers.tsx +++ b/apps/studio/components/interfaces/Reports/renderers/ApiRenderers.tsx @@ -1,47 +1,47 @@ -import sumBy from 'lodash/sumBy' -import { ChevronRight } from 'lucide-react' -import { Fragment, useRef, useState } from 'react' -import * as z from 'zod' - import { useParams } from 'common' +import { COUNTRY_LAT_LON } from 'components/interfaces/ProjectCreation/ProjectCreation.constants' +import { + MAP_CHART_THEME, + buildCountsByIso2, + computeMarkerRadius, + extractIso2FromFeatureProps, + getFillColor, + getFillOpacity, + isKnownCountryCode, + isMicroCountry, + iso2ToCountryName, +} from 'components/interfaces/Reports/utils/geo' import { - jsonSyntaxHighlight, TextFormatter, + jsonSyntaxHighlight, } from 'components/interfaces/Settings/Logs/LogsFormatters' import Table from 'components/to-be-cleaned/Table' import AlertError from 'components/ui/AlertError' import BarChart from 'components/ui/Charts/BarChart' +import { geoCentroid } from 'd3-geo' import { useFillTimeseriesSorted } from 'hooks/analytics/useFillTimeseriesSorted' +import { BASE_PATH } from 'lib/constants' +import sumBy from 'lodash/sumBy' +import { ChevronRight } from 'lucide-react' +import { useTheme } from 'next-themes' +import { Fragment, useRef, useState } from 'react' +import { ComposableMap, Geographies, Geography, Marker, ZoomableGroup } from 'react-simple-maps' import type { ResponseError } from 'types' import { - Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, + Alert_Shadcn_, Button, Collapsible, - Collapsible_Shadcn_, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, + Collapsible_Shadcn_, WarningIcon, } from 'ui' -import { queryParamsToObject } from '../Reports.utils' +import * as z from 'zod' + import { ReportWidgetProps, ReportWidgetRendererProps } from '../ReportWidget' -import { ComposableMap, Geographies, Geography, Marker, ZoomableGroup } from 'react-simple-maps' -import { COUNTRY_LAT_LON } from 'components/interfaces/ProjectCreation/ProjectCreation.constants' -import { BASE_PATH } from 'lib/constants' -import { geoCentroid } from 'd3-geo' -import { - buildCountsByIso2, - getFillColor, - getFillOpacity, - isMicroCountry, - isKnownCountryCode, - computeMarkerRadius, - MAP_CHART_THEME, - extractIso2FromFeatureProps, - iso2ToCountryName, -} from 'components/interfaces/Reports/utils/geo' -import { useTheme } from 'next-themes' +import { queryParamsToObject } from '../Reports.utils' export const NetworkTrafficRenderer = ( props: ReportWidgetProps<{ @@ -50,14 +50,14 @@ export const NetworkTrafficRenderer = ( egress: number }> ) => { - const { data, error, isError } = useFillTimeseriesSorted( - props.data, - 'timestamp', - ['ingress_mb', 'egress_mb'], - 0, - props.params?.iso_timestamp_start, - props.params?.iso_timestamp_end - ) + const { data, error, isError } = useFillTimeseriesSorted({ + data: props.data, + timestampKey: 'timestamp', + valueKey: ['ingress_mb', 'egress_mb'], + defaultValue: 0, + startDate: props.params?.iso_timestamp_start, + endDate: props.params?.iso_timestamp_end, + }) const totalIngress = sumBy(props.data, 'ingress_mb') const totalEgress = sumBy(props.data, 'egress_mb') @@ -76,7 +76,7 @@ export const NetworkTrafficRenderer = ( Failed to retrieve network traffic - {error.message} + {error?.message ?? 'Unknown error'} ) } @@ -121,14 +121,14 @@ export const TotalRequestsChartRenderer = ( const total = props.data.reduce((acc, datum) => { return acc + datum.count }, 0) - const { data, error, isError } = useFillTimeseriesSorted( - props.data, - 'timestamp', - 'count', - 0, - props.params?.iso_timestamp_start, - props.params?.iso_timestamp_end - ) + const { data, error, isError } = useFillTimeseriesSorted({ + data: props.data, + timestampKey: 'timestamp', + valueKey: 'count', + defaultValue: 0, + startDate: props.params?.iso_timestamp_start, + endDate: props.params?.iso_timestamp_end, + }) if (!!props.error) { const error = ( @@ -140,7 +140,7 @@ export const TotalRequestsChartRenderer = ( Failed to retrieve total requests - {error.message} + {error?.message ?? 'Unknown error'} ) } @@ -253,14 +253,14 @@ export const ErrorCountsChartRenderer = ( return acc + datum.count }, 0) - const { data, error, isError } = useFillTimeseriesSorted( - props.data, - 'timestamp', - 'count', - 0, - props.params?.iso_timestamp_start, - props.params?.iso_timestamp_end - ) + const { data, error, isError } = useFillTimeseriesSorted({ + data: props.data, + timestampKey: 'timestamp', + valueKey: 'count', + defaultValue: 0, + startDate: props.params?.iso_timestamp_start, + endDate: props.params?.iso_timestamp_end, + }) if (!!props.error) { const error = ( @@ -272,7 +272,7 @@ export const ErrorCountsChartRenderer = ( Failed to retrieve request errors - {error.message} + {error?.message ?? 'Unknown error'} ) } @@ -302,14 +302,14 @@ export const ResponseSpeedChartRenderer = ( avg: datum.avg, })) - const { data, error, isError } = useFillTimeseriesSorted( - transformedData, - 'timestamp', - 'avg', - 0, - props.params?.iso_timestamp_start, - props.params?.iso_timestamp_end - ) + const { data, error, isError } = useFillTimeseriesSorted({ + data: transformedData, + timestampKey: 'timestamp', + valueKey: 'avg', + defaultValue: 0, + startDate: props.params?.iso_timestamp_start, + endDate: props.params?.iso_timestamp_end, + }) const lastAvg = props.data[props.data.length - 1]?.avg @@ -323,7 +323,7 @@ export const ResponseSpeedChartRenderer = ( Failed to retrieve response speeds - {error.message} + {error?.message ?? 'Unknown error'} ) } diff --git a/apps/studio/components/interfaces/Reports/v2/ReportChartV2.tsx b/apps/studio/components/interfaces/Reports/v2/ReportChartV2.tsx index ef21606cd2c..39633d13047 100644 --- a/apps/studio/components/interfaces/Reports/v2/ReportChartV2.tsx +++ b/apps/studio/components/interfaces/Reports/v2/ReportChartV2.tsx @@ -1,16 +1,17 @@ import { useQuery } from '@tanstack/react-query' -import { Loader2 } from 'lucide-react' -import { useState } from 'react' - import type { ChartHighlightAction } from 'components/ui/Charts/ChartHighlightActions' import { ComposedChart } from 'components/ui/Charts/ComposedChart' +import type { MultiAttribute } from 'components/ui/Charts/ComposedChart.utils' import { useChartHighlight } from 'components/ui/Charts/useChartHighlight' import type { AnalyticsInterval } from 'data/analytics/constants' import type { ReportConfig } from 'data/reports/v2/reports.types' import { useFillTimeseriesSorted } from 'hooks/analytics/useFillTimeseriesSorted' import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' +import { Loader2 } from 'lucide-react' +import { useState } from 'react' import { Card, CardContent, cn } from 'ui' + import { ReportChartUpsell } from './ReportChartUpsell' export interface ReportChartV2Props { @@ -32,10 +33,13 @@ export interface ReportChartV2Props { // Compute total across entire period over unique attribute keys. // Excludes attributes that are disabled, reference lines, max values, or marked omitFromTotal. -export function computePeriodTotal(chartData: any[], dynamicAttributes: any[]): number { +export function computePeriodTotal( + chartData: Record[], + dynamicAttributes: MultiAttribute[] +): number { const attributeKeys = Array.from( new Set( - (dynamicAttributes as any[]) + dynamicAttributes .filter( (a) => a?.enabled !== false && @@ -43,11 +47,11 @@ export function computePeriodTotal(chartData: any[], dynamicAttributes: any[]): !a?.isMaxValue && !a?.omitFromTotal ) - .map((a: any) => a.attribute) + .map((a) => a.attribute) ) ) - return chartData.reduce((sum: number, row: any) => { + return chartData.reduce((sum: number, row: Record) => { const rowTotal = attributeKeys.reduce((acc: number, key: string) => { const value = row?.[key] return acc + (typeof value === 'number' ? value : 0) @@ -111,16 +115,16 @@ export const ReportChartV2 = ({ const firstItem = chartData[0] const timestampKey = firstItem?.hasOwnProperty('timestamp') ? 'timestamp' : 'period_start' - const { data: filledChartData, isError: isFillError } = useFillTimeseriesSorted( - chartData, + const { data: filledChartData, isError: isFillError } = useFillTimeseriesSorted({ + data: chartData, timestampKey, - (dynamicAttributes as any[]).map((attr: any) => attr.attribute), - 0, + valueKey: dynamicAttributes.map((attr) => attr.attribute), + defaultValue: 0, startDate, endDate, - undefined, - interval - ) + minPointsToFill: undefined, + interval, + }) const [chartStyle, setChartStyle] = useState(report.defaultChartStyle) const chartHighlight = useChartHighlight() diff --git a/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx b/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx index 67fd5a83bea..32b76175d8a 100644 --- a/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx +++ b/apps/studio/components/interfaces/Settings/General/DeleteProjectPanel/DeleteProjectModal.tsx @@ -7,22 +7,32 @@ import { CANCELLATION_REASONS } from 'components/interfaces/Billing/Billing.cons import { TextConfirmModal } from 'components/ui/TextConfirmModalWrapper' import { useSendDowngradeFeedbackMutation } from 'data/feedback/exit-survey-send' import { useProjectDeleteMutation } from 'data/projects/project-delete-mutation' +import type { OrgProject } from 'data/projects/org-projects-infinite-query' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' +import type { Organization } from 'types' import { Input } from 'ui' export const DeleteProjectModal = ({ visible, onClose, + project: projectProp, + organization: organizationProp, }: { visible: boolean onClose: () => void + project?: OrgProject + organization?: Organization }) => { const router = useRouter() - const { data: project } = useSelectedProjectQuery() - const { data: organization } = useSelectedOrganizationQuery() + const { data: projectFromQuery } = useSelectedProjectQuery() + const { data: organizationFromQuery } = useSelectedOrganizationQuery() + + // Use props if provided, otherwise fall back to hooks + const project = projectProp || projectFromQuery + const organization = organizationProp || organizationFromQuery const [lastVisitedOrganization] = useLocalStorageQuery( LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION, diff --git a/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/InitializeForeignSchemaDialog.tsx b/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/InitializeForeignSchemaDialog.tsx index bb79a037b79..03f2f808e53 100644 --- a/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/InitializeForeignSchemaDialog.tsx +++ b/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/InitializeForeignSchemaDialog.tsx @@ -1,10 +1,4 @@ import { zodResolver } from '@hookform/resolvers/zod' -import { parseAsBoolean, useQueryState } from 'nuqs' -import { useState } from 'react' -import { SubmitHandler, useForm } from 'react-hook-form' -import { toast } from 'sonner' -import z from 'zod' - import { useQueryClient } from '@tanstack/react-query' import { useParams } from 'common' import { formatWrapperTables } from 'components/interfaces/Integrations/Wrappers/Wrappers.utils' @@ -16,6 +10,10 @@ import { useFDWUpdateMutation } from 'data/fdw/fdw-update-mutation' import { fdwKeys } from 'data/fdw/keys' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { DOCS_URL } from 'lib/constants' +import { parseAsBoolean, useQueryState } from 'nuqs' +import { useState } from 'react' +import { SubmitHandler, useForm } from 'react-hook-form' +import { toast } from 'sonner' import { Button, Dialog, @@ -26,13 +24,17 @@ import { DialogSectionSeparator, DialogTitle, DialogTrigger, - Form_Shadcn_, FormField_Shadcn_, + Form_Shadcn_, Input_Shadcn_, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' +import z from 'zod' + import { getDecryptedParameters } from '../../Storage.utils' +import { useS3VectorsWrapperExtension } from '../useS3VectorsWrapper' import { useS3VectorsWrapperInstance } from '../useS3VectorsWrapperInstance' +import { isGreaterThanOrEqual } from '@/lib/semver' // Create foreign tables for vector bucket export const InitializeForeignSchemaDialog = () => { @@ -45,6 +47,10 @@ export const InitializeForeignSchemaDialog = () => { const [isCreating, setIsCreating] = useState(false) const { data: wrapperInstance, meta: wrapperMeta } = useS3VectorsWrapperInstance({ bucketId }) + const { extension: wrappersExtension } = useS3VectorsWrapperExtension() + const updatedImportForeignSchemaSyntax = !!wrappersExtension?.installed_version + ? isGreaterThanOrEqual(wrappersExtension.installed_version, '0.5.7') + : false const FormSchema = z.object({ schema: z @@ -107,9 +113,9 @@ export const InitializeForeignSchemaDialog = () => { projectRef, connectionString: project?.connectionString, serverName: wrapperInstance.server_name, - sourceSchema: values.schema, + sourceSchema: updatedImportForeignSchemaSyntax ? bucketId : values.schema, targetSchema: values.schema, - schemaOptions: [`bucket_name '${bucketId}'`], + schemaOptions: updatedImportForeignSchemaSyntax ? undefined : [`bucket_name '${bucketId}'`], }) toast.success( diff --git a/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/VectorBucketTableExamplesSheet.tsx b/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/VectorBucketTableExamplesSheet.tsx index 9ececaf5dff..1b57d52ebc1 100644 --- a/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/VectorBucketTableExamplesSheet.tsx +++ b/apps/studio/components/interfaces/Storage/VectorBuckets/VectorBucketDetails/VectorBucketTableExamplesSheet.tsx @@ -1,8 +1,3 @@ -import { ChevronDown, ListPlus } from 'lucide-react' -import Link from 'next/link' -import { parseAsBoolean, useQueryState } from 'nuqs' -import { useState } from 'react' - import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { DocsButton } from 'components/ui/DocsButton' @@ -11,26 +6,33 @@ import { VectorBucketIndex } from 'data/storage/vector-buckets-indexes-query' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { SqlEditor } from 'icons' import { DOCS_URL } from 'lib/constants' +import { ChevronDown, ListPlus } from 'lucide-react' +import Link from 'next/link' +import { parseAsBoolean, useQueryState } from 'nuqs' +import { useState } from 'react' import { Button, - cn, CodeBlock, - Command_Shadcn_, CommandGroup_Shadcn_, CommandItem_Shadcn_, CommandList_Shadcn_, - Popover_Shadcn_, + Command_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, + Popover_Shadcn_, Sheet, SheetContent, SheetHeader, SheetSection, SheetTitle, SheetTrigger, + cn, } from 'ui' import { Admonition } from 'ui-patterns' + +import { useS3VectorsWrapperExtension } from '../useS3VectorsWrapper' import { useS3VectorsWrapperInstance } from '../useS3VectorsWrapperInstance' +import { isGreaterThanOrEqual } from '@/lib/semver' interface VectorBucketTableExamplesSheetProps { index: VectorBucketIndex @@ -147,6 +149,11 @@ function VectorBucketIndexExamples({ const { data: wrapperInstance } = useS3VectorsWrapperInstance({ bucketId }) const foreignTable = wrapperInstance?.tables?.find((x) => x.name === indexName) + const { extension: wrappersExtension } = useS3VectorsWrapperExtension() + const updatedImportForeignSchemaSyntax = !!wrappersExtension?.installed_version + ? isGreaterThanOrEqual(wrappersExtension.installed_version, '0.5.7') + : false + const dimensionLabel = `Data should match ${dimension} dimension${dimension > 1 ? 's' : ''}` const startValue = 0.1 const dimensionComment = generateDimensionComment(dimension) @@ -159,12 +166,12 @@ insert into values ( 'doc-1', - '[${dimensionExample}]'::embd${sqlComment}, + '[${dimensionExample}]'${updatedImportForeignSchemaSyntax ? '::s3vec' : '::embd'}${sqlComment}, '{${metadataKeys.map((key) => `"${key}": "${key} value"`).join(', ')}}'::jsonb ), ( 'doc-2', - '[${dimensionExample}]'::embd${sqlComment}, + '[${dimensionExample}]'${updatedImportForeignSchemaSyntax ? '::s3vec' : '::embd'}${sqlComment}, '{${metadataKeys.map((key) => `"${key}": "${key} value"`).join(', ')}}'::jsonb );` diff --git a/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx b/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx index 1d6bf95ae82..58720a1cd27 100644 --- a/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx +++ b/apps/studio/components/interfaces/Support/CategoryAndSeverityInfo.tsx @@ -1,21 +1,21 @@ -import type { UseFormReturn } from 'react-hook-form' // End of third-party imports - import { SupportCategories } from '@supabase/shared-types/out/constants' import { InlineLink } from 'components/ui/InlineLink' +import type { UseFormReturn } from 'react-hook-form' import { - cn, FormControl_Shadcn_, FormField_Shadcn_, - Select_Shadcn_, SelectContent_Shadcn_, SelectGroup_Shadcn_, SelectItem_Shadcn_, SelectTrigger_Shadcn_, SelectValue_Shadcn_, + Select_Shadcn_, + cn, } from 'ui' import { Admonition } from 'ui-patterns/admonition' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' + import { CATEGORY_OPTIONS, type ExtendedSupportCategories, @@ -91,7 +91,7 @@ function CategorySelector({ form }: CategorySelectorProps) { - {CATEGORY_OPTIONS.filter((option) => !option.hidden).map((option) => ( + {CATEGORY_OPTIONS.map((option) => ( {option.label} diff --git a/apps/studio/components/interfaces/Support/LinkSupportTicketForm.tsx b/apps/studio/components/interfaces/Support/LinkSupportTicketForm.tsx index aeb32893467..68f35b26718 100644 --- a/apps/studio/components/interfaces/Support/LinkSupportTicketForm.tsx +++ b/apps/studio/components/interfaces/Support/LinkSupportTicketForm.tsx @@ -1,21 +1,21 @@ import { zodResolver } from '@hookform/resolvers/zod' +import { useLinkSupportTicketMutation } from 'data/feedback/link-support-ticket-mutation' +import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { Link2 } from 'lucide-react' import { useEffect } from 'react' import type { SubmitHandler } from 'react-hook-form' import { useForm } from 'react-hook-form' import { toast } from 'sonner' - -import { useLinkSupportTicketMutation } from 'data/feedback/link-support-ticket-mutation' -import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { Button, DialogSectionSeparator, - Form_Shadcn_, FormControl_Shadcn_, FormField_Shadcn_, + Form_Shadcn_, Input_Shadcn_, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' + import { CategoryAndSeverityInfo } from './CategoryAndSeverityInfo' import { LinkSupportTicketFormSchema, @@ -23,8 +23,8 @@ import { } from './LinkSupportTicketForm.schema' import { OrganizationSelector } from './OrganizationSelector' import { ProjectAndPlanInfo } from './ProjectAndPlanInfo' -import { SUPPORT_ACCESS_CATEGORIES, SupportAccessToggle } from './SupportAccessToggle' -import { getOrgSubscriptionPlan, NO_ORG_MARKER, NO_PROJECT_MARKER } from './SupportForm.utils' +import { DISABLE_SUPPORT_ACCESS_CATEGORIES, SupportAccessToggle } from './SupportAccessToggle' +import { NO_ORG_MARKER, NO_PROJECT_MARKER, getOrgSubscriptionPlan } from './SupportForm.utils' interface LinkSupportTicketFormProps { conversationId: string @@ -88,9 +88,10 @@ export const LinkSupportTicketForm = ({ ? values.projectRef : undefined, category: values.category, - allow_support_access: SUPPORT_ACCESS_CATEGORIES.includes(values.category) - ? values.allowSupportAccess - : false, + allow_support_access: + values.category && !DISABLE_SUPPORT_ACCESS_CATEGORIES.includes(values.category) + ? values.allowSupportAccess + : false, }) } @@ -153,7 +154,7 @@ export const LinkSupportTicketForm = ({ - {SUPPORT_ACCESS_CATEGORIES.includes(category) && ( + {!!category && !DISABLE_SUPPORT_ACCESS_CATEGORIES.includes(category) && ( <>
    diff --git a/apps/studio/components/interfaces/Support/Support.constants.ts b/apps/studio/components/interfaces/Support/Support.constants.ts index f7ddf76f920..8e02f560bfd 100644 --- a/apps/studio/components/interfaces/Support/Support.constants.ts +++ b/apps/studio/components/interfaces/Support/Support.constants.ts @@ -10,7 +10,6 @@ export const CATEGORY_OPTIONS: { label: string description: string query?: string - hidden?: boolean }[] = [ { value: SupportCategories.PROBLEM, @@ -78,13 +77,6 @@ export const CATEGORY_OPTIONS: { query: undefined, }, ]), - { - value: 'Others' as const, - label: 'Others', - description: 'Issues that are not related to any of the other categories', - query: undefined, - hidden: true, - }, ] export const SEVERITY_OPTIONS = [ diff --git a/apps/studio/components/interfaces/Support/SupportAccessToggle.tsx b/apps/studio/components/interfaces/Support/SupportAccessToggle.tsx index a5b49bc2e01..0c24ef7d63a 100644 --- a/apps/studio/components/interfaces/Support/SupportAccessToggle.tsx +++ b/apps/studio/components/interfaces/Support/SupportAccessToggle.tsx @@ -1,26 +1,26 @@ -import { ChevronRight } from 'lucide-react' -import Link from 'next/link' -import type { UseFormReturn } from 'react-hook-form' // End of third-party imports import { SupportCategories } from '@supabase/shared-types/out/constants' +import { ChevronRight } from 'lucide-react' +import Link from 'next/link' +import type { UseFormReturn } from 'react-hook-form' import { Badge, - Collapsible_Shadcn_, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, + Collapsible_Shadcn_, FormField_Shadcn_, Switch, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' + import type { ExtendedSupportCategories } from './Support.constants' import type { SupportFormValues } from './SupportForm.schema' -export const SUPPORT_ACCESS_CATEGORIES: ExtendedSupportCategories[] = [ - SupportCategories.DATABASE_UNRESPONSIVE, - SupportCategories.PERFORMANCE_ISSUES, - SupportCategories.PROBLEM, - SupportCategories.DASHBOARD_BUG, +export const DISABLE_SUPPORT_ACCESS_CATEGORIES: ExtendedSupportCategories[] = [ + SupportCategories.ACCOUNT_DELETION, + SupportCategories.SALES_ENQUIRY, + SupportCategories.REFUND, ] interface SupportAccessToggleProps { diff --git a/apps/studio/components/interfaces/Support/SupportFormV2.tsx b/apps/studio/components/interfaces/Support/SupportFormV2.tsx index 38e928934a7..26bbf789add 100644 --- a/apps/studio/components/interfaces/Support/SupportFormV2.tsx +++ b/apps/studio/components/interfaces/Support/SupportFormV2.tsx @@ -1,7 +1,4 @@ -import { useEffect, type Dispatch, type MouseEventHandler } from 'react' -import type { SubmitHandler, UseFormReturn } from 'react-hook-form' // End of third-party imports - import { SupportCategories } from '@supabase/shared-types/out/constants' import { useConstant, useFlag } from 'common' import { CLIENT_LIBRARIES } from 'common/constants' @@ -13,7 +10,10 @@ import { useGenerateAttachmentURLsMutation } from 'data/support/generate-attachm import { useDeploymentCommitQuery } from 'data/utils/deployment-commit-query' import { detectBrowser } from 'lib/helpers' import { useProfile } from 'lib/profile' +import { type Dispatch, type MouseEventHandler } from 'react' +import type { SubmitHandler, UseFormReturn } from 'react-hook-form' import { DialogSectionSeparator, Form_Shadcn_ } from 'ui' + import { AffectedServicesSelector, CATEGORIES_WITHOUT_AFFECTED_SERVICES, @@ -27,15 +27,15 @@ import { OrganizationSelector } from './OrganizationSelector' import { ProjectAndPlanInfo } from './ProjectAndPlanInfo' import { SubjectAndSuggestionsInfo } from './SubjectAndSuggestionsInfo' import { SubmitButton } from './SubmitButton' -import { SUPPORT_ACCESS_CATEGORIES, SupportAccessToggle } from './SupportAccessToggle' +import { DISABLE_SUPPORT_ACCESS_CATEGORIES, SupportAccessToggle } from './SupportAccessToggle' import type { SupportFormValues } from './SupportForm.schema' import type { SupportFormActions, SupportFormState } from './SupportForm.state' import { + NO_ORG_MARKER, + NO_PROJECT_MARKER, formatMessage, formatStudioVersion, getOrgSubscriptionPlan, - NO_ORG_MARKER, - NO_PROJECT_MARKER, } from './SupportForm.utils' import { DASHBOARD_LOG_CATEGORIES, @@ -128,12 +128,12 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo const payload = { ...values, - category, organizationSlug: values.organizationSlug ?? NO_ORG_MARKER, projectRef: values.projectRef ?? NO_PROJECT_MARKER, - allowSupportAccess: SUPPORT_ACCESS_CATEGORIES.includes(values.category) - ? values.allowSupportAccess - : false, + allowSupportAccess: + values.category && !DISABLE_SUPPORT_ACCESS_CATEGORIES.includes(values.category) + ? values.allowSupportAccess + : false, library: values.category === SupportCategories.PROBLEM && selectedLibrary !== undefined ? selectedLibrary.key @@ -179,15 +179,6 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo handleFormSubmit(event) } - useEffect(() => { - if (simplifiedSupportForm) { - form.setValue('category', 'Others') - } else { - form.setValue('category', '' as any) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [simplifiedSupportForm]) - return (
    @@ -202,14 +193,12 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo subscriptionPlanId={subscriptionPlanId} category={category} /> - {!simplifiedSupportForm && ( - - )} +
    @@ -235,7 +224,7 @@ export const SupportFormV2 = ({ form, initialError, state, dispatch }: SupportFo )} - {SUPPORT_ACCESS_CATEGORIES.includes(category) && ( + {!!category && !DISABLE_SUPPORT_ACCESS_CATEGORIES.includes(category) && ( <> diff --git a/apps/studio/components/interfaces/Support/__tests__/SupportFormPage.test.tsx b/apps/studio/components/interfaces/Support/__tests__/SupportFormPage.test.tsx index 4c85b45f3ca..9313f6d4d9c 100644 --- a/apps/studio/components/interfaces/Support/__tests__/SupportFormPage.test.tsx +++ b/apps/studio/components/interfaces/Support/__tests__/SupportFormPage.test.tsx @@ -891,10 +891,6 @@ describe('SupportFormPage', () => { await userEvent.clear(messageField) await userEvent.type(messageField, 'MFA challenge fails with an unknown error code') - expect( - screen.queryByRole('switch', { name: /allow support access to your project/i }) - ).toBeNull() - await userEvent.click(getSubmitButton(screen)) await waitFor(() => { @@ -910,7 +906,7 @@ describe('SupportFormPage', () => { organizationSlug: 'org-2', library: '', affectedServices: '', - allowSupportAccess: false, + allowSupportAccess: true, verified: true, tags: ['dashboard-support-form'], siteUrl: 'https://project-2.supabase.dev', diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx index 297dd0d443c..eb87f92a313 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnType.tsx @@ -1,3 +1,4 @@ +import type { EnumeratedType } from 'data/enumerated-types/enumerated-types-query' import { noop } from 'lodash' import { Calendar, @@ -11,8 +12,6 @@ import { } from 'lucide-react' import Link from 'next/link' import { ReactNode, useState } from 'react' - -import type { EnumeratedType } from 'data/enumerated-types/enumerated-types-query' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, @@ -23,6 +22,7 @@ import { CommandInput_Shadcn_, CommandItem_Shadcn_, CommandList_Shadcn_, + CommandSeparator_Shadcn_, Command_Shadcn_, CriticalIcon, Input, @@ -36,6 +36,7 @@ import { TooltipTrigger, cn, } from 'ui' + import { POSTGRES_DATA_TYPES, POSTGRES_DATA_TYPE_OPTIONS, @@ -165,7 +166,7 @@ const ColumnType = ({ return (
    {showLabel && Type} - +
    - diff --git a/apps/www/components/Events/EventGridItem.tsx b/apps/www/components/Events/EventGridItem.tsx index 319d916602d..a16bee905ea 100644 --- a/apps/www/components/Events/EventGridItem.tsx +++ b/apps/www/components/Events/EventGridItem.tsx @@ -40,7 +40,13 @@ const EventGridItem = ({ event }: Props) => { fill sizes="100%" quality={100} - src={event.type === 'casestudy' ? event.thumb : `/images/blog/${event.thumb}`} + src={ + event.type === 'casestudy' || + event.thumb.startsWith('/') || + event.thumb.startsWith('http') + ? event.thumb + : `/images/blog/${event.thumb}` + } className="scale-100 object-cover overflow-hidden" alt={`${event.title} thumbnail`} /> diff --git a/apps/www/components/Solutions/PostGrid.tsx b/apps/www/components/Solutions/PostGrid.tsx index 8c6198a58fd..480576e00f1 100644 --- a/apps/www/components/Solutions/PostGrid.tsx +++ b/apps/www/components/Solutions/PostGrid.tsx @@ -1,8 +1,8 @@ -import { cn } from 'ui' -import SectionContainer from '../Layouts/SectionContainer' -import { getSortedPosts } from '~/lib/posts' import Image from 'next/image' import Link from 'next/link' +import { cn } from 'ui' +import { getSortedPosts } from '~/lib/posts' +import SectionContainer from '../Layouts/SectionContainer' interface PostGridProps { id?: string @@ -31,10 +31,14 @@ function PostGrid({ id, className, header, subheader, posts }: PostGridProps) { className="border rounded-md flex flex-col relative overflow-hidden" key={post.slug} > - {post.thumb && ( + {post.imgThumb && (
    {post.title ), }, - { - id: Solutions.convex, - text: 'Switch from Convex', - description: '', - url: '/solutions/switch-from-convex', - icon: (props: any) => ( - - - - - - ), - }, ], } diff --git a/apps/www/data/solutions/convex.tsx b/apps/www/data/solutions/convex.tsx deleted file mode 100644 index 6ead8869eb8..00000000000 --- a/apps/www/data/solutions/convex.tsx +++ /dev/null @@ -1,702 +0,0 @@ -import { cn, Image } from 'ui' -import dynamic from 'next/dynamic' -import { CubeIcon } from '@heroicons/react/outline' -import { - ArrowRight, - ArrowUpRight, - Timer, - Lock, - ShieldCheck, - Database, - Code2, - Check, - InfoIcon, - TrendingUp, - Puzzle, - DollarSign, -} from 'lucide-react' -import RealtimeLogs from 'components/Products/Functions/RealtimeLogs' - -import MainProducts from 'data/MainProducts' -import { PRODUCT_SHORTNAMES } from 'shared-data/products' -import { companyStats } from 'data/company-stats' - -const AuthVisual = dynamic(() => import('components/Products/AuthVisual')) -const FunctionsVisual = dynamic(() => import('components/Products/FunctionsVisual')) -const RealtimeVisual = dynamic(() => import('components/Products/RealtimeVisual')) - -const data = { - metadata: { - metaTitle: 'Switch from Convex to Supabase', - metaDescription: - 'Migrate from Convex to Supabase for more control, flexibility, and the power of Postgres.', - }, - heroSection: { - id: 'hero', - title: 'Moving from Convex to Supabase', - sectionContainerClassName: cn( - '[&_h1]:text-xl [&_h1]:md:!text-2xl [&_h1]:lg:!text-4xl [&_h1]:2xl:!text-5xl', - '[&_.image-container]:flex [&_.image-container]:items-center' - ), - h1: 'Ship fast. Scale forever.', - subheader: [ - <> - Supabase gives you the speed you love with the foundation you need. Spin up a backend in - minutes. Get Auth, Storage, Edge Functions, Realtime, and Vector search out of the box. - Build with AI tools like Cursor or Claude Code that already speak fluent SQL. - , - <> - Build your project in a weekend. Scale it to millions of users without switching platforms, - rewriting queries, or learning the limits of a proprietary database. - , - ], - ctas: [ - { - label: 'View documentation', - href: 'https://supabase.com/docs', - type: 'default' as any, - icon: , - }, - { - label: 'Start your project', - href: 'https://supabase.com/dashboard', - type: 'primary' as any, - icon: , - }, - ], - image: ( - Convex to Supabase illustration - ), - }, - - why: { - id: 'why-supabase', - label: '', - heading: ( - <> - Why developers choose Supabase over Convex - - ), - subheading: - 'Supabase is built on standard Postgres with everything you need out of the box: auth, storage, edge functions, realtime, and vector search—all with zero lock-in. Use any ORM, connect with any tool, and build fast with AI coding assistants like Claude Code or Cursor. Your database, your data, your choice.', - features: [ - { - id: 'postgres-first', - icon: Database, - heading: "It's just Postgres", - subheading: - "Familiar SQL, familiar model, proven platform. No proprietary query language. No artificial limits. Just standard SQL that works everywhere. Postgres powers the world's most demanding applications, and you get the same battle-tested database.", - }, - { - id: 'scale-to-millions', - icon: TrendingUp, - heading: 'Scale to millions', - subheading: - 'Real Postgres means real scale. Start small and grow to millions of users on the same database. Postgres handles everything from startups to enterprise workloads with proven scalability. Your database grows with you, no migrations needed.', - }, - { - id: 'powerful-ecosystem', - icon: Puzzle, - heading: 'A powerful ecosystem', - subheading: - 'Connect with pgAdmin, Postico, DBeaver, or your favorite tool. Use any SQL client, ORM, or database tool. Tap into the most popular Postgres extensions, including: PostGIS for geospatial, pgvector for AI, and more. The entire Postgres ecosystem is at your fingertips.', - }, - { - id: 'complete-stack', - icon: CubeIcon, - heading: 'A complete backend in the box', - subheading: - "Supabase includes Auth, Storage, Edge Functions, Realtime, and Vector search. Everything is fully integrated and ready to use. Stop stitching together services from different providers. Stop paying for tools that don't talk to each other. One platform, one bill, one dashboard.", - }, - { - id: 'ultra-flexible', - icon: Code2, - heading: 'Ultra flexible by design', - subheading: - "Don't want to use our SDKs? Use any Postgres client. Don't need RLS? Don't use it. Want to use your own auth? Go ahead. Connect to external data sources with Foreign Data Wrappers. Supabase adapts to your workflow, not the other way around.", - }, - { - id: 'ai-optimized', - icon: Timer, - heading: 'Ready to move fast with AI', - subheading: - 'Perfect for teams using Claude Code, Cursor, or other AI tools. Declare your schemas in code and deploy them to your project. Generate types on the fly.', - }, - { - id: 'analytics-etl', - icon: ArrowRight, - heading: 'Built-in ETL for analytics', - subheading: - 'Stream changes from Postgres to BigQuery, Iceberg, or other analytical destinations in real time. No third-party tools needed—just configure a publication and start replicating. Keep your app fast while unlocking powerful analytics.', - }, - { - id: 'predictable-pricing', - icon: DollarSign, - heading: 'Fair, predictable pricing', - subheading: - "Supabase charges for what you use, not how your data syncs. No surprise bandwidth bills from real-time subscriptions. No per-function-call pricing that explodes with traffic. Postgres connection pooling means efficient resource usage at any scale. Know what you'll pay before you ship.", - }, - ], - }, - platform: { - id: 'postgres-platform', - title: ( - <> - A complete backend platform with zero lock-in - - ), - subheading: - "Standard Postgres plus auth, storage, functions, and real-time. Use what you need, skip what you don't.", - features: [ - { - id: 'database', - title: 'Database', - isDatabase: true, - icon: MainProducts[PRODUCT_SHORTNAMES.DATABASE].icon, - subheading: ( - <> - A fully managed Postgres database. -
    No forks: 100% pure Postgres. - - ), - className: 'lg:col-span-2 flex-col lg:flex-row', - image: ( -
    -
    - - - - - - - - - {[ - { name: 'Jon Meyers', pub: 'All', active: false }, - { name: 'Chris Martin', pub: 'All', active: true }, - { name: 'Amy Quek', pub: 'No', active: false }, - { name: 'Riccardo Bussetti', pub: 'No', active: false }, - { name: 'Beng Eu', pub: 'All', active: false }, - { name: 'Tyler Hillery', pub: 'All', active: false }, - ].map((row) => ( - - - - - ))} - -
    - NAME - - PUBLICATION -
    {row.name}{row.pub}
    -
    -
    -
    - ), - highlights: ( -
      -
    • - 100% portable -
    • -
    • - Built-in Auth with RLS -
    • -
    • - Easy to extend -
    • -
    - ), - }, - { - id: 'authentication', - title: 'Authentication', - icon: MainProducts[PRODUCT_SHORTNAMES.AUTHENTICATION].icon, - subheading: ( - <> - Secure authentication with email/password, magic links, OAuth (Google, GitHub, Twitter, - etc.), SAML, SSO, and phone/SMS OTP. - - ), - className: '!border-l-0 sm:!border-l sm:!border-t-0', - image: , - }, - { - id: 'rbac', - title: 'Role-Based Access Control', - icon: 'M17.6874 22.888V20.3886C17.6874 17.5888 15.4178 15.3192 12.618 15.3192C9.8182 15.3192 7.54852 17.5888 7.54852 20.3886V22.888M21.5531 11.5235C21.8189 14.1669 20.9393 16.9038 18.9141 18.9289C18.5359 19.3072 18.1328 19.6455 17.7101 19.9438M20.8038 8.70448C20.3598 7.71036 19.7299 6.77911 18.9141 5.96334C15.3338 2.38299 9.52889 2.38299 5.94855 5.96334C4.17501 7.73687 3.28 10.0562 3.26352 12.3807M24.0875 13.1161L23.2046 12.2332C22.3264 11.355 20.9026 11.355 20.0244 12.2332L19.1415 13.1161M0.875198 10.9503L1.75809 11.8331C2.63629 12.7113 4.06012 12.7113 4.93832 11.8331L5.82121 10.9503M7.49904 20.4919C5.77226 19.4557 4.37848 17.8555 3.62143 15.8584M15.6799 12.1942C15.6799 13.9201 14.2808 15.3192 12.5549 15.3192C10.829 15.3192 9.42993 13.9201 9.42993 12.1942C9.42993 10.4683 10.829 9.06917 12.5549 9.06917C14.2808 9.06917 15.6799 10.4683 15.6799 12.1942Z', - subheading: <>Secure your data properly., - className: '!border-l-0', - image: ( - Role Based Access Control diagram - ), - }, - { - id: 'realtime', - title: 'Realtime', - icon: MainProducts[PRODUCT_SHORTNAMES.REALTIME].icon, - subheading: ( - <> - Postgres replication enables{' '} - live sync functionality for collaborative - applications. - - ), - className: '!border-l-0 sm:!border-l', - image: ( - - ), - }, - { - id: 'storage', - title: 'Storage', - icon: MainProducts[PRODUCT_SHORTNAMES.STORAGE].icon, - subheading: ( - <> - Scalable S3-compatible object storage for - managing files, images, and videos. - - ), - className: '!border-l-0 lg:!border-l', - image: ( - Storage - ), - }, - { - id: 'edge-functions', - title: 'Edge Functions', - icon: MainProducts[PRODUCT_SHORTNAMES.FUNCTIONS].icon, - subheading: ( - <>Serverless functions powered by Deno, deployed globally for low-latency execution. - ), - className: '!border-l-0 sm:!border-l lg:!border-l-0', - image: , - }, - { - id: 'vectors', - title: 'Vectors', - icon: 'M4.13477 12.8129C4.13477 14.1481 4.43245 15.4138 4.96506 16.5471M12.925 4.02271C11.5644 4.02271 10.276 4.33184 9.12614 4.88371M21.7152 12.8129C21.7152 11.4644 21.4115 10.1867 20.8688 9.0447M12.925 21.6032C14.2829 21.6032 15.5689 21.2952 16.717 20.7454M16.717 20.7454C17.2587 21.5257 18.1612 22.0366 19.1831 22.0366C20.84 22.0366 22.1831 20.6935 22.1831 19.0366C22.1831 17.3798 20.84 16.0366 19.1831 16.0366C17.5263 16.0366 16.1831 17.3798 16.1831 19.0366C16.1831 19.6716 16.3804 20.2605 16.717 20.7454ZM4.96506 16.5471C4.16552 17.086 3.63965 17.9999 3.63965 19.0366C3.63965 20.6935 4.98279 22.0366 6.63965 22.0366C8.2965 22.0366 9.63965 20.6935 9.63965 19.0366C9.63965 17.3798 8.2965 16.0366 6.63965 16.0366C6.01951 16.0366 5.44333 16.2248 4.96506 16.5471ZM9.12614 4.88371C8.58687 4.08666 7.67444 3.56274 6.63965 3.56274C4.98279 3.56274 3.63965 4.90589 3.63965 6.56274C3.63965 8.2196 4.98279 9.56274 6.63965 9.56274C8.2965 9.56274 9.63965 8.2196 9.63965 6.56274C9.63965 5.94069 9.45032 5.36285 9.12614 4.88371ZM20.8688 9.0447C21.6621 8.50486 22.1831 7.59464 22.1831 6.56274C22.1831 4.90589 20.84 3.56274 19.1831 3.56274C17.5263 3.56274 16.1831 4.90589 16.1831 6.56274C16.1831 8.2196 17.5263 9.56274 19.1831 9.56274C19.8081 9.56274 20.3884 9.37165 20.8688 9.0447Z', - subheading: ( - <> - pgvector extension for AI/ML applications, enabling fast semantic search and embedding - storage. - - ), - className: '!border-l-0 lg:!border-l', - image: ( - Vector embeddings - ), - }, - { - id: 'row-level-security', - title: 'Row Level Security', - icon: 'M20.3124 10.9373C21.1753 10.9373 21.8749 11.6369 21.8749 12.4998V12.4998C21.8749 13.3628 21.1753 14.0623 20.3124 14.0623L4.68738 14.0623C3.82443 14.0623 3.12488 13.3628 3.12488 12.4998V12.4998C3.12488 11.6369 3.82443 10.9373 4.68738 10.9373L20.3124 10.9373Z M20.3124 3.90454C21.1753 3.90454 21.8749 4.6041 21.8749 5.46704V5.46704C21.8749 6.32999 21.1753 7.02954 20.3124 7.02954L4.68738 7.02954C3.82443 7.02954 3.12488 6.32998 3.12488 5.46704V5.46704C3.12488 4.6041 3.82443 3.90454 4.68738 3.90454L20.3124 3.90454Z M20.3124 17.9701C21.1753 17.9701 21.8749 18.6696 21.8749 19.5326V19.5326C21.8749 20.3955 21.1753 21.0951 20.3124 21.0951L4.68738 21.0951C3.82443 21.0951 3.12488 20.3955 3.12488 19.5326V19.5326C3.12488 18.6696 3.82443 17.9701 4.68738 17.9701L20.3124 17.9701Z', - subheading: <>Granular access control policies to secure data at the row level., - image: ( - Row Level Security - ), - }, - ], - }, - uiSection: { - id: 'ui-components', - title: 'Pre-built UI components', - link: 'https://supabase.com/ui', - subheading: - 'A collection of components and blocks built on the shadcn/ui library that connect your front-end to your Supabase back-end via a single command.', - content: ( - Pre-built UI components - ), - }, - developerExperience: { - id: 'developer-experience', - className: '[&_h2]:!max-w-sm', - title: ( - <> - Developers can build faster with Supabase - - ), - subheading: 'Features that help developers move quickly and focus.', - features: [ - { - id: 'ai-assistant', - title: 'AI Assistant', - icon: 'M11.8949 2.39344C12.5051 1.78324 13.4944 1.78324 14.1046 2.39344L22.9106 11.1994C23.5208 11.8096 23.5208 12.7989 22.9106 13.4091L14.1046 22.2151C13.4944 22.8253 12.5051 22.8253 11.8949 22.2151L3.08892 13.4091C2.47872 12.7989 2.47872 11.8096 3.08892 11.1994L11.8949 2.39344Z M16.5408 12.3043C16.5408 14.2597 14.9556 15.8449 13.0002 15.8449C11.0448 15.8449 9.45961 14.2597 9.45961 12.3043C9.45961 10.3489 11.0448 8.76371 13.0002 8.76371C14.9556 8.76371 16.5408 10.3489 16.5408 12.3043Z', - subheading: - 'A single panel that persists across the Supabase Dashboard and maintains context across AI prompts.', - image: ( -
    -
    - - - -
    -

    AI Assistant

    - -
    -
    -
    -

    Entity: Auth

    -

    Schema:

    -

    - Issue: We have detected that you have enabled the email provider with an expiry time - of more than an hour. It is recommended to set this value to less th... -

    -
    -
    - ), - }, - { - id: 'mcp-server', - title: 'MCP Server', - icon: 'M19 5L22 2M2 22L5 19M7.5 13.5L10 11M10.5 16.5L13 14M6.3 20.3C6.52297 20.5237 6.78791 20.7013 7.07963 20.8224C7.37136 20.9435 7.68413 21.0059 8 21.0059C8.31587 21.0059 8.62864 20.9435 8.92036 20.8224C9.21209 20.7013 9.47703 20.5237 9.7 20.3L12 18L6 12L3.7 14.3C3.47626 14.523 3.29873 14.7879 3.17759 15.0796C3.05646 15.3714 2.99411 15.6841 2.99411 16C2.99411 16.3159 3.05646 16.6286 3.17759 16.9204C3.29873 17.2121 3.47626 17.477 3.7 17.7L6.3 20.3ZM12 6L18 12L20.3 9.7C20.5237 9.47703 20.7013 9.21209 20.8224 8.92036C20.9435 8.62864 21.0059 8.31587 21.0059 8C21.0059 7.68413 20.9435 7.37136 20.8224 7.07963C20.7013 6.78791 20.5237 6.52297 20.3 6.3L17.7 3.7C17.477 3.47626 17.2121 3.29873 16.9204 3.17759C16.6286 3.05646 16.3159 2.99411 16 2.99411C15.6841 2.99411 15.3714 3.05646 15.0796 3.17759C14.7879 3.29873 14.523 3.47626 14.3 3.7L12 6Z', - subheading: - 'Connect your favorite AI tools such as Cursor or Claude directly with Supabase.', - image: ( - Vector embeddings - ), - }, - { - id: 'auto-generated-apis', - title: 'Auto-generated APIs', - icon: 'M4.13477 12.8129C4.13477 14.1481 4.43245 15.4138 4.96506 16.5471M12.925 4.02271C11.5644 4.02271 10.276 4.33184 9.12614 4.88371M21.7152 12.8129C21.7152 11.4644 21.4115 10.1867 20.8688 9.0447M12.925 21.6032C14.2829 21.6032 15.5689 21.2952 16.717 20.7454M16.717 20.7454C17.2587 21.5257 18.1612 22.0366 19.1831 22.0366C20.84 22.0366 22.1831 20.6935 22.1831 19.0366C22.1831 17.3798 20.84 16.0366 19.1831 16.0366C17.5263 16.0366 16.1831 17.3798 16.1831 19.0366C16.1831 19.6716 16.3804 20.2605 16.717 20.7454ZM4.96506 16.5471C4.16552 17.086 3.63965 17.9999 3.63965 19.0366C3.63965 20.6935 4.98279 22.0366 6.63965 22.0366C8.2965 22.0366 9.63965 20.6935 9.63965 19.0366C9.63965 17.3798 8.2965 16.0366 6.63965 16.0366C6.01951 16.0366 5.44333 16.2248 4.96506 16.5471ZM9.12614 4.88371C8.58687 4.08666 7.67444 3.56274 6.63965 3.56274C4.98279 3.56274 3.63965 4.90589 3.63965 6.56274C3.63965 8.2196 4.98279 9.56274 6.63965 9.56274C8.2965 9.56274 9.63965 8.2196 9.63965 6.56274C9.63965 5.94069 9.45032 5.36285 9.12614 4.88371ZM20.8688 9.0447C21.6621 8.50486 22.1831 7.59464 22.1831 6.56274C22.1831 4.90589 20.84 3.56274 19.1831 3.56274C17.5263 3.56274 16.1831 4.90589 16.1831 6.56274C16.1831 8.2196 17.5263 9.56274 19.1831 9.56274C19.8081 9.56274 20.3884 9.37165 20.8688 9.0447Z', - subheading: - "Learn SQL when you're ready. In the meantime, Supabase generates automatic APIs to make coding a lot easier.", - image: ( - Auto Generated APIs - ), - }, - { - id: 'foreign-data-wrappers', - title: 'Foreign Data Wrappers', - icon: 'M10.2805 18.2121C11.2419 18.6711 12.3325 18.8932 13.4711 18.8084C15.2257 18.6776 16.7596 17.843 17.8169 16.6015M8.21496 8.36469C9.27117 7.14237 10.7928 6.322 12.5311 6.19248C13.7196 6.10392 14.8558 6.34979 15.8474 6.85054M17.8169 16.6015L20.5242 19.3223C22.1857 17.5141 23.1562 15.1497 23.1562 12.5005C23.1562 6.89135 18.6091 2.34424 13 2.34424C10.9595 2.34424 9.16199 2.87659 7.57035 3.91232C8.35717 3.56865 9.22613 3.37801 10.1396 3.37801C12.6236 3.37801 14.7783 4.78762 15.8474 6.85054M17.8169 16.6015V16.6015C16.277 15.059 16.3448 12.5527 16.5387 10.3817C16.5557 10.191 16.5644 9.99794 16.5644 9.80282C16.5644 8.73844 16.3056 7.73451 15.8474 6.85054M13 22.6567C7.39086 22.6567 2.84375 18.1096 2.84375 12.5005C2.84375 9.84123 3.8026 7.48969 5.4753 5.67921L8.21496 8.42354V8.42354C9.76942 9.98064 9.69844 12.5133 9.51947 14.7062C9.50526 14.8803 9.49802 15.0564 9.49802 15.2341C9.49802 18.7705 12.3648 21.6373 15.9012 21.6373C16.8116 21.6373 17.6776 21.4473 18.4618 21.1048C16.8609 22.1588 15.06 22.6567 13 22.6567Z', - subheading: - 'Connect Supabase to Redshift, BigQuery, MySQL, and external APIs for seamless integrations.', - image: ( - Foreign Data Wrappers - ), - }, - { - id: 'instant-deployment', - title: 'Instant and secure deployment', - icon: 'M12.5 1.5625C6.45939 1.5625 1.5625 6.45939 1.5625 12.5C1.5625 18.5406 6.45939 23.4375 12.5 23.4375C18.5406 23.4375 23.4375 18.5406 23.4375 12.5C23.4375 9.90692 22.5351 7.52461 21.0273 5.64995L11.6145 15.0627L9.61957 13.0677M12.6068 5.82237C8.92939 5.82237 5.94826 8.80351 5.94826 12.4809C5.94826 16.1583 8.92939 19.1395 12.6068 19.1395C16.2842 19.1395 19.2654 16.1583 19.2654 12.4809C19.2654 11.1095 18.8507 9.83483 18.14 8.77557', - subheading: ( - <> - No need to set up servers, manage DevOps, or - tweak security settings. - - ), - - image: ( - <> - background grid -
    - Postgres slonik elephant - - ), - }, - { - id: 'observability', - title: 'Observability', - icon: 'M11.1404 7.66537C11.1404 5.18146 13.1541 3.16785 15.638 3.16785H17.3775C19.8614 3.16785 21.875 5.18146 21.875 7.66537V17.3776C21.875 19.8615 19.8614 21.8751 17.3775 21.8751H15.638C13.1541 21.8751 11.1404 19.8615 11.1404 17.3776V7.66537Z M3.125 14.7821C3.125 13.4015 4.24419 12.2823 5.62477 12.2823C7.00536 12.2823 8.12454 13.4015 8.12454 14.7821V19.3754C8.12454 20.7559 7.00536 21.8751 5.62477 21.8751C4.24419 21.8751 3.125 20.7559 3.125 19.3754V14.7821Z M3.125 5.58522C3.125 4.20463 4.24419 3.08545 5.62477 3.08545C7.00536 3.08545 8.12454 4.20463 8.12454 5.58522V6.95164C8.12454 8.33223 7.00536 9.45142 5.62477 9.45142C4.24419 9.45142 3.125 8.33223 3.125 6.95164V5.58522Z', - subheading: - 'Built-in logs, query performance tools, and security insights for easy debugging.', - image: ( - - ), - }, - ], - }, - resultsSection: { - id: 'results', - heading: ( - <> - Top performance, -
    - at any scale - - ), - subheading: - "Supabase ensures optimal database performance at any scale, so you can focus on innovating and growing without worrying about infrastructure limitations — whether you're handling high-traffic applications, complex queries, or massive data volumes.", - highlights: [ - { - heading: companyStats.databasesManaged.label, - subheading: companyStats.databasesManaged.text, - }, - { - heading: companyStats.databasesLaunchedDaily.label, - subheading: companyStats.databasesLaunchedDaily.text, - }, - ], - }, - featureGrid: { - id: 'database-features', - features: [ - { - id: 'postgres-core', - title: 'Postgres at its core', - description: ( - <> - ACID-compliant, battle-tested database{' '} - trusted by enterprises and startups. - - ), - icon: 'M13.2689 14.9229C14.04 16.494 15.6379 17.4892 17.3881 17.4893H22.0892C22.4726 17.4893 22.7843 17.8003 22.7845 18.1836C22.7845 18.5671 22.4728 18.8789 22.0892 18.8789H20.1664C20.1564 21.0605 18.171 22.4853 16.0052 22.4854C14.044 22.4854 12.4009 21.1292 11.9603 19.3037L11.9213 19.126L11.9086 18.9854C11.9116 18.6624 12.1408 18.3748 12.4701 18.3105C12.7994 18.2463 13.1203 18.4265 13.2445 18.7246L13.2845 18.8594L13.3412 19.0947C13.6746 20.251 14.742 21.0967 16.0052 21.0967C17.6551 21.0966 18.7655 20.0649 18.7758 18.8789H17.3881C15.108 18.8788 13.0263 17.5811 12.0218 15.5342L13.2689 14.9229ZM18.7767 15.6787V11.4639C18.7766 8.09738 16.0476 5.36816 12.681 5.36816H11.7269C11.7032 5.36816 11.6797 5.36364 11.6566 5.36133H7.15564C6.5783 5.36133 6.05835 5.69927 5.82068 6.21777L5.77673 6.32422L4.26404 10.4443C4.03486 11.0686 4.21563 11.7696 4.71814 12.2051L5.75622 13.1045L5.93298 13.2754C6.32193 13.694 6.54138 14.2468 6.54138 14.8242V16.4775L6.5531 16.7227C6.67574 17.9298 7.69544 18.8721 8.93493 18.8721C9.2213 18.8721 9.45986 18.6685 9.51501 18.3984L9.52771 18.2793V10.9121C9.52772 9.33737 10.1566 7.82755 11.2748 6.71875L11.3842 6.63086C11.6543 6.45411 12.0199 6.48475 12.2562 6.72266C12.5263 6.995 12.5247 7.43503 12.2523 7.70508L12.097 7.86816C11.3396 8.69814 10.9164 9.78304 10.9164 10.9121V18.2793L10.9056 18.4814C10.8044 19.4807 9.96094 20.2607 8.93493 20.2607C6.91113 20.2607 5.25814 18.6714 5.15661 16.6729L5.15173 16.4775V14.8242C5.15173 14.5993 5.06693 14.3838 4.9154 14.2207L4.84607 14.1543L3.80798 13.2549C2.86934 12.4414 2.53223 11.1318 2.96033 9.96582L4.47302 5.84473L4.55798 5.63867C5.02039 4.62971 6.03224 3.97266 7.15564 3.97266H11.8246V3.97949H12.681C16.8146 3.97949 20.1662 7.33032 20.1664 11.4639V15.6787C20.1664 16.0622 19.8546 16.373 19.4711 16.373C19.0877 16.3728 18.7767 16.0621 18.7767 15.6787ZM12.3392 14.6055C12.6835 14.4365 13.1 14.5785 13.2689 14.9229L12.0218 15.5342C11.8532 15.1901 11.9953 14.7745 12.3392 14.6055Z M14.4779 10.7135C14.4779 11.1278 14.8137 11.4635 15.2279 11.4635C15.6421 11.4635 15.9779 11.1278 15.9779 10.7135C15.9779 10.2993 15.6421 9.96354 15.2279 9.96354C14.8137 9.96354 14.4779 10.2993 14.4779 10.7135Z', - iconNoStroke: true, - }, - { - id: 'scaling', - title: 'Horizontal & Vertical Scaling', - description: ( - <> - Scale compute and storage independently, including support for{' '} - read replicas. - - ), - icon: 'M14.2847 11.1404V7.8447C14.2847 5.36078 12.2711 3.34717 9.7872 3.34717H7.84476C5.36084 3.34717 3.34723 5.36078 3.34723 7.8447V9.78714C3.34723 12.2711 5.36084 14.2847 7.84476 14.2847H11.1253M8.63752 8.65306L18.4524 18.468M19.1282 14.068V16.5986C19.1282 17.8405 18.1214 18.8474 16.8794 18.8474H14.2847M15.6573 22.0972H17.5997C20.0836 22.0972 22.0972 20.0836 22.0972 17.5996V15.6572C22.0972 13.1733 20.0836 11.1597 17.5997 11.1597H15.6573C13.1733 11.1597 11.1597 13.1733 11.1597 15.6572V17.5996C11.1597 20.0836 13.1733 22.0972 15.6573 22.0972Z', - }, - { - id: 'multi-region', - title: 'Multi-region Deployments', - description: ( - <> - Deploy databases across multiple regions for{' '} - global availability. - - ), - icon: 'M8.48462 3.05339C6.79298 3.58819 5.33457 4.64831 4.30037 6.0436C3.4029 7.25444 2.82613 8.71636 2.69516 10.306H6.77142C6.83771 8.01994 7.22916 5.93809 7.84745 4.36313C8.03485 3.88578 8.24723 3.44433 8.48462 3.05339ZM10.9999 1.27832C7.79633 1.27832 4.95467 2.82842 3.18457 5.21656C1.98658 6.83284 1.2778 8.83471 1.2778 11.0001C1.2778 13.1781 1.99476 15.1906 3.20527 16.8117C4.97675 19.1842 7.80877 20.7225 10.9999 20.7225C14.191 20.7225 17.023 19.1841 18.7944 16.8117C20.005 15.1906 20.722 13.1781 20.722 11.0001C20.722 8.83471 20.0132 6.83284 18.8152 5.21656L18.7944 5.18864C17.0229 2.81635 14.1909 1.27832 10.9999 1.27832ZM10.9999 2.66721C10.768 2.66721 10.4732 2.78413 10.1294 3.15462C9.78466 3.52602 9.44227 4.10142 9.14028 4.87067C8.596 6.2571 8.22699 8.16013 8.16092 10.306H13.8389C13.7728 8.16013 13.4038 6.2571 12.8595 4.87067C12.5575 4.10142 12.2151 3.52602 11.8704 3.15462C11.5265 2.78413 11.2318 2.66721 10.9999 2.66721ZM15.2284 10.306C15.1621 8.01994 14.7706 5.93809 14.1523 4.36313C13.9649 3.88578 13.7525 3.44433 13.5152 3.05339C15.1971 3.58512 16.6485 4.63618 17.6816 6.01966L17.6994 6.0436C18.5969 7.25443 19.1737 8.71636 19.3046 10.306H15.2284ZM13.8389 11.6949H8.16092C8.22699 13.8407 8.596 15.7437 9.14028 17.1301C9.44227 17.8994 9.78466 18.4748 10.1294 18.8462C10.4732 19.2167 10.768 19.3336 10.9999 19.3336C11.2318 19.3336 11.5265 19.2167 11.8704 18.8462C12.2151 18.4748 12.5575 17.8994 12.8595 17.1301C13.4038 15.7437 13.7728 13.8407 13.8389 11.6949ZM13.5152 18.9473C13.7526 18.5564 13.965 18.115 14.1523 17.6377C14.7706 16.0627 15.1621 13.9809 15.2284 11.6949H19.3046C19.1727 13.2947 18.5892 14.7653 17.6816 15.9807C16.6485 17.3643 15.1971 18.4155 13.5152 18.9473ZM8.48458 18.9474C8.24721 18.5564 8.03484 18.115 7.84745 17.6377C7.22916 16.0627 6.83771 13.9809 6.77142 11.6949H2.6952C2.82712 13.2947 3.41061 14.7653 4.31815 15.9808C5.35126 17.3644 6.80264 18.4156 8.48458 18.9474Z', - iconNoStroke: true, - }, - { - id: 'high-availability', - title: 'High Availability Architecture', - description: ( - <> - Enterprise plans offer{' '} - automatic failover and redundancy for - mission-critical applications. - - ), - icon: 'M16.3046 3.24514C15.3004 2.91279 14.2268 2.73291 13.1111 2.73291C7.50197 2.73291 2.95486 7.28002 2.95486 12.8892C2.95486 18.4983 7.50197 23.0454 13.1111 23.0454C18.7203 23.0454 23.2674 18.4983 23.2674 12.8892C23.2674 10.5703 22.4902 8.4329 21.1822 6.72328L12.2253 15.5572L10.2303 13.5622M13.2175 6.31682C9.54013 6.31682 6.55899 9.29795 6.55899 12.4809C6.55899 16.1583 9.54013 19.1395 13.2175 19.1395C16.895 19.1395 19.8761 16.1583 19.8761 12.4809C19.8761 11.1095 19.4615 9.83483 18.7507 8.77557', - }, - { - id: 'pitr', - title: 'Point-in-Time Recovery', - description: ( - <> - Restore your database to any point in time for - disaster recovery. - - ), - icon: 'M3.3784 13.3407C3.1413 10.4689 4.12132 7.51558 6.31845 5.31845C10.2847 1.35219 16.7153 1.35219 20.6816 5.31845C24.6478 9.28471 24.6478 15.7153 20.6816 19.6816C16.7153 23.6478 10.2847 23.6478 6.31845 19.6816C5.3819 18.745 4.6665 17.671 4.17224 16.5246M0.706939 11.443L2.28117 13.0172C2.89137 13.6274 3.88069 13.6274 4.49088 13.0172L6.06512 11.443M10.761 17.5453L16.0995 17.5453C16.9625 17.5453 17.662 16.8458 17.662 15.9828V15.7328C17.662 14.8699 16.9625 14.1703 16.0995 14.1703L10.761 14.1703C9.89806 14.1703 9.1985 14.8699 9.1985 15.7328L9.1985 15.9828C9.1985 16.8458 9.89806 17.5453 10.761 17.5453ZM11.1648 14.1711L15.6537 14.1711C16.5167 14.1711 17.2162 13.4716 17.2162 12.6086L17.2162 12.3586C17.2162 11.4956 16.5167 10.7961 15.6537 10.7961L11.1648 10.7961C10.3019 10.7961 9.60234 11.4956 9.60234 12.3586L9.60234 12.6086C9.60234 13.4716 10.3019 14.1711 11.1648 14.1711ZM10.7606 10.7963L16.0991 10.7963C16.9621 10.7963 17.6616 10.0967 17.6616 9.2338V8.98375C17.6616 8.1208 16.9621 7.42125 16.0991 7.42125L10.7606 7.42125C9.89765 7.42125 9.19809 8.12081 9.19809 8.98375L9.19809 9.2338C9.19809 10.0967 9.89765 10.7963 10.7606 10.7963Z', - }, - { - id: 'backups', - title: 'Automatic Backups', - description: ( - <> - Daily backups with retention policies for added - security. - - ), - icon: 'M22.375 5.7085C22.375 7.43439 18.1777 8.8335 13 8.8335C7.82233 8.8335 3.625 7.43439 3.625 5.7085M22.375 5.7085C22.375 3.98261 18.1777 2.5835 13 2.5835C7.82233 2.5835 3.625 3.98261 3.625 5.7085M22.375 5.7085V10.1877M3.625 5.7085L3.625 20.2918C3.62434 20.9675 4.28075 21.6251 5.49583 22.166C6.71091 22.7069 8.41919 23.1019 10.3646 23.2918M3.625 13.0002C3.6235 13.5826 4.11036 14.1536 5.03066 14.6487C5.95095 15.1438 7.26805 15.5434 8.83334 15.8022M13 13.0002V17.1668M13 17.1668H17.1667M13 17.1668L15.1771 14.9897C16.0833 14.0835 17.3438 13.521 18.7292 13.521C19.9724 13.521 21.1647 14.0149 22.0437 14.8939C22.9228 15.773 23.4167 16.9653 23.4167 18.2085C23.4167 19.3016 23.0727 20.3671 22.4336 21.2539C21.7944 22.1407 20.8924 22.8039 19.8554 23.1496C18.8183 23.4952 17.6988 23.5059 16.6554 23.1799C15.612 22.854 14.6975 22.208 14.0417 21.3335', - }, - ], - }, - securitySection: { - id: 'security', - label: 'Security', - heading: 'Enterprise-grade security', - subheading: 'Supabase is built with security at its core.', - features: [ - { - icon: ShieldCheck, - heading: 'SOC 2 Type II', - subheading: 'Certified compliant with industry security standards.', - }, - { - icon: Lock, - heading: 'Row Level Security', - subheading: 'PostgreSQL native RLS policies protect your data at the database level.', - }, - { - icon: Database, - heading: 'Encrypted at rest', - subheading: 'All data is encrypted at rest and in transit.', - }, - ], - cta: { - label: 'Learn about security', - url: '/security', - }, - }, - ctaSection: { - id: 'get-started', - title: ( - <> - Start building with Supabase and{' '} - unlock the power of Postgres - - ), - primaryCta: { - label: 'Get started', - url: 'https://supabase.com/dashboard', - target: '_blank', - icon: , - }, - }, -} - -export default data diff --git a/apps/www/data/solutions/innovation-teams.tsx b/apps/www/data/solutions/innovation-teams.tsx index 68eec351eee..5e59d34608b 100644 --- a/apps/www/data/solutions/innovation-teams.tsx +++ b/apps/www/data/solutions/innovation-teams.tsx @@ -1,4 +1,15 @@ -import dynamic from 'next/dynamic' +import { CubeIcon } from '@heroicons/react/outline' +import { useBreakpoint } from 'common' +import type { SecuritySectionProps } from 'components/Enterprise/Security' +import { frameworks } from 'components/Hero/HeroFrameworks' +import RealtimeLogs from 'components/Products/Functions/RealtimeLogs' +import type { DXSectionProps } from 'components/Solutions/DeveloperExperienceSection' +import type { FeatureGridProps } from 'components/Solutions/FeatureGrid' +import type { MPCSectionProps } from 'components/Solutions/MPCSection' +import type { PlatformSectionProps } from 'components/Solutions/PlatformSection' +import type { ResultsSectionProps } from 'components/Solutions/ResultsSection' +import { companyStats } from 'data/company-stats' +import { useSendTelemetryEvent } from 'lib/telemetry' import { ArrowLeftRight, Check, @@ -14,47 +25,85 @@ import { ShieldCheck, Sparkles, Timer, - Users, UserX, + Users, } from 'lucide-react' -import { CubeIcon } from '@heroicons/react/outline' +import dynamic from 'next/dynamic' +import { PRODUCT_SHORTNAMES } from 'shared-data/products' import { Image } from 'ui' import MainProducts from '../MainProducts' -import { TwoColumnsSectionProps } from '~/components/Solutions/TwoColumnsSection' -import RealtimeLogs from 'components/Products/Functions/RealtimeLogs' -import { frameworks } from 'components/Hero/HeroFrameworks' - -import type { DXSectionProps } from 'components/Solutions/DeveloperExperienceSection' -import type { ResultsSectionProps } from 'components/Solutions/ResultsSection' -import type { PlatformSectionProps } from 'components/Solutions/PlatformSection' import { + type FeaturesSection, FrameworkLink, type FrameworkLinkProps, - type FeaturesSection, type HeroSection, type Metadata, getEditors, } from './solutions.utils' -import type { FeatureGridProps } from 'components/Solutions/FeatureGrid' -import type { SecuritySectionProps } from 'components/Enterprise/Security' -import type { MPCSectionProps } from 'components/Solutions/MPCSection' - -import { PRODUCT_SHORTNAMES } from 'shared-data/products' -import { useBreakpoint } from 'common' -import { useSendTelemetryEvent } from 'lib/telemetry' -import { companyStats } from 'data/company-stats' +import { TwoColumnsSectionProps } from '~/components/Solutions/TwoColumnsSection' const AuthVisual = dynamic(() => import('components/Products/AuthVisual')) const FunctionsVisual = dynamic(() => import('components/Products/FunctionsVisual')) const RealtimeVisual = dynamic(() => import('components/Products/RealtimeVisual')) +interface Quote { + text: string + author: string + role: string + logo?: string | JSX.Element + link?: string + avatar?: string | JSX.Element +} + +interface AIBuilderEcosystemSection { + id: string + heading: React.ReactNode + subheading: string + builders: Array<{ + name: string + description: string + }> +} + +interface CustomerEvidenceSection { + id: string + heading: React.ReactNode + customers: Array<{ + name: string + highlights: string[] + cta?: { + label: string + href: string + } + }> +} + +interface InnovationEnablementSection { + id: string + heading: React.ReactNode + options: Array<{ + title: string + type: string + description: string + cta: { + label: string + href: string + } + }> +} + const data: () => { metadata: Metadata heroSection: HeroSection + quote: Quote + secondaryQuote?: Quote + aiBuilderEcosystem: AIBuilderEcosystemSection why: FeaturesSection + customerEvidence: CustomerEvidenceSection platform: PlatformSectionProps developerExperience: DXSectionProps + innovationEnablement: InnovationEnablementSection platformStarterSection: TwoColumnsSectionProps mcp: MPCSectionProps } = () => { @@ -64,57 +113,116 @@ const data: () => { return { metadata: { - metaTitle: 'Supabase for Developers', - metaDescription: 'Build fast. Scale easily. Trust your tech stack.', + metaTitle: 'The Production Backend for AI-Built Applications | Supabase', + metaDescription: + "Supabase powers every major AI builder. Your prototypes deserve enterprise infrastructure that passes security reviews, scales with success, and doesn't blow up budgets.", }, heroSection: { id: 'hero', - title: 'Supabase for Innovation Teams', + title: + 'Supabase powers every major AI builder. Your prototypes deserve enterprise infrastructure.', h1: ( <> - Prototype fast, integrate with your data, scale when ready. + The Production Backend for AI-Built Applications ), subheader: [ - <>Prototype in an afternoon, scale to millions., <> - You need to move fast without battling red tape, complex infrastructure, or data silos. - Supabase gives you a fully managed Postgres backend with everything you need to prove your - ideas while maintaining enterprise-grade security and compliance.{' '} + Your teams are already building with Lovable, Bolt, v0, and Claude. But prototypes aren't + enough. You need production-ready infrastructure that passes security reviews, scales with + success, and doesn't blow up budgets. , ], image: undefined, ctas: [ { - label: 'Start your project', + label: 'Start Building', href: 'https://supabase.com/dashboard', type: 'primary' as any, onClick: () => sendTelemetryEvent({ action: 'start_project_button_clicked', - properties: { buttonLocation: 'Solutions: Developers page hero' }, + properties: { buttonLocation: 'Solutions: Innovation Teams page hero' }, }), }, { - label: 'Request a demo', + label: 'Request a Demo', href: 'https://supabase.com/contact/sales', type: 'default' as any, onClick: () => sendTelemetryEvent({ action: 'request_demo_button_clicked', - properties: { buttonLocation: 'Solutions: Developers page hero' }, + properties: { buttonLocation: 'Solutions: Innovation Teams page hero' }, }), }, ], }, + quote: { + text: 'We saved over a million dollars eliminating Meta Workplace. Another million replacing our vendor websites. Now our non-technical teams build production apps themselves. Supabase and Lovable made this possible.', + author: 'Seth', + role: 'Chief Innovation Officer', + logo: ( + + ), + link: '/customers/exprealty', + avatar: ( + Seth Siegler + ), + }, + aiBuilderEcosystem: { + id: 'ai-builder-ecosystem', + heading: ( + <> + Your AI Tools Already Choose Supabase + + ), + subheading: + 'Every major AI builder integrates with Supabase by default. One backend, infinite possibilities:', + builders: [ + { + name: 'Lovable & Bolt', + description: 'Full-stack applications in minutes', + }, + { + name: 'Vercel v0', + description: 'Component to production pipeline', + }, + { + name: 'Claude & Cursor', + description: 'AI-powered local development', + }, + { + name: 'Figma', + description: 'Design to database in hours', + }, + ], + }, why: { id: 'why-supabase', label: '', heading: ( <> - Why innovation teams choose Supabase + Why Enterprises Choose Supabase for{' '} + AI Development ), subheading: @@ -157,12 +265,45 @@ const data: () => { /> ), - heading: 'Scalable with confidence', + heading: 'Scale with confidence', subheading: 'Go from prototype to production on a single platform. Applications built on Supabase are ready to handle enterprise-level workloads, with features like read replicas and high-availability architecture ensuring performance and reliability as you scale.', }, ], }, + customerEvidence: { + id: 'customer-evidence', + heading: ( + <> + Real Results From Real Enterprises + + ), + customers: [ + { + name: 'eXp Realty', + logo: '/images/customers/logos/exprealty.svg', + highlights: [ + 'Saved $3M+ annually across multiple systems', + '70+ vibe-coded applications in production', + 'Non-technical real estate agents building customer-facing apps', + 'Moved entire international division off legacy systems', + ], + cta: { + label: 'Read the case study', + href: '/customers/exprealty', + }, + }, + { + name: 'Accenture Innovation Labs', + logo: '/images/customers/logos/accenture.svg', + highlights: [ + 'Prototype-to-production in hours, not months', + 'Avoiding AWS complexity and auto-shutdown policies', + 'Engineers and non-technical consultants collaborating seamlessly', + ], + }, + ], + }, platform: { id: 'postgres-platform', title: ( @@ -390,68 +531,26 @@ const data: () => { }, developerExperience: { id: 'developer-experience', - className: '[&_h2]:!max-w-sm', + className: '[&_h2]:!max-w-lg', title: ( <> - Build faster with Supabase + Built for How AI Builders Work ), - subheading: 'Features that help developers move quickly and focus.', + subheading: '', features: [ - { - id: 'ai-assistant', - title: 'AI Assistant', - icon: 'M11.8949 2.39344C12.5051 1.78324 13.4944 1.78324 14.1046 2.39344L22.9106 11.1994C23.5208 11.8096 23.5208 12.7989 22.9106 13.4091L14.1046 22.2151C13.4944 22.8253 12.5051 22.8253 11.8949 22.2151L3.08892 13.4091C2.47872 12.7989 2.47872 11.8096 3.08892 11.1994L11.8949 2.39344Z M16.5408 12.3043C16.5408 14.2597 14.9556 15.8449 13.0002 15.8449C11.0448 15.8449 9.45961 14.2597 9.45961 12.3043C9.45961 10.3489 11.0448 8.76371 13.0002 8.76371C14.9556 8.76371 16.5408 10.3489 16.5408 12.3043Z', - subheading: ( - <> - A single panel that persists across the Supabase Dashboard and maintains{' '} - context across AI prompts. - - ), - image: ( -
    -
    - - - -
    -

    AI Assistant

    - -
    -
    -
    -

    Entity: Auth

    -

    Schema:

    -

    - Issue: We have detected that you have enabled the email provider with an expiry - time of more than an hour. It is recommended to set this value to less th... -

    -
    -
    - ), - }, { id: 'mcp-server', - title: 'MCP Server', + title: 'MCP Server Integration', icon: 'M19 5L22 2M2 22L5 19M7.5 13.5L10 11M10.5 16.5L13 14M6.3 20.3C6.52297 20.5237 6.78791 20.7013 7.07963 20.8224C7.37136 20.9435 7.68413 21.0059 8 21.0059C8.31587 21.0059 8.62864 20.9435 8.92036 20.8224C9.21209 20.7013 9.47703 20.5237 9.7 20.3L12 18L6 12L3.7 14.3C3.47626 14.523 3.29873 14.7879 3.17759 15.0796C3.05646 15.3714 2.99411 15.6841 2.99411 16C2.99411 16.3159 3.05646 16.6286 3.17759 16.9204C3.29873 17.2121 3.47626 17.477 3.7 17.7L6.3 20.3ZM12 6L18 12L20.3 9.7C20.5237 9.47703 20.7013 9.21209 20.8224 8.92036C20.9435 8.62864 21.0059 8.31587 21.0059 8C21.0059 7.68413 20.9435 7.37136 20.8224 7.07963C20.7013 6.78791 20.5237 6.52297 20.3 6.3L17.7 3.7C17.477 3.47626 17.2121 3.29873 16.9204 3.17759C16.6286 3.05646 16.3159 2.99411 16 2.99411C15.6841 2.99411 15.3714 3.05646 15.0796 3.17759C14.7879 3.29873 14.523 3.47626 14.3 3.7L12 6Z', subheading: ( <> - Connect your favorite AI tools such as Cursor - or Claude directly with Supabase. + Your AI editor understands your entire database schema.{' '} + + Claude and Cursor can manage migrations, write Edge Functions, and implement RLS + policies + + —no context switching. ), image: ( @@ -461,7 +560,7 @@ const data: () => { dark: '/images/solutions/neon/mcp-server-dark.svg', light: '/images/solutions/neon/mcp-server-light.svg', }} - alt="Vector embeddings" + alt="MCP Server Integration" width={100} height={100} quality={100} @@ -469,13 +568,16 @@ const data: () => { ), }, { - id: 'auto-generated-apis', - title: 'Auto-generated APIs', + id: 'instant-apis', + title: 'Instant APIs from Your Schema', icon: 'M4.13477 12.8129C4.13477 14.1481 4.43245 15.4138 4.96506 16.5471M12.925 4.02271C11.5644 4.02271 10.276 4.33184 9.12614 4.88371M21.7152 12.8129C21.7152 11.4644 21.4115 10.1867 20.8688 9.0447M12.925 21.6032C14.2829 21.6032 15.5689 21.2952 16.717 20.7454M16.717 20.7454C17.2587 21.5257 18.1612 22.0366 19.1831 22.0366C20.84 22.0366 22.1831 20.6935 22.1831 19.0366C22.1831 17.3798 20.84 16.0366 19.1831 16.0366C17.5263 16.0366 16.1831 17.3798 16.1831 19.0366C16.1831 19.6716 16.3804 20.2605 16.717 20.7454ZM4.96506 16.5471C4.16552 17.086 3.63965 17.9999 3.63965 19.0366C3.63965 20.6935 4.98279 22.0366 6.63965 22.0366C8.2965 22.0366 9.63965 20.6935 9.63965 19.0366C9.63965 17.3798 8.2965 16.0366 6.63965 16.0366C6.01951 16.0366 5.44333 16.2248 4.96506 16.5471ZM9.12614 4.88371C8.58687 4.08666 7.67444 3.56274 6.63965 3.56274C4.98279 3.56274 3.63965 4.90589 3.63965 6.56274C3.63965 8.2196 4.98279 9.56274 6.63965 9.56274C8.2965 9.56274 9.63965 8.2196 9.63965 6.56274C9.63965 5.94069 9.45032 5.36285 9.12614 4.88371ZM20.8688 9.0447C21.6621 8.50486 22.1831 7.59464 22.1831 6.56274C22.1831 4.90589 20.84 3.56274 19.1831 3.56274C17.5263 3.56274 16.1831 4.90589 16.1831 6.56274C16.1831 8.2196 17.5263 9.56274 19.1831 9.56274C19.8081 9.56274 20.3884 9.37165 20.8688 9.0447Z', subheading: ( <> - Learn SQL when you're ready. In the meantime, - Supabase generates automatic APIs to make coding a lot easier. + AI builders need endpoints immediately.{' '} + + Every table gets REST and GraphQL APIs automatically + + . Your prompts become production APIs. ), image: ( @@ -485,7 +587,7 @@ const data: () => { dark: '/images/solutions/neon/auto-generated-apis-dark.png', light: '/images/solutions/neon/auto-generated-apis-light.png', }} - alt="Auto Generated APIs" + alt="Instant APIs" width={100} height={100} quality={100} @@ -493,41 +595,18 @@ const data: () => { ), }, { - id: 'foreign-data-wrappers', - title: 'Foreign Data Wrappers', - icon: 'M10.2805 18.2121C11.2419 18.6711 12.3325 18.8932 13.4711 18.8084C15.2257 18.6776 16.7596 17.843 17.8169 16.6015M8.21496 8.36469C9.27117 7.14237 10.7928 6.322 12.5311 6.19248C13.7196 6.10392 14.8558 6.34979 15.8474 6.85054M17.8169 16.6015L20.5242 19.3223C22.1857 17.5141 23.1562 15.1497 23.1562 12.5005C23.1562 6.89135 18.6091 2.34424 13 2.34424C10.9595 2.34424 9.16199 2.87659 7.57035 3.91232C8.35717 3.56865 9.22613 3.37801 10.1396 3.37801C12.6236 3.37801 14.7783 4.78762 15.8474 6.85054M17.8169 16.6015V16.6015C16.277 15.059 16.3448 12.5527 16.5387 10.3817C16.5557 10.191 16.5644 9.99794 16.5644 9.80282C16.5644 8.73844 16.3056 7.73451 15.8474 6.85054M13 22.6567C7.39086 22.6567 2.84375 18.1096 2.84375 12.5005C2.84375 9.84123 3.8026 7.48969 5.4753 5.67921L8.21496 8.42354V8.42354C9.76942 9.98064 9.69844 12.5133 9.51947 14.7062C9.50526 14.8803 9.49802 15.0564 9.49802 15.2341C9.49802 18.7705 12.3648 21.6373 15.9012 21.6373C16.8116 21.6373 17.6776 21.4473 18.4618 21.1048C16.8609 22.1588 15.06 22.6567 13 22.6567Z', - subheading: ( - <> - Connect Supabase to Redshift, BigQuery, MySQL - , and external APIs for seamless integrations. - - ), - image: ( - Foreign Data Wrappers - ), - }, - { - id: 'instant-deployment', - title: 'Instant and secure deployment', + id: 'dev-pipeline', + title: 'Development → Staging → Production Pipeline', icon: 'M12.5 1.5625C6.45939 1.5625 1.5625 6.45939 1.5625 12.5C1.5625 18.5406 6.45939 23.4375 12.5 23.4375C18.5406 23.4375 23.4375 18.5406 23.4375 12.5C23.4375 9.90692 22.5351 7.52461 21.0273 5.64995L11.6145 15.0627L9.61957 13.0677M12.6068 5.82237C8.92939 5.82237 5.94826 8.80351 5.94826 12.4809C5.94826 16.1583 8.92939 19.1395 12.6068 19.1395C16.2842 19.1395 19.2654 16.1583 19.2654 12.4809C19.2654 11.1095 18.8507 9.83483 18.14 8.77557', subheading: ( <> - No need to set up servers, manage DevOps, or - tweak security settings. + Start with free prototypes, graduate to production.{' '} + + One git push promotes everything: schema, functions, and security rules + + . ), - image: ( <> { ), }, - { - id: 'observability', - title: 'Observability', - icon: 'M11.1404 7.66537C11.1404 5.18146 13.1541 3.16785 15.638 3.16785H17.3775C19.8614 3.16785 21.875 5.18146 21.875 7.66537V17.3776C21.875 19.8615 19.8614 21.8751 17.3775 21.8751H15.638C13.1541 21.8751 11.1404 19.8615 11.1404 17.3776V7.66537Z M3.125 14.7821C3.125 13.4015 4.24419 12.2823 5.62477 12.2823C7.00536 12.2823 8.12454 13.4015 8.12454 14.7821V19.3754C8.12454 20.7559 7.00536 21.8751 5.62477 21.8751C4.24419 21.8751 3.125 20.7559 3.125 19.3754V14.7821Z M3.125 5.58522C3.125 4.20463 4.24419 3.08545 5.62477 3.08545C7.00536 3.08545 8.12454 4.20463 8.12454 5.58522V6.95164C8.12454 8.33223 7.00536 9.45142 5.62477 9.45142C4.24419 9.45142 3.125 8.33223 3.125 6.95164V5.58522Z', - subheading: ( - <> - Built-in logs, query performance tools, and security insights for{' '} - easy debugging. - - ), - image: ( - - ), - }, ], }, resultsSection: { @@ -680,6 +741,44 @@ const data: () => { }, ], }, + innovationEnablement: { + id: 'innovation-enablement', + heading: ( + <> + From Prototype to Production:
    {' '} + The Innovation Playbook + + ), + options: [ + { + title: 'Prototype Today', + type: 'Self-Serve', + description: 'Start free with any AI builder + Supabase', + cta: { + label: 'Create Your First App in 5 Minutes', + href: 'https://supabase.com/dashboard', + }, + }, + { + title: 'Innovation Sprint', + type: 'Guided', + description: '1-day hackathon for your team. Build 3 production-ready prototypes.', + cta: { + label: 'Contact our Growth Team', + href: '/contact/sales', + }, + }, + { + title: 'Enterprise Pilot', + type: 'Full Support', + description: '30-day program with dedicated success team', + cta: { + label: 'Talk to Sales', + href: '/contact/sales', + }, + }, + ], + }, securitySection: { id: 'security', label: 'Security', diff --git a/apps/www/layouts/comparison.tsx b/apps/www/layouts/comparison.tsx index d693c2e81d9..a5a9949371b 100644 --- a/apps/www/layouts/comparison.tsx +++ b/apps/www/layouts/comparison.tsx @@ -1,4 +1,5 @@ import authors from 'lib/authors.json' +import { MDXRemote } from 'next-mdx-remote' import { NextSeo } from 'next-seo' import Image from 'next/image' import Link from 'next/link' @@ -7,7 +8,6 @@ import React from 'react' import CTABanner from '~/components/CTABanner' import DefaultLayout from '~/components/Layouts/Default' import { generateReadingTime } from '~/lib/helpers' -import { MDXRemote } from 'next-mdx-remote' interface Props { components: React.ReactNode @@ -76,13 +76,15 @@ const LayoutComparison = ({ components, props }: Props) => { return cat }), }, - images: [ - { - url: `https://supabase.com${basePath}/images/blog/${ - props.blog.image ? props.blog.image : props.blog.thumb - }`, - }, - ], + images: (() => { + const img = props.blog.imgSocial || props.blog.imgThumb + if (!img) return [] + const url = + img.startsWith('/') || img.startsWith('http') + ? img + : `https://supabase.com${basePath}/images/blog/${img}` + return [{ url }] + })(), }} /> diff --git a/apps/www/lib/get-cms-posts.tsx b/apps/www/lib/get-cms-posts.tsx index 01dd212eeb8..3cf31b7c465 100644 --- a/apps/www/lib/get-cms-posts.tsx +++ b/apps/www/lib/get-cms-posts.tsx @@ -1,7 +1,7 @@ +import { convertRichTextToMarkdown } from './cms/convertRichTextToMarkdown' import { CMS_SITE_ORIGIN } from './constants' import { generateReadingTime } from './helpers' import { generateTocFromMarkdown } from './toc' -import { convertRichTextToMarkdown } from './cms/convertRichTextToMarkdown' // Payload API configuration const PAYLOAD_URL = CMS_SITE_ORIGIN || 'http://localhost:3030' @@ -37,7 +37,7 @@ type CMSBlogPost = { region?: string logo?: string logo_inverse?: string - thumb?: { + imgThumb?: { url: string } createdAt: string @@ -54,7 +54,7 @@ type CMSBlogPost = { }[] meta?: { title?: string | null - image?: (number | string | null) | Media + imgSocial?: (number | string | null) | Media description?: string | null } } @@ -175,7 +175,7 @@ type ProcessedPost = { username: string }> toc_depth: number - thumb: string | null + imgThumb: string | null url: string path: string isCMS: boolean @@ -183,7 +183,7 @@ type ProcessedPost = { content: string meta?: { title?: string | null - image?: (number | string | null) | Media + imgSocial?: (number | string | null) | Media description?: string | null } } @@ -413,17 +413,17 @@ async function processPostData(post: any) { post.toc_depth ? post.toc_depth : 3 ) - // Extract thumb and image URLs from the nested structure - const thumbUrl = post.thumb?.url - ? typeof post.thumb.url === 'string' && post.thumb.url.includes('http') - ? post.thumb.url - : `${PAYLOAD_URL}${post.thumb.url}` + // Extract imgThumb and imgSocial URLs from the nested structure + const imgThumbUrl = post.imgThumb?.url + ? typeof post.imgThumb.url === 'string' && post.imgThumb.url.includes('http') + ? post.imgThumb.url + : `${PAYLOAD_URL}${post.imgThumb.url}` : null - const imageUrl = post.meta?.image - ? typeof post.meta?.image === 'string' && post.meta?.image.includes('http') - ? post.meta?.image - : `${PAYLOAD_URL}${post.meta?.image}` - : thumbUrl + const imgSocialUrl = post.meta?.imgSocial + ? typeof post.meta?.imgSocial === 'string' && post.meta?.imgSocial.includes('http') + ? post.meta?.imgSocial + : `${PAYLOAD_URL}${post.meta?.imgSocial}` + : imgThumbUrl const processedData = { slug: post.slug, @@ -448,8 +448,8 @@ async function processPostData(post: any) { username: author.username || '', })) || [], toc_depth: post.toc_depth || 3, - thumb: thumbUrl, - image: imageUrl, + imgThumb: imgThumbUrl, + imgSocial: imgSocialUrl, url: `/blog/${post.slug}`, path: `/blog/${post.slug}`, isCMS: true, @@ -524,17 +524,17 @@ export async function getAllCMSPosts({ const markdownContent = convertRichTextToMarkdown(post.content) const readingTime = post.readingTime || generateReadingTime(markdownContent) - // Extract thumb and image URLs from the nested structure (handle absolute and relative) - const thumbUrl = post.thumb?.url - ? typeof post.thumb.url === 'string' && post.thumb.url.includes('http') - ? post.thumb.url - : `${PAYLOAD_URL}${post.thumb.url}` + // Extract imgThumb and imgSocial URLs from the nested structure (handle absolute and relative) + const imgThumbUrl = post.imgThumb?.url + ? typeof post.imgThumb.url === 'string' && post.imgThumb.url.includes('http') + ? post.imgThumb.url + : `${PAYLOAD_URL}${post.imgThumb.url}` : null - const imageUrl = post.meta?.image - ? typeof post.meta?.image === 'string' && post.meta?.image.includes('http') - ? post.meta?.image - : `${PAYLOAD_URL}${post.meta?.image}` - : thumbUrl + const imgSocialUrl = post.meta?.imgSocial + ? typeof post.meta?.imgSocial === 'string' && post.meta?.imgSocial.includes('http') + ? post.meta?.imgSocial + : `${PAYLOAD_URL}${post.meta?.imgSocial}` + : imgThumbUrl return { slug: post.slug || '', @@ -558,8 +558,8 @@ export async function getAllCMSPosts({ username: author.username || '', })) || [], toc_depth: post.toc_depth || 3, - thumb: thumbUrl, - image: imageUrl, + imgThumb: imgThumbUrl, + imgSocial: imgSocialUrl, url: `/blog/${post.slug || ''}`, path: `/blog/${post.slug || ''}`, isCMS: true, diff --git a/apps/www/lib/posts.tsx b/apps/www/lib/posts.tsx index 6ac170883f8..494a7c138e1 100644 --- a/apps/www/lib/posts.tsx +++ b/apps/www/lib/posts.tsx @@ -1,6 +1,6 @@ import fs from 'fs' -import path from 'path' import matter from 'gray-matter' +import path from 'path' import { generateReadingTime } from './helpers' type Directories = '_blog' | '_case-studies' | '_customers' | '_alternatives' | '_events' @@ -14,8 +14,8 @@ export type Post = { title?: string description?: string author?: string - image?: string - thumb?: string + imgSocial?: string + imgThumb?: string categories?: string[] tags?: string[] date?: string diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index bfab86bc92b..9402eb8ec42 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -3038,11 +3038,6 @@ module.exports = [ source: '/mcp', destination: '/docs/guides/getting-started/mcp', }, - { - permanent: true, - source: '/design', - destination: '/design-system', - }, // marketing @@ -3068,4 +3063,22 @@ module.exports = [ source: '/docs/guides/platform/fly-postgres', destination: '/docs/guides/database/overview', }, + + // design + { + permanent: true, + source: '/:path*', + has: [ + { + type: 'host', + value: 'supabase.design', + }, + ], + destination: 'https://supabase.com/design-system/:path*', + }, + { + permanent: true, + source: '/design', + destination: '/design-system', + }, ] diff --git a/apps/www/lib/remotePatterns.js b/apps/www/lib/remotePatterns.js index 8a60102b56a..02ff77d2b83 100644 --- a/apps/www/lib/remotePatterns.js +++ b/apps/www/lib/remotePatterns.js @@ -153,6 +153,13 @@ module.exports = [ port: '', pathname: '**', }, + // OG Edge Function + { + protocol: 'https', + hostname: 'zhfonblqamxferhoguzj.supabase.co', + port: '', + pathname: '/functions/v1/generate-og', + }, // Dynamically generated CMS patterns based on CMS_SITE_ORIGIN ...generateCMSRemotePatterns(), ] diff --git a/apps/www/pages/solutions/innovation-teams.tsx b/apps/www/pages/solutions/innovation-teams.tsx index 326b7d8d87a..5e5731aa532 100644 --- a/apps/www/pages/solutions/innovation-teams.tsx +++ b/apps/www/pages/solutions/innovation-teams.tsx @@ -1,11 +1,15 @@ import { NextPage } from 'next' import dynamic from 'next/dynamic' import { NextSeo } from 'next-seo' +import Link from 'next/link' +import { Check, ArrowRight } from 'lucide-react' import Layout from 'components/Layouts/Default' import SolutionsStickyNav from 'components/SolutionsStickyNav' +import SectionContainer from 'components/Layouts/SectionContainer' import content from 'data/solutions/innovation-teams' import { Solutions } from 'data/Solutions' +import { Button, cn } from 'ui' const ProductHeader = dynamic(() => import('components/Sections/ProductHeader2')) const SingleQuote = dynamic(() => import('components/Sections/SingleQuote')) @@ -16,6 +20,7 @@ const ResultsSection = dynamic(() => import('components/Solutions/ResultsSection const FeatureGrid = dynamic(() => import('components/Solutions/FeatureGrid')) const PlatformStarterSection = dynamic(() => import('components/Solutions/TwoColumnsSection')) const MPCSection = dynamic(() => import('components/Solutions/MPCSection')) +const SecuritySection = dynamic(() => import('components/Enterprise/Security')) const InnovationTeams: NextPage = () => { const data = (content as any)() @@ -48,6 +53,139 @@ const InnovationTeams: NextPage = () => { {...data.quote} /> )} + {data.secondaryQuote && ( + + )} + {/* AI Builder Ecosystem Section */} + {data.aiBuilderEcosystem && ( + +
    + {/* Left column - Content */} +
    +

    + {data.aiBuilderEcosystem.heading} +

    +

    + {data.aiBuilderEcosystem.subheading} +

    +
      + {data.aiBuilderEcosystem.builders.map( + (builder: { name: string; description: string }) => ( +
    • + +
      + {builder.name} + : {builder.description} +
      +
    • + ) + )} +
    +

    + No configuration. No complexity. It just works. +

    +
    + + {/* Right column - Visual */} +
    + {/* Supabase Logo */} +
    + Supabase +
    + + {/* SVG Lines */} + + + + + + + + + {/* Provider Logos Bar */} +
    + Lovable + Bolt + v0 + Figma + Tempo +
    +
    +
    +
    + )} { subheading={data.why.subheading} features={data.why.features} /> + {/* Customer Evidence Section */} + {data.customerEvidence && ( + +
    +

    + {data.customerEvidence.heading} +

    +
    +
    + {data.customerEvidence.customers.map( + (customer: { + name: string + logo?: string + highlights: string[] + cta?: { label: string; href: string } + }) => ( +
    + {customer.logo && ( +
    + )} +

    {customer.name}

    +
      + {customer.highlights.map((highlight: string, i: number) => ( +
    • + + {highlight} +
    • + ))} +
    + {customer.cta && ( + + {customer.cta.label} + + + )} +
    + ) + )} +
    + + )} { {data.featureGrid && ( )} + {/* Security Section */} + {data.securitySection && ( + + )} + {/* Innovation Enablement Section */} + {data.innovationEnablement && ( + +
    +

    + {data.innovationEnablement.heading} +

    +
    +
    + {data.innovationEnablement.options.map( + ( + option: { + title: string + type: string + description: string + cta: { label: string; href: string } + }, + index: number + ) => ( +
    +
    + + {option.type} + +

    {option.title}

    +

    + {option.description} +

    + +
    + {index < data.innovationEnablement.options.length - 1 && ( +
    + + + +
    + )} +
    + ) + )} +
    +
    + )} {data.platformStarterSection && } {data.mcp && } diff --git a/apps/www/pages/solutions/switch-from-convex.tsx b/apps/www/pages/solutions/switch-from-convex.tsx deleted file mode 100644 index 8dc78220c89..00000000000 --- a/apps/www/pages/solutions/switch-from-convex.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { NextPage } from 'next' -import dynamic from 'next/dynamic' -import { NextSeo } from 'next-seo' -import Link from 'next/link' -import { ArrowUpRight } from 'lucide-react' - -import Layout from 'components/Layouts/Default' -import SectionContainer from 'components/Layouts/SectionContainer' -import SolutionsStickyNav from 'components/SolutionsStickyNav' -import content from 'data/solutions/convex' -import { Solutions } from 'data/Solutions' - -const ProductHeader = dynamic(() => import('components/Sections/ProductHeader2')) -const FeaturesSection = dynamic(() => import('components/Solutions/FeaturesSection')) -const PlatformSection = dynamic(() => import('components/Solutions/PlatformSection')) -const DXSection = dynamic(() => import('components/Solutions/DeveloperExperienceSection')) -const ResultsSection = dynamic(() => import('components/Solutions/ResultsSection')) -const FeatureGrid = dynamic(() => import('components/Solutions/FeatureGrid')) -const Security = dynamic(() => import('components/Enterprise/Security')) -const CtaSection = dynamic(() => import('components/Solutions/CtaSection')) - -const Convex: NextPage = () => { - return ( - <> - - - - - - - - - -
    -
    -

    {content.uiSection.title}

    -

    {content.uiSection.subheading}

    -
    - - Explore - - -
    -
    -
    {content.uiSection.content}
    -
    -
    - - - - - - -
    - - ) -} - -export default Convex diff --git a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/add-supabase-mcp-server.png b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/add-supabase-mcp-server.png index 3a89472eec5..2173b70472c 100644 Binary files a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/add-supabase-mcp-server.png and b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/add-supabase-mcp-server.png differ diff --git a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-1.png b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-1.png index 0e60db4f572..4e0c81d3f5b 100644 Binary files a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-1.png and b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-1.png differ diff --git a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-2.png b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-2.png index 5560a3e925f..c3f21e461c5 100644 Binary files a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-2.png and b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-2.png differ diff --git a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-3.png b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-3.png index a88b5e2b0ec..12dafe0773f 100644 Binary files a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-3.png and b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/chatgpt-chat-3.png differ diff --git a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/og.png b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/og.png index b7581b1e888..3657d877232 100644 Binary files a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/og.png and b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/og.png differ diff --git a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/thumb.png b/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/thumb.png deleted file mode 100644 index b7581b1e888..00000000000 Binary files a/apps/www/public/images/blog/2025-12-17-building-chatgpt-apps-with-supabase/thumb.png and /dev/null differ diff --git a/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/dashboard.png b/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/dashboard.png index 56eeeaa9af5..0d8175fd05f 100644 Binary files a/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/dashboard.png and b/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/dashboard.png differ diff --git a/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/og.png b/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/og.png deleted file mode 100644 index abbb7b8b865..00000000000 Binary files a/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/og.png and /dev/null differ diff --git a/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/thumb.png b/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/thumb.png index abbb7b8b865..0752fc76021 100644 Binary files a/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/thumb.png and b/apps/www/public/images/blog/2025-12-19-stripe-sync-engine-integration/thumb.png differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/1.png b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/1.png deleted file mode 100644 index b99763cc764..00000000000 Binary files a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/1.png and /dev/null differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/1.webp b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/1.webp new file mode 100644 index 00000000000..aabbe80b8bc Binary files /dev/null and b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/1.webp differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/2.png b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/2.png deleted file mode 100644 index 6a02faee218..00000000000 Binary files a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/2.png and /dev/null differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/2.webp b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/2.webp new file mode 100644 index 00000000000..c8aa2ef65f9 Binary files /dev/null and b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/2.webp differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/3.png b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/3.png deleted file mode 100644 index b433f9f7be4..00000000000 Binary files a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/3.png and /dev/null differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/3.webp b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/3.webp new file mode 100644 index 00000000000..b17b0e4433f Binary files /dev/null and b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/3.webp differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/4.png b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/4.png deleted file mode 100644 index 4d5dd172839..00000000000 Binary files a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/4.png and /dev/null differ diff --git a/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/4.webp b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/4.webp new file mode 100644 index 00000000000..50400bd83e7 Binary files /dev/null and b/apps/www/public/images/blog/2026-01-08-introducing-trae-solo-integration/4.webp differ diff --git a/apps/www/public/images/blog/2026/security-retro/og.png b/apps/www/public/images/blog/2026/security-retro/og.png deleted file mode 100644 index c4c7d0234b6..00000000000 Binary files a/apps/www/public/images/blog/2026/security-retro/og.png and /dev/null differ diff --git a/apps/www/public/images/blog/2026/security-retro/social.png b/apps/www/public/images/blog/2026/security-retro/social.png new file mode 100644 index 00000000000..5905865ee52 Binary files /dev/null and b/apps/www/public/images/blog/2026/security-retro/social.png differ diff --git a/apps/www/public/images/blog/2026/security-retro/thumb.png b/apps/www/public/images/blog/2026/security-retro/thumb.png index 5905865ee52..c4c7d0234b6 100644 Binary files a/apps/www/public/images/blog/2026/security-retro/thumb.png and b/apps/www/public/images/blog/2026/security-retro/thumb.png differ diff --git a/apps/www/public/images/blog/avatars/conner-aldrich.jpg b/apps/www/public/images/blog/avatars/conner-aldrich.jpg index 0d845f36c3a..3ac89e8b1c3 100644 Binary files a/apps/www/public/images/blog/avatars/conner-aldrich.jpg and b/apps/www/public/images/blog/avatars/conner-aldrich.jpg differ diff --git a/apps/www/public/images/customers/logos/accenture.svg b/apps/www/public/images/customers/logos/accenture.svg new file mode 100644 index 00000000000..807a0b5e4c0 --- /dev/null +++ b/apps/www/public/images/customers/logos/accenture.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/www/public/rss.xml b/apps/www/public/rss.xml index 911f9ea2a2e..66e497ec4ad 100644 --- a/apps/www/public/rss.xml +++ b/apps/www/public/rss.xml @@ -20,6 +20,13 @@ We are releasing Agent Skills for Postgres Best Practices to help AI coding agents write high quality, correct Postgres code. Wed, 21 Jan 2026 00:00:00 -0700 + + https://supabase.com/blog/read-replicas-vs-bigger-compute + When to use Read Replicas vs. bigger compute + https://supabase.com/blog/read-replicas-vs-bigger-compute + A practical guide to diagnosing database slowdowns and choosing between vertical scaling and Read Replicas based on your workload, budget, and performance bottlenecks. + Thu, 15 Jan 2026 00:00:00 -0700 + https://supabase.com/blog/introducing-trae-solo-integration Introducing TRAE SOLO integration with Supabase @@ -2463,6 +2470,13 @@ Five days of Supabase. Thu, 25 Mar 2021 00:00:00 -0700 + + https://supabase.com/blog/postgresql-views + Postgres Views + https://supabase.com/blog/postgresql-views + Creating and using a view in PostgreSQL. + Wed, 18 Nov 2020 00:00:00 -0700 + https://supabase.com/blog/continuous-postgresql-backup-walg Continuous PostgreSQL Backups using WAL-G @@ -2498,13 +2512,6 @@ We're releasing a new version of our Supabase client with some awesome new improvements. Fri, 30 Oct 2020 00:00:00 -0700 - - https://supabase.com/blog/postgresql-views - Postgres Views - https://supabase.com/blog/postgresql-views - Creating and using a view in PostgreSQL. - Wed, 18 Nov 2020 00:00:00 -0700 - https://supabase.com/blog/case-study-monitoro Monitoro Built a Web Crawler Handling Millions of API Requests diff --git a/apps/www/types/post.ts b/apps/www/types/post.ts index 8413e1aefd9..30633e4fc78 100644 --- a/apps/www/types/post.ts +++ b/apps/www/types/post.ts @@ -15,8 +15,10 @@ export interface PostTypes { url: string } content?: string - thumb: string - image?: string + imgThumb?: string // Used by blog posts and case studies + imgSocial?: string // Used by blog posts + thumb?: string // Used by events + thumb_light?: string // Used by events (light mode variant) readingTime?: string description: string url: string @@ -110,8 +112,8 @@ export type Blog = { blog_url?: string url?: string source: string - image?: string - thumb?: string + imgSocial?: string + imgThumb?: string youtubeHero?: string launchweek?: number | string meta_title?: string @@ -145,8 +147,8 @@ export type BlogData = { blog_url?: string url?: string source: string - image?: string - thumb?: string + imgSocial?: string + imgThumb?: string youtubeHero?: string launchweek?: number | string meta_title?: string diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 67f64fdc414..e791e5b4f29 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -60,8 +60,10 @@ services: # Uncomment to use Big Query backend for analytics # NEXT_ANALYTICS_BACKEND_PROVIDER: bigquery SNIPPETS_MANAGEMENT_FOLDER: /app/snippets + EDGE_FUNCTIONS_MANAGEMENT_FOLDER: /app/edge-functions volumes: - ./volumes/snippets:/app/snippets:Z + - ./volumes/functions:/app/edge-functions:Z kong: container_name: supabase-kong diff --git a/examples/auth/quickstarts/react-native/.env.example b/examples/auth/quickstarts/react-native/.env.example new file mode 100644 index 00000000000..025c72c8abf --- /dev/null +++ b/examples/auth/quickstarts/react-native/.env.example @@ -0,0 +1,2 @@ +EXPO_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL +EXPO_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY \ No newline at end of file diff --git a/examples/auth/quickstarts/react-native/App.tsx b/examples/auth/quickstarts/react-native/App.tsx index a13a74f5339..5f3073ab25a 100644 --- a/examples/auth/quickstarts/react-native/App.tsx +++ b/examples/auth/quickstarts/react-native/App.tsx @@ -3,25 +3,27 @@ import { useState, useEffect } from 'react' import { supabase } from './lib/supabase' import Auth from './components/Auth' import { View, Text } from 'react-native' -import { User } from '@supabase/supabase-js' +import { JwtPayload } from '@supabase/supabase-js' export default function App() { - const [user, setUser] = useState(null) + const [claims, setClaims] = useState(null) useEffect(() => { - supabase.auth.getUser().then(({ data: { user } }) => { - setUser(user) + supabase.auth.getClaims().then(({ data: { claims } }) => { + setClaims(claims) }) - supabase.auth.onAuthStateChange((_event, session) => { - setUser(session?.user ?? null) + supabase.auth.onAuthStateChange(() => { + supabase.auth.getClaims().then(({ data: { claims } }) => { + setClaims(claims) + }) }) }, []) return ( - {user && {user.id}} + {claims && {claims.sub}} ) } diff --git a/examples/auth/quickstarts/react-native/app.json b/examples/auth/quickstarts/react-native/app.json index 450df5179e3..4fa99035b5f 100644 --- a/examples/auth/quickstarts/react-native/app.json +++ b/examples/auth/quickstarts/react-native/app.json @@ -13,7 +13,8 @@ }, "assetBundlePatterns": ["**/*"], "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "com.supabase.authreactnative" }, "android": { "adaptiveIcon": { diff --git a/examples/auth/quickstarts/react-native/components/Auth.tsx b/examples/auth/quickstarts/react-native/components/Auth.tsx index 553847a4ee1..bacfaa17e89 100644 --- a/examples/auth/quickstarts/react-native/components/Auth.tsx +++ b/examples/auth/quickstarts/react-native/components/Auth.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react' -import { Alert, StyleSheet, View } from 'react-native' +import { Alert, StyleSheet, View, Text, TextInput, TouchableOpacity } from 'react-native' import { supabase } from '../lib/supabase' -import { Button, Input } from '@rneui/themed' export default function Auth() { const [email, setEmail] = useState('') @@ -37,31 +36,43 @@ export default function Auth() { return ( - Email + setEmail(text)} value={email} placeholder="email@address.com" - autoCapitalize={'none'} + autoCapitalize="none" + style={styles.input} /> - Password + setPassword(text)} value={password} secureTextEntry={true} placeholder="Password" - autoCapitalize={'none'} + autoCapitalize="none" + style={styles.input} /> - +
    + ); + } + + // Show auth success (briefly before claims load) + if (authSuccess && !claims) { + return ( +
    +

    Authentication

    +

    ✓ Authentication successful!

    +

    Loading your account...

    +
    + ); + } + + // If user is logged in, show welcome screen + if (claims) { + return ( +
    +

    Welcome!

    +

    You are logged in as: {claims.email}

    + +
    + ); + } + + // Show login form + return ( +
    +

    Supabase + React

    +

    Sign in via magic link with your email below

    + + setEmail(e.target.value)} + /> + + +
    + ); +} diff --git a/examples/auth/quickstarts/react/src/index.css b/examples/auth/quickstarts/react/src/index.css new file mode 100644 index 00000000000..37e5776c841 --- /dev/null +++ b/examples/auth/quickstarts/react/src/index.css @@ -0,0 +1,59 @@ +* { + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + background-color: #f5f5f5; + margin: 0; + padding: 20px; +} + +h1 { + color: #333; + margin-bottom: 10px; +} + +p { + color: #666; + margin-bottom: 20px; +} + +form { + display: flex; + flex-direction: column; + gap: 10px; + max-width: 300px; +} + +input { + padding: 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 16px; +} + +input:focus { + outline: none; + border-color: #3ecf8e; +} + +button { + padding: 12px 24px; + background-color: #3ecf8e; + color: white; + border: none; + border-radius: 4px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.2s; +} + +button:hover { + background-color: #2db97a; +} + +button:disabled { + background-color: #ccc; + cursor: not-allowed; +} diff --git a/examples/auth/quickstarts/react/src/main.jsx b/examples/auth/quickstarts/react/src/main.jsx new file mode 100644 index 00000000000..5e8d112308a --- /dev/null +++ b/examples/auth/quickstarts/react/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + +) diff --git a/examples/auth/quickstarts/react/vite.config.js b/examples/auth/quickstarts/react/vite.config.js new file mode 100644 index 00000000000..9ffcc675746 --- /dev/null +++ b/examples/auth/quickstarts/react/vite.config.js @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eae1ffc40d9..9a1e6d88148 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,17 +10,17 @@ catalogs: specifier: ^10.26.0 version: 10.27.0 '@supabase/auth-js': - specifier: 2.93.1 - version: 2.93.1 + specifier: 2.93.2 + version: 2.93.2 '@supabase/postgrest-js': - specifier: 2.93.1 - version: 2.93.1 + specifier: 2.93.2 + version: 2.93.2 '@supabase/realtime-js': - specifier: 2.93.1 - version: 2.93.1 + specifier: 2.93.2 + version: 2.93.2 '@supabase/supabase-js': - specifier: 2.93.1 - version: 2.93.1 + specifier: 2.93.2 + version: 2.93.2 '@types/node': specifier: ^22.0.0 version: 22.13.14 @@ -440,10 +440,10 @@ importers: version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/nextjs': specifier: 'catalog:' - version: 10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) + version: 10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@tailwindcss/container-queries': specifier: ^0.1.1 version: 0.1.1(tailwindcss@3.4.1(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.9.2))) @@ -870,7 +870,7 @@ importers: version: 7.5.0 '@supabase/auth-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@supabase/mcp-server-supabase': specifier: ^0.6.2 version: 0.6.2(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76) @@ -882,7 +882,7 @@ importers: version: link:../../packages/pg-meta '@supabase/realtime-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@supabase/shared-types': specifier: 0.1.83 version: 0.1.83 @@ -891,7 +891,7 @@ importers: version: 0.1.6(encoding@0.1.13)(supports-color@8.1.1) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@tanstack/react-query': specifier: ^5.0.0 version: 5.83.0(react@18.3.1) @@ -1402,7 +1402,7 @@ importers: version: 7.4.0(@react-router/dev@7.9.6(@types/node@22.13.14)(@vitejs/plugin-rsc@0.5.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1)))(babel-plugin-macros@3.1.0)(jiti@2.5.1)(react-router@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(yaml@2.8.1))(typescript@5.9.2) '@supabase/postgrest-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@supabase/supa-mdx-lint': specifier: 0.2.6-alpha version: 0.2.6-alpha @@ -1523,10 +1523,10 @@ importers: version: 1.6.0 '@supabase/ssr': specifier: ^0.7.0 - version: 0.7.0(@supabase/supabase-js@2.93.1) + version: 0.7.0(@supabase/supabase-js@2.93.2) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@tanstack/react-router': specifier: ^1.114.27 version: 1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1637,10 +1637,10 @@ importers: version: 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/nextjs': specifier: 'catalog:' - version: 10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) + version: 10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@vercel/og': specifier: ^0.6.2 version: 0.6.2 @@ -1727,13 +1727,13 @@ importers: version: 4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) next-seo: specifier: ^6.5.0 - version: 6.5.0(next@15.5.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.5.0(next@15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nuqs: specifier: ^2.8.1 - version: 2.8.1(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 2.8.1(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) openai: specifier: ^4.75.1 version: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76) @@ -1872,10 +1872,10 @@ importers: dependencies: '@supabase/ssr': specifier: ^0.7.0 - version: 0.7.0(@supabase/supabase-js@2.93.1) + version: 0.7.0(@supabase/supabase-js@2.93.2) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -1915,7 +1915,7 @@ importers: version: 1.56.1 '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 cross-fetch: specifier: ^4.1.0 version: 4.1.0(encoding@0.1.13) @@ -1943,7 +1943,7 @@ importers: version: 0.18.5 '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 ai: specifier: 5.0.52 version: 5.0.52(zod@3.25.76) @@ -2037,10 +2037,10 @@ importers: dependencies: '@supabase/auth-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@types/dat.gui': specifier: ^0.7.12 version: 0.7.12 @@ -2521,7 +2521,7 @@ importers: version: 0.1.6(encoding@0.1.13)(supports-color@8.1.1) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.93.1 + version: 2.93.2 '@vitest/coverage-v8': specifier: ^3.2.0 version: 3.2.4(supports-color@8.1.1)(vitest@3.2.4) @@ -8914,12 +8914,12 @@ packages: resolution: {integrity: sha512-Cq3KKe+G1o7PSBMbmrgpT2JgBeyH2THHr3RdIX2MqF7AnBuspIMgtZ3ktcCgP7kZsTMvnmWymr7zZCT1zeWbMw==} engines: {node: '>=12.16'} - '@supabase/auth-js@2.93.1': - resolution: {integrity: sha512-pC0Ek4xk4z6q7A/3+UuZ/eYgfFUUQTg3DhapzrAgJnFGDJDFDyGCj6v9nIz8+3jfLqSZ3QKGe6AoEodYjShghg==} + '@supabase/auth-js@2.93.2': + resolution: {integrity: sha512-uifI5vkhvHCQjn+LUPL5QlsuDMP4oVBD5SiliREgYuTJvCkbPLOcAPGrw88q7VUX9S0J0QuJn+37hrcTITisuw==} engines: {node: '>=20.0.0'} - '@supabase/functions-js@2.93.1': - resolution: {integrity: sha512-Ott2IcIXHGupaC0nX9WNEiJAX4OdlGRu9upkkURaQHbaLdz9JuCcHxlwTERgtgjMpikbIWHfMM1M9QTQFYABiA==} + '@supabase/functions-js@2.93.2': + resolution: {integrity: sha512-reSp7yj4KmvAFfmN+N7vYsHXOIZQh9cmRBh+VrZlm7qgIIUdYmzKuD85TvFnWApqcdI2pPnuZGKWE/2B4GXT1A==} engines: {node: '>=20.0.0'} '@supabase/mcp-server-supabase@0.6.2': @@ -8939,12 +8939,12 @@ packages: resolution: {integrity: sha512-vz5gc6RKNfDVnIfRUmH2ssTMYFI0U3MYOVyQ9R4YkzOS2dKSanjC4rTEDGjlMFwGTCUPW3N3pbY7HJIW81wMyg==} engines: {node: '>=16', npm: '>=8'} - '@supabase/postgrest-js@2.93.1': - resolution: {integrity: sha512-uRKKQJBDnfi6XFNFPNMh9+u3HT2PCgp065PcMPmG7e0xGuqvLtN89QxO2/SZcGbw2y1+mNBz0yUs5KmyNqF2fA==} + '@supabase/postgrest-js@2.93.2': + resolution: {integrity: sha512-W2AWDsYwRT217II5yD3jWaX3fJjB7DwyNi2KNi4sphdUI3DKY4fP2XYVDGfeb1clEFL18gw+GBhyQb3BcpNWkw==} engines: {node: '>=20.0.0'} - '@supabase/realtime-js@2.93.1': - resolution: {integrity: sha512-2WaP/KVHPlQDjWM6qe4wOZz6zSRGaXw1lfXf4thbfvk3C3zPPKqXRyspyYnk3IhphyxSsJ2hQ/cXNOz48008tg==} + '@supabase/realtime-js@2.93.2': + resolution: {integrity: sha512-YpAmJn7DLbMeYfQilcf3f0DKoY8O8TRbTF2oRpWFzHXTlEA+YWms8fBqM13Mf7RE72ouSNKDYyf5K2pWRSHvFw==} engines: {node: '>=20.0.0'} '@supabase/shared-types@0.1.83': @@ -8958,8 +8958,8 @@ packages: peerDependencies: '@supabase/supabase-js': ^2.43.4 - '@supabase/storage-js@2.93.1': - resolution: {integrity: sha512-3KVwd4S1i1BVPL6KIywe5rnruNQXSkLyvrdiJmwnqwbCcDujQumARdGWBPesqCjOPKEU2M9ORWKAsn+2iLzquA==} + '@supabase/storage-js@2.93.2': + resolution: {integrity: sha512-abRSVClfIQn+SqpdqL7S7b3VeyS8270/o0gqmGFtiidb7Lu0COsIV6Mor/mK9xE99KYWzyd37vwYYwv/jaANhw==} engines: {node: '>=20.0.0'} '@supabase/supa-mdx-lint-darwin@0.2.6-alpha': @@ -9052,8 +9052,8 @@ packages: resolution: {integrity: sha512-TNbBLSofM6jQg3JwzO4lttd59dScTTzW4p504/OWcgRWghQLRNfxXRJJtdui83gBMLWpgeUZqvgtfYIwS1Flzw==} hasBin: true - '@supabase/supabase-js@2.93.1': - resolution: {integrity: sha512-FJTgS5s0xEgRQ3u7gMuzGObwf3jA4O5Ki/DgCDXx94w1pihLM4/WG3XFa4BaCJYfuzLxLcv6zPPA5tDvBUjAUg==} + '@supabase/supabase-js@2.93.2': + resolution: {integrity: sha512-G3bZZi6rPwXcPtyHLXQTeHKa5ADZ2UW/+hv8YhwZFwngz4TlPnR4+TeO37EwU5+d/reD02qXozOZgz+QHv1Jtg==} engines: {node: '>=20.0.0'} '@swc/helpers@0.5.15': @@ -27407,7 +27407,7 @@ snapshots: '@sentry/core@10.27.0': {} - '@sentry/nextjs@10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)': + '@sentry/nextjs@10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.38.0 @@ -27992,11 +27992,11 @@ snapshots: '@stripe/stripe-js@7.5.0': {} - '@supabase/auth-js@2.93.1': + '@supabase/auth-js@2.93.2': dependencies: tslib: 2.8.1 - '@supabase/functions-js@2.93.1': + '@supabase/functions-js@2.93.2': dependencies: tslib: 2.8.1 @@ -28031,11 +28031,11 @@ snapshots: - pg-native - supports-color - '@supabase/postgrest-js@2.93.1': + '@supabase/postgrest-js@2.93.2': dependencies: tslib: 2.8.1 - '@supabase/realtime-js@2.93.1': + '@supabase/realtime-js@2.93.2': dependencies: '@types/phoenix': 1.6.6 '@types/ws': 8.18.1 @@ -28056,12 +28056,12 @@ snapshots: - encoding - supports-color - '@supabase/ssr@0.7.0(@supabase/supabase-js@2.93.1)': + '@supabase/ssr@0.7.0(@supabase/supabase-js@2.93.2)': dependencies: - '@supabase/supabase-js': 2.93.1 + '@supabase/supabase-js': 2.93.2 cookie: 1.0.2 - '@supabase/storage-js@2.93.1': + '@supabase/storage-js@2.93.2': dependencies: iceberg-js: 0.8.1 tslib: 2.8.1 @@ -28130,13 +28130,13 @@ snapshots: '@supabase/supa-mdx-lint-win32-x64': 0.3.1 node-pty: 1.0.0 - '@supabase/supabase-js@2.93.1': + '@supabase/supabase-js@2.93.2': dependencies: - '@supabase/auth-js': 2.93.1 - '@supabase/functions-js': 2.93.1 - '@supabase/postgrest-js': 2.93.1 - '@supabase/realtime-js': 2.93.1 - '@supabase/storage-js': 2.93.1 + '@supabase/auth-js': 2.93.2 + '@supabase/functions-js': 2.93.2 + '@supabase/postgrest-js': 2.93.2 + '@supabase/realtime-js': 2.93.2 + '@supabase/storage-js': 2.93.2 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -36388,7 +36388,7 @@ snapshots: next: 16.0.11(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react: 18.3.1 - next-seo@6.5.0(next@15.5.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-seo@6.5.0(next@15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: next: 15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react: 18.3.1 @@ -36780,7 +36780,7 @@ snapshots: next: 16.0.11(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react-router: 7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - nuqs@2.8.1(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + nuqs@2.8.1(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(next@15.5.10(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: '@standard-schema/spec': 1.0.0 react: 18.3.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2ff5cdb36a1..058cc24dc7f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,10 +6,10 @@ packages: catalog: '@sentry/nextjs': ^10.26.0 - '@supabase/auth-js': 2.93.1 - '@supabase/postgrest-js': 2.93.1 - '@supabase/realtime-js': 2.93.1 - '@supabase/supabase-js': 2.93.1 + '@supabase/auth-js': 2.93.2 + '@supabase/postgrest-js': 2.93.2 + '@supabase/realtime-js': 2.93.2 + '@supabase/supabase-js': 2.93.2 '@types/node': ^22.0.0 '@types/react': ^18.3.0 '@types/react-dom': ^18.3.0 diff --git a/scripts/generateLocalEnv.js b/scripts/generateLocalEnv.js index b6fb6bd79ac..808f964d89d 100644 --- a/scripts/generateLocalEnv.js +++ b/scripts/generateLocalEnv.js @@ -31,6 +31,7 @@ const defaultEnv = { NEXT_PUBLIC_HCAPTCHA_SITE_KEY: '10000000-ffff-ffff-ffff-000000000001', NEXT_PUBLIC_NODE_ENV: 'test', SNIPPETS_MANAGEMENT_FOLDER: '../../supabase/snippets', + EDGE_FUNCTIONS_MANAGEMENT_FOLDER: '../../supabase/functions' // path relative to studio project } const environment = { ...generatedEnv, ...defaultEnv } diff --git a/turbo.json b/turbo.json index eabf2b1f0ad..693483684b0 100644 --- a/turbo.json +++ b/turbo.json @@ -3,11 +3,18 @@ "ui": "stream", "tasks": { "build": { - "dependsOn": ["^build"], - "outputs": ["dist/**", ".next/**"] + "dependsOn": [ + "^build" + ], + "outputs": [ + "dist/**", + ".next/**" + ] }, "cms#build": { - "dependsOn": ["^build"], + "dependsOn": [ + "^build" + ], "env": [ "BLOG_APP_URL", "CRON_SECRET", @@ -25,10 +32,15 @@ "S3_ENDPOINT", "VERCEL_PROJECT_PRODUCTION_URL" ], - "outputs": [".next/**", "!.next/cache/**"] + "outputs": [ + ".next/**", + "!.next/cache/**" + ] }, "design-system#build": { - "dependsOn": ["^build"], + "dependsOn": [ + "^build" + ], "env": [ "NEXT_PUBLIC_BASE_PATH", "NEXT_PUBLIC_APP_URL", @@ -41,10 +53,16 @@ "NEXT_PUBLIC_GOTRUE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY" ], - "outputs": [".next/**", "!.next/cache/**", ".contentlayer/**"] + "outputs": [ + ".next/**", + "!.next/cache/**", + ".contentlayer/**" + ] }, "studio#build": { - "dependsOn": ["^build"], + "dependsOn": [ + "^build" + ], "env": [ "ANALYZE", "NEXT_PUBLIC_SUPPORT_API_URL", @@ -133,12 +151,18 @@ "passThroughEnv": [ "CURRENT_CLI_VERSION", "VERCEL_GIT_COMMIT_SHA", - "SNIPPETS_MANAGEMENT_FOLDER" + "SNIPPETS_MANAGEMENT_FOLDER", + "EDGE_FUNCTIONS_MANAGEMENT_FOLDER" ], - "outputs": [".next/**", "!.next/cache/**"] + "outputs": [ + ".next/**", + "!.next/cache/**" + ] }, "www#build": { - "dependsOn": ["^build"], + "dependsOn": [ + "^build" + ], "env": [ "ANALYZE", "CI", @@ -210,10 +234,16 @@ "CUSTOMERIO_API_KEY", "CUSTOMERIO_APP_API_KEY" ], - "outputs": [".next/**", "!.next/cache/**", ".contentlayer/**"] + "outputs": [ + ".next/**", + "!.next/cache/**", + ".contentlayer/**" + ] }, "ui-library#build": { - "dependsOn": ["^build"], + "dependsOn": [ + "^build" + ], "env": [ "NEXT_PUBLIC_BASE_PATH", "NEXT_PUBLIC_APP_URL", @@ -235,7 +265,11 @@ "SUPABASE_MANAGEMENT_API_TOKEN", "OPENAI_API_KEY" ], - "outputs": [".next/**", "!.next/cache/**", ".contentlayer/**"] + "outputs": [ + ".next/**", + "!.next/cache/**", + ".contentlayer/**" + ] }, "lint": { "outputs": [] @@ -251,7 +285,9 @@ "cache": false }, "typecheck": { - "dependsOn": ["^typecheck"], + "dependsOn": [ + "^typecheck" + ], "outputs": [] } }