diff --git a/apps/docs/app/contributing/content.mdx b/apps/docs/app/contributing/content.mdx
index d56648eb1f263..2a5086cd2d922 100644
--- a/apps/docs/app/contributing/content.mdx
+++ b/apps/docs/app/contributing/content.mdx
@@ -181,6 +181,8 @@ Line numbers are 1-indexed and inclusive.
lines={[[1, 3], [5, -1]]}
{/* Optional, displays as a file name on the code block */}
meta="name=display/path.js"
+{/* Optional, strips TypeScript types to produce JavaScript, which you also include with another */}
+convertToJs={true}
/>
```
@@ -195,11 +197,31 @@ commit="1623aa9b95ec90e21c5bae5a0d50dcf272abe92f"
path="/relative/path/from/root.js"
lines={[[1, 3], [5, -1]]}
meta="name=display/path.js"
+convertToJs={true}
/>
```
The repo must be public, the org must be on the allow list, and the commit must be an immutable SHA (not a mutable tag or branch name).
+#### Converting TypeScript to JavaScript
+
+You can automatically strip TypeScript types from code samples to produce JavaScript using the `convertToJs` option:
+
+```mdx
+<$CodeSample
+path="/path/to/typescript-file.ts"
+lines={[[1, -1]]}
+convertToJs={true}
+/>
+```
+
+This is useful when you want to show JavaScript examples from TypeScript source files. The conversion:
+
+- Removes all TypeScript type annotations, interfaces, and type definitions
+- Converts the language identifier from `typescript` to `javascript` (or `tsx` to `jsx`)
+- Happens before any line selection or elision processing
+- Defaults to `false` to preserve TypeScript code when not specified
+
#### Multi-file code samples
Multi-file code samples use the `<$CodeTabs>` annotation:
diff --git a/apps/docs/content/guides/platform/sso.mdx b/apps/docs/content/guides/platform/sso.mdx
index a4df878167814..6553a1f39f2ff 100644
--- a/apps/docs/content/guides/platform/sso.mdx
+++ b/apps/docs/content/guides/platform/sso.mdx
@@ -13,11 +13,11 @@ Supabase offers single sign-on (SSO) as a login option to provide additional acc
-Supabase currently provides SAML SSO for [Team and Enterprise Plan customers](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below. Once completed, [contact us](https://supabase.com/dashboard/support/new?category=Login_issues&subject=Enquiry%20about%20setting%20up%20SSO&message=I%20would%20like%20to%20set%20up%20SAML%20SSO%20for%20my%20team%20and%20have%20followed%20https://supabase.com/docs/guides/platform/sso%20and%20configured%20my%20provider%20%0A%0APlease%20attach%20the%20IDP%20metadata%20in%20the%20attachments%20below) to enable SSO for your team.
+Supabase currently provides SAML SSO for [Team and Enterprise Plan customers](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below.
-## Setup and limitations
+## Supported providers
Supabase supports practically all identity providers that support the SAML 2.0 SSO protocol. We've prepared these guides for commonly used identity providers to help you get started. If you use a different provider, our support stands ready to support you.
@@ -25,18 +25,33 @@ Supabase supports practically all identity providers that support the SAML 2.0 S
- [Azure Active Directory](/docs/guides/platform/sso/azure)
- [Okta](/docs/guides/platform/sso/okta)
-Accounts signing in with SSO have certain limitations.
-The following sections outline the limitations when SSO is enabled or disabled for your team.
+Once configured, you can update your settings anytime via the [SSO tab](https://supabase.com/dashboard/org/_/sso) under **Organization Settings**.
-### Enable SSO for your team [#enable-sso]
+
+
+## Key configuration options
+
+- **Multiple domains** - You can associate one or more email domains with your SSO provider. Users with email addresses matching these domains are eligible to sign in via SSO.
+- **Auto-join** - Optionally allow users with a matching domain to be added to your organization automatically when they first sign in, without an invitation.
+- **Default role for auto-joined users** - Choose the role (e.g., `Read-only`, `Developer`, `Administrator`, `Owner`) that automatically joined users receive. Refer to [access control](/docs/guides/platform/access-control) for more information about roles.
+
+## How SSO works in Supabase
+
+When SSO is enabled for an organization:
- Organization invites are restricted to company members belonging to the same identity provider.
- Every user has an organization created by default. They can create as many projects as they want.
- An SSO user will not be able to update or reset their password since the company administrator manages their access via the identity provider.
- If an SSO user with the following email of `alice@foocorp.com` attempts to sign in with a GitHub account that uses the same email, a separate Supabase account is created and will not be linked to the SSO user's account.
-- An SSO user will not be able to see all organizations/projects created under the same identity provider. They will need to be invited to the Supabase organization first. Refer to [access control](/docs/guides/platform/access-control) for more information.
+- SSO users will only see organizations/projects they've been invited to or auto-joined into. See [access control](/docs/guides/platform/access-control) for more details.
+
+## Disabling SSO for an organization
+
+If you disable the SSO provider for an organization, **all SSO users will immediately be unable to sign in**. Before disabling SSO, ensure you have at least one non-SSO owner account to prevent being locked out.
+
+## Removing an individual SSO user's access
-### Disable SSO for your team [#disable-sso]
+To revoke access for a specific SSO user without disabling the provider entirely you may:
-- You can prevent a user's account from further access to Supabase by removing or disabling their account in your identity provider.
-- You should also remove or downgrade their permissions from any organizations inside Supabase.
+- Remove or disable the user's account in your identity provider
+- Downgrade or remove their permissions for any organizations in Supabase.
diff --git a/apps/docs/content/guides/platform/sso/azure.mdx b/apps/docs/content/guides/platform/sso/azure.mdx
index 3f3e93888ecc3..1ced76f50ddd4 100644
--- a/apps/docs/content/guides/platform/sso/azure.mdx
+++ b/apps/docs/content/guides/platform/sso/azure.mdx
@@ -5,7 +5,7 @@ description: 'Configure single sign-on with Azure AD (Microsoft Entra).'
-This feature is only available on the [Team and Enterprise Plans](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below. Once completed, [contact us](https://supabase.com/dashboard/support/new?category=Login_issues&subject=Enquiry%20about%20setting%20up%20SSO&message=I%20would%20like%20to%20set%20up%20SAML%20SSO%20for%20my%20team%20and%20have%20followed%20https://supabase.com/docs/guides/platform/sso%20and%20configured%20my%20provider%20%0A%0APlease%20attach%20the%20IDP%20metadata%20in%20the%20attachments%20below) to enable SSO for your team.
+This feature is only available on the [Team and Enterprise Plans](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below.
@@ -33,7 +33,7 @@ You'll be using the custom enterprise application setup for Supabase.
## Step 3: Fill in application details [#add-application-details]
-In the modal titled _Create your own application_, enter a display name for Supabase. This is the name your Azure AD users see when signing in to Supabase from Azure. `Supabase` works in most cases.
+In the modal titled _Create your own application_, enter a display name for Supabase. This is the name your Azure AD users will see when signing in to Supabase from Azure. `Supabase` works in most cases.
Make sure to choose the third option: _Integrate any other application you
don't find in the gallery (Non-gallery)_.
@@ -81,24 +81,73 @@ All of the correct information should automatically populate the _Basic SAML Con
Finally, click the _Save_ button to save the configuration.
-## Step 7: Obtain metadata URL and send to Supabase [#send-metadata-url]
+## Step 7: Obtain metadata URL [#idp-metadata-url]
-Supabase needs to finalize enabling single sign-on with your Azure AD application. To do this, copy and send the link under **App Federation Metadata URL** in \*section 3 **SAML Certificates\*** to your support contact and await further instructions. If you're not clear who to send this link to or need further assistance, reach out to [Supabase Support](https://supabase.help).
-
-**Do not test the login until you have heard back from the support contact.**
+Save the link under **App Federation Metadata URL** in \*section 3 **SAML Certificates\***. You will need to enter this URL later in [Step 10](#dashboard-configure-metadata).

-## Step 8: Wait for confirmation [#confirmation]
+## Step 8: Enable SSO in the Dashboard [#dashboard-enable-sso]
+
+1. Visit the [SSO tab](https://supabase.com/dashboard/org/_/sso) under the Organization Settings page. 
+
+2. Toggle **Enable Single Sign-On** to begin configuration. Once enabled, the configuration form appears. 
+
+## Step 9: Configure domains [#dashboard-configure-domain]
+
+Enter one or more domains associated with your users email addresses (e.g., `supabase.com`).
+These domains determine which users are eligible to sign in via SSO.
+
+
+
+If your organization uses more than one email domain - for example, `supabase.com` for staff and `supabase.io` for contractors - you can add multiple domains here. All listed domains will be authorized for SSO sign-in.
+
+
+
+
+
+We do not permit use of public domains like `gmail.com`, `yahoo.com`.
+
+
+
+## Step 10: Configure metadata [#dashboard-configure-metadata]
+
+Enter the metadata URL you obtained from [Step 7](#idp-metadata-url) into the Metadata URL field:
-Wait for confirmation or further instructions from your support contact at Supabase before proceeding to the next step. It usually takes us 1 business day to configure SSO for you.
+
-## Step 9: Test single sign-on [#testing]
+## Step 11: Configure attribute mapping [#dashboard-configure-attributes]
+
+Fill out the Attribute Mapping section using the **Azure** preset.
+
+
+
+## Step 12: Join organization on signup (optional) [#dashboard-configure-autojoin]
+
+By default this setting is disabled, users logging in via SSO will not be added to your organization automatically.
+
+
+
+Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO.
+
+
+
+When auto-join is enabled, you can choose the **default role** for new users:
+
+
+
+Choose a role that fits the level of access you want to grant to new members.
+
+
+
+Visit [access-control](/docs/guides/platform/access-control) documentation for details about each role.
+
+
-_Testing sign-on before your Azure AD has been registered with Supabase will not work. Make sure you've received confirmation from your support contact at Supabase as laid out in the [confirmation](#confirmation) step._
+## Step 13: Save changes and test single sign-on [#dashboard-configure-save]
-Once you’ve received confirmation from your support contact at Supabase that SSO setup has been completed for your enterprise, you can ask some of your users to sign in via their Azure AD account.
+When you click **Save changes**, your new SSO configuration is applied immediately. From that moment, any user with an email address matching one of your configured domains who visits your organization's sign-in URL will be routed through the SSO flow.
-You ask them to enter their email address on the [Sign in with SSO](https://supabase.com/dashboard/sign-in-sso) page.
+We recommend asking a few users to test signing in via their Azure AD account. They can do this by entering their email address on the [Sign in with SSO](https://supabase.com/dashboard/sign-in-sso) page.
-If sign in is not working correctly, reach out to your support contact at Supabase for further guidance.
+If SSO sign-in doesn't work as expected, contact your Supabase support representative for assistance.
diff --git a/apps/docs/content/guides/platform/sso/gsuite.mdx b/apps/docs/content/guides/platform/sso/gsuite.mdx
index 2eed8215f20a3..cfa2960348597 100644
--- a/apps/docs/content/guides/platform/sso/gsuite.mdx
+++ b/apps/docs/content/guides/platform/sso/gsuite.mdx
@@ -5,7 +5,7 @@ description: 'Configure single sign-on with Google Workspace (G Suite).'
-This feature is only available on the [Team and Enterprise Plans](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below. Once completed, [contact us](https://supabase.com/dashboard/support/new?category=Login_issues&subject=Enquiry%20about%20setting%20up%20SSO&message=I%20would%20like%20to%20set%20up%20SAML%20SSO%20for%20my%20team%20and%20have%20followed%20https://supabase.com/docs/guides/platform/sso%20and%20configured%20my%20provider%20%0A%0APlease%20attach%20the%20IDP%20metadata%20in%20the%20attachments%20below) to enable SSO for your team.
+This feature is only available on the [Team and Enterprise Plans](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below.
@@ -35,12 +35,10 @@ The information you enter here is for visibility into your Google Workspace. You
## Step 4: Download IdP metadata [#download-idp-metadata]
-This is a very important step. Click on _DOWNLOAD METADATA_ and save the file that was downloaded.
+This is a very important step. Click on _DOWNLOAD METADATA_ and save the file that was downloaded. You will need to upload this file later in [Step 10](#dashboard-configure-metadata).

-It's very important to send this file to your support contact at Supabase to complete the SSO setup process. If you're not sure where to send this file, you can always reach out to [Supabase Support](https://supabase.help).
-
**Important: Make sure the certificate as shown on screen has at least 1 year before it expires. Mark down this date in your calendar so you will be reminded that you need to update the certificate without any downtime for your users.**
## Step 5: Add service provider details [#add-service-provider-details]
@@ -63,34 +61,91 @@ Attribute mappings allow Supabase to get information about your Google Workspace
**A _Primary email_ to `email` mapping is required.** Other mappings shown below are optional and configurable depending on your Google Workspace setup. If in doubt, replicate the same config as shown.
-Share any changes, if any, from this screen with your Supabase support contact.
+Any changes you make from this screen will be used later in [Step 10: Configure Attribute Mapping](#dashboard-configure-attributes).

-## Step 7: Wait for confirmation [#confirmation]
+## Step 7: Configure user access [#configure-user-access]
-Once you’ve configured the Google Workspace app as shown above, make sure you send the [metadata file you downloaded](#download-idp-metadata) and information regarding the [attribute mapping](#configure-attribute-mappings) (if any changes are applicable) to your support contact at Supabase.
+You can configure which Google Workspace user accounts will get access to Supabase. This is important if you wish to limit access to your software engineering teams.
-This information needs to be entered into Supabase before SSO is activated end-to-end.
+You can configure this access by clicking on the _User access_ card (or down-arrow). Follow the instructions on screen.
-Wait for confirmation that this information has successfully been added to Supabase. It usually takes us 1 business day to configure this information for you.
+
-## Step 8: Configure user access [#configure-user-access]
+
-You can configure which Google Workspace user accounts will get access to Supabase. This is important if you wish to limit access to your software engineering teams.
+Changes from this step sometimes take a while to propagate across Google's systems. Wait at least 15 minutes before testing your changes.
-You can configure this access by clicking on the _User access_ card (or down-arrow). Follow the instructions on screen.
+
-Changes from this step sometimes take a while to propagate across Google’s systems. Wait at least 15 minutes before proceeding to the next step.
+## Step 8: Enable SSO in the Dashboard [#dashboard-enable-sso]
-
+1. Visit the [SSO tab](https://supabase.com/dashboard/org/_/sso) under the Organization Settings page. 
+
+2. Toggle **Enable Single Sign-On** to begin configuration. Once enabled, the configuration form appears. 
+
+## Step 9: Configure domains [#dashboard-configure-domain]
+
+Enter one or more domains associated with your users email addresses (e.g., `supabase.com`).
+These domains determine which users are eligible to sign in via SSO.
+
+
+
+If your organization uses more than one email domain - for example, `supabase.com` for staff and `supabase.io` for contractors - you can add multiple domains here. All listed domains will be authorized for SSO sign-in.
+
+
+
+
+
+We do not permit use of public domains like `gmail.com`, `yahoo.com`.
+
+
+
+## Step 10: Configure metadata [#dashboard-configure-metadata]
+
+Upload the metadata file you downloaded in [Step 6](#download-idp-metadata) into the Metadata Upload File field.
-## Step 9: Test single sign-on [#testing]
+
+
+## Step 11: Configure attribute mapping [#dashboard-configure-attributes]
+
+Enter the SAML attributes you filled out in [Step 6](#configure-attribute-mapping) into the Attribute Mapping section.
+
+
+
+
+
+If you did not customize your settings you may save some time by clicking the **G Suite** preset.
+
+
+
+## Step 12: Join organization on signup (optional) [#dashboard-configure-autojoin]
+
+By default this setting is disabled, users logging in via SSO will not be added to your organization automatically.
+
+
+
+Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO.
+
+
+
+When auto-join is enabled, you can choose the **default role** for new users:
+
+
+
+Choose a role that fits the level of access you want to grant to new members.
+
+
+
+Visit [access-control](/docs/guides/platform/access-control) documentation for details about each role.
+
+
-Once you’ve turned on access to Supabase for users in your organization, ask one of those users to help you out in testing the setup.
+## Step 13: Save changes and test single sign-on [#dashboard-configure-save]
-It often helps to ask them to log out of their Google account and log back in.
+When you click **Save changes**, your new SSO configuration is applied immediately. From that moment, any user with an email address matching one of your configured domains who visits your organization's sign-in URL will be routed through the SSO flow.
-Ask them to enter their email address in the [Sign in with SSO](https://supabase.com/dashboard/sign-in-sso) page.
+We recommend asking a few users to test signing in via their Google Workspace account. They can do this by entering their email address on the [Sign in with SSO](https://supabase.com/dashboard/sign-in-sso) page.
-If sign in is not working correctly, reach out to your support contact at Supabase.
+If SSO sign-in doesn't work as expected, contact your Supabase support representative for assistance.
diff --git a/apps/docs/content/guides/platform/sso/okta.mdx b/apps/docs/content/guides/platform/sso/okta.mdx
index d86e95d6711bd..22bf180e98785 100644
--- a/apps/docs/content/guides/platform/sso/okta.mdx
+++ b/apps/docs/content/guides/platform/sso/okta.mdx
@@ -5,7 +5,7 @@ description: 'Configure single sign-on with Okta.'
-This feature is only available on the [Team and Enterprise Plans](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below. Once completed, [contact us](https://supabase.com/dashboard/support/new?category=Login_issues&subject=Enquiry%20about%20setting%20up%20SSO&message=I%20would%20like%20to%20set%20up%20SAML%20SSO%20for%20my%20team%20and%20have%20followed%20https://supabase.com/docs/guides/platform/sso%20and%20configured%20my%20provider%20%0A%0APlease%20attach%20the%20IDP%20metadata%20in%20the%20attachments%20below) to enable SSO for your team.
+This feature is only available on the [Team and Enterprise Plans](https://supabase.com/pricing). If you are an existing Team or Enterprise Plan customer, continue with the setup below.
@@ -37,7 +37,7 @@ The information you enter here is for visibility into your Okta applications men
## Step 4: Fill out SAML settings [#add-saml-settings]
-These settings let Supabase use SAML 2.0 properly with your Okta application. Make sure you enter this information exactly as shown on in this table and screenshot.
+These settings let Supabase use SAML 2.0 properly with your Okta application. Make sure you enter this information exactly as shown on in this table.
| Setting | Value |
| ---------------------------------------------- | --------------------------------------------------- |
@@ -55,9 +55,7 @@ These settings let Supabase use SAML 2.0 properly with your Okta application. Ma
Attribute Statements allow Supabase to get information about your Okta users on each login.
-**A `email` to `user.email` statement is required.** Other mappings shown below are optional and configurable depending on your Okta setup. If in doubt, replicate the same config as shown.
-
-Share any changes, if any, from this screen with your Supabase support contact.
+**A `email` to `user.email` statement is required.** Other mappings shown below are optional and configurable depending on your Okta setup. If in doubt, replicate the same config as shown. You will use this mapping later in [Step 10](#dashboard-configure-attributes).

@@ -67,22 +65,79 @@ Supabase needs to finalize enabling single sign-on with your Okta application.
To do this scroll down to the _SAML Signing Certificates_ section on the _Sign On_ tab of the _Supabase_ application. Pick the the _SHA-2_ row with an _Active_ status. Click on the _Actions_ dropdown button and then on the _View IdP Metadata_.
-This will open up the SAML 2.0 Metadata XML file in a new tab in your browser. Copy this URL and send it to your support contact and await further instructions. If you're not clear who to send this link to or need further assistance, contact [Supabase Support](https://supabase.help).
+This will open up the SAML 2.0 Metadata XML file in a new tab in your browser. You will need to enter this URL later in [Step 9](#dashboard-configure-metadata).
The link usually has this structure: `https://.okta.com/apps//sso/saml/metadata`

-## Step 7: Wait for confirmation [#confirmation]
+## Step 7: Enable SSO in the Dashboard [#dashboard-enable-sso]
+
+1. Visit the [SSO tab](https://supabase.com/dashboard/org/_/sso) under the Organization Settings page. 
+
+2. Toggle **Enable Single Sign-On** to begin configuration. Once enabled, the configuration form appears. 
+
+## Step 8: Configure domains [#dashboard-configure-domain]
+
+Enter one or more domains associated with your users email addresses (e.g., `supabase.com`).
+These domains determine which users are eligible to sign in via SSO.
+
+
+
+If your organization uses more than one email domain - for example, `supabase.com` for staff and `supabase.io` for contractors - you can add multiple domains here. All listed domains will be authorized for SSO sign-in.
+
+
+
+
+
+We do not permit use of public domains like `gmail.com`, `yahoo.com`.
+
+
+
+## Step 9: Configure metadata [#dashboard-configure-metadata]
+
+Enter the metadata URL you obtained from [Step 6](#idp-metadata-url) into the Metadata URL field:
+
+
+
+## Step 10: Configure attribute mapping [#dashboard-configure-attributes]
+
+Enter the SAML attributes you filled out in [Step 5](#add-attribute-statements) into the Attribute Mapping section.
-Once you’ve configured the Okta app as shown above, make sure you send the [metadata URL](#idp-metadata-url) and information regarding the [attribute statements](#add-attribute-statements) (if any changes are applicable) to your support contact at Supabase.
+
-Wait for confirmation that this information has successfully been added to Supabase. It usually takes us 1 business day to configure this information for you.
+
+
+If you did not customize your settings you may save some time by clicking the **Okta** preset.
+
+
+
+## Step 11: Join organization on signup (optional) [#dashboard-configure-autojoin]
+
+By default this setting is disabled, users logging in via SSO will not be added to your organization automatically.
+
+
+
+Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO.
+
+
+
+When auto-join is enabled, you can choose the **default role** for new users:
+
+
+
+Choose a role that fits the level of access you want to grant to new members.
+
+
+
+Visit [access-control](/docs/guides/platform/access-control) documentation for details about each role.
+
+
-## Step 8: Test single sign-on [#testing]
+## Step 12: Save changes and test single sign-on [#dashboard-configure-save]
-Once you’ve received confirmation from your support contact at Supabase that SSO setup has been completed for your enterprise, you can ask some of your users to sign in via their Okta account.
+When you click **Save changes**, your new SSO configuration is applied immediately. From that moment, any user with an email address matching one of your configured domains who visits your organization's sign-in URL will be routed through the SSO flow.
-You ask them to enter their email address on the [Sign in with SSO](https://supabase.com/dashboard/sign-in-sso) page.
+We recommend asking a few users to test signing in via their Okta account. They can do this by entering their email address on the [Sign in with SSO](https://supabase.com/dashboard/sign-in-sso) page.
-If sign in is not working correctly, reach out to your support contact at Supabase for further guidance.
+If SSO sign-in doesn't work as expected, contact your Supabase support representative for assistance.
diff --git a/apps/docs/features/app.providers.tsx b/apps/docs/features/app.providers.tsx
index 63b3ace9babbf..f6c2f3089b370 100644
--- a/apps/docs/features/app.providers.tsx
+++ b/apps/docs/features/app.providers.tsx
@@ -11,7 +11,6 @@ import { QueryClientProvider } from './data/queryClient.client'
import { PageTelemetry } from './telemetry/telemetry.client'
import { ScrollRestoration } from './ui/helpers.scroll.client'
import { ThemeSandbox } from './ui/theme.client'
-import { PromoToast } from 'ui-patterns'
/**
* Global providers that wrap the entire app
@@ -28,7 +27,6 @@ function GlobalProviders({ children }: PropsWithChildren) {
-
{children}
diff --git a/apps/docs/features/directives/CodeSample.test.ts b/apps/docs/features/directives/CodeSample.test.ts
index a3ae68b0e1ec2..3c8e83df2fb3f 100644
--- a/apps/docs/features/directives/CodeSample.test.ts
+++ b/apps/docs/features/directives/CodeSample.test.ts
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, it, expect, vi } from 'vitest'
+import { stripIndent } from 'common-tags'
import { fromMarkdown } from 'mdast-util-from-markdown'
import { mdxFromMarkdown, mdxToMarkdown } from 'mdast-util-mdx'
import { toMarkdown } from 'mdast-util-to-markdown'
@@ -18,6 +19,42 @@ vi.mock('~/lib/constants', () => ({
IS_PLATFORM: true,
}))
+/**
+ * Checks if str1 contains str2, ignoring leading whitespace on each line.
+ * Lines are matched if they have the same content after trimming leading whitespace.
+ *
+ * @param str1 - The string to search in
+ * @param str2 - The string to search for
+ * @returns true if str1 contains str2 modulo leading whitespace, false otherwise
+ */
+export function containsStringIgnoringLeadingWhitespace(str1: string, str2: string): boolean {
+ const lines1 = str1.split('\n').map((line) => line.trimStart())
+ const lines2 = str2.split('\n').map((line) => line.trimStart())
+
+ if (lines2.length === 0) {
+ return true
+ }
+
+ if (lines2.length > lines1.length) {
+ return false
+ }
+
+ for (let i = 0; i <= lines1.length - lines2.length; i++) {
+ let matches = true
+ for (let j = 0; j < lines2.length; j++) {
+ if (lines1[i + j] !== lines2[j]) {
+ matches = false
+ break
+ }
+ }
+ if (matches) {
+ return true
+ }
+ }
+
+ return false
+}
+
describe('$CodeSample', () => {
beforeAll(() => {
env = process.env
@@ -533,6 +570,169 @@ Some more text.
expect(output).toEqual(expected)
})
+
+ describe('convertToJs option', () => {
+ it('should convert TypeScript to JavaScript when convertToJs is true', async () => {
+ const markdown = `
+# Embed code sample
+
+<$CodeSample path="/_internal/fixtures/typescript.ts" lines={[[1, -1]]} convertToJs={true} />
+
+Some more text.
+`.trim()
+
+ const mdast = fromMarkdown(markdown, {
+ mdastExtensions: [mdxFromMarkdown()],
+ extensions: [mdxjs()],
+ })
+ const transformed = await transformWithMock(mdast)
+ const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] })
+
+ const expected = stripIndent`
+ \`\`\`javascript
+ const users = [
+ { id: 1, name: 'John', email: 'john@example.com' },
+ { id: 2, name: 'Jane' },
+ ];
+
+ function getUserById(id) {
+ return users.find((user) => user.id === id);
+ }
+
+ function createUser(name, email) {
+ const newId = Math.max(...users.map((u) => u.id)) + 1;
+ const newUser = { id: newId, name };
+ if (email) {
+ newUser.email = email;
+ }
+ users.push(newUser);
+ return newUser;
+ }
+
+ class UserManager {
+ users = [];
+
+ constructor(initialUsers = []) {
+ this.users = initialUsers;
+ }
+
+ addUser(user) {
+ this.users.push(user);
+ }
+
+ getUsers() {
+ return [...this.users];
+ }
+ }
+ \`\`\`
+ `.trim()
+
+ expect(containsStringIgnoringLeadingWhitespace(output, expected)).toBe(true)
+ })
+
+ it('should preserve TypeScript when convertToJs is false', async () => {
+ const markdown = `
+# Embed code sample
+
+<$CodeSample path="/_internal/fixtures/typescript.ts" lines={[[1, -1]]} convertToJs={false} />
+
+Some more text.
+`.trim()
+
+ const mdast = fromMarkdown(markdown, {
+ mdastExtensions: [mdxFromMarkdown()],
+ extensions: [mdxjs()],
+ })
+ const transformed = await transformWithMock(mdast)
+ const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] })
+
+ // The output should contain TypeScript types
+ expect(output).toContain('```typescript')
+ expect(output).toContain('interface User')
+ expect(output).toContain('type Status')
+ expect(output).toContain(': User')
+ expect(output).toContain(': number')
+ expect(output).toContain(': string')
+ })
+
+ it('should preserve TypeScript when convertToJs is not specified (default)', async () => {
+ const markdown = `
+# Embed code sample
+
+<$CodeSample path="/_internal/fixtures/typescript.ts" lines={[[1, -1]]} />
+
+Some more text.
+`.trim()
+
+ const mdast = fromMarkdown(markdown, {
+ mdastExtensions: [mdxFromMarkdown()],
+ extensions: [mdxjs()],
+ })
+ const transformed = await transformWithMock(mdast)
+ const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] })
+
+ // The output should contain TypeScript types by default
+ expect(output).toContain('```typescript')
+ expect(output).toContain('interface User')
+ expect(output).toContain('type Status')
+ })
+
+ it('should convert types but preserve line selection and elision', async () => {
+ const markdown = `
+# Embed code sample
+
+<$CodeSample path="/_internal/fixtures/typescript.ts" lines={[[1, 4], [10, -1]]} convertToJs={true} />
+
+Some more text.
+`.trim()
+
+ const mdast = fromMarkdown(markdown, {
+ mdastExtensions: [mdxFromMarkdown()],
+ extensions: [mdxjs()],
+ })
+ const transformed = await transformWithMock(mdast)
+ const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] })
+
+ const expected = `
+ \`\`\`javascript
+ const users = [
+ { id: 1, name: 'John', email: 'john@example.com' },
+ { id: 2, name: 'Jane' },
+ ];
+
+ // ...
+
+ function createUser(name, email) {
+ const newId = Math.max(...users.map((u) => u.id)) + 1;
+ const newUser = { id: newId, name };
+ if (email) {
+ newUser.email = email;
+ }
+ users.push(newUser);
+ return newUser;
+ }
+
+ class UserManager {
+ users = [];
+
+ constructor(initialUsers = []) {
+ this.users = initialUsers;
+ }
+
+ addUser(user) {
+ this.users.push(user);
+ }
+
+ getUsers() {
+ return [...this.users];
+ }
+ }
+ \`\`\`
+ `.trim()
+
+ expect(containsStringIgnoringLeadingWhitespace(output, expected)).toBe(true)
+ })
+ })
})
describe('_createElidedLine', () => {
diff --git a/apps/docs/features/directives/CodeSample.ts b/apps/docs/features/directives/CodeSample.ts
index d962ca8486a63..dc505c0bda1ba 100644
--- a/apps/docs/features/directives/CodeSample.ts
+++ b/apps/docs/features/directives/CodeSample.ts
@@ -11,6 +11,7 @@
* lines={[1, 2], [5, 7]} // -1 may be used in end position as an alias for the last line, e.g., [1, -1]
* meta="utils/client.ts" // Optional, for displaying a file path on the code block
* hideElidedLines={true} // Optional, for hiding elided lines in the code block
+ * convertToJs={true} // Optional, strips TypeScript types to produce JavaScript
* />
* ```
*
@@ -26,6 +27,7 @@
* lines={[1, 2], [5, 7]} // -1 may be used in end position as an alias for the last line, e.g., [1, -1]
* meta="utils/client.ts" // Optional, for displaying a file path on the code block
* hideElidedLines={true} // Optional, for hiding elided lines in the code block
+ * convertToJs={true} // Optional, strips TypeScript types to produce JavaScript
* />
*/
@@ -33,8 +35,10 @@ import * as acorn from 'acorn'
import tsPlugin from 'acorn-typescript'
import { type DefinitionContent, type BlockContent, type Code, type Root } from 'mdast'
import type { MdxJsxAttributeValueExpression, MdxJsxFlowElement } from 'mdast-util-mdx-jsx'
+import assert from 'node:assert'
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
+import { removeTypes } from 'remove-types'
import { type Parent } from 'unist'
import { visitParents } from 'unist-util-visit-parents'
import { z, type SafeParseError } from 'zod'
@@ -69,6 +73,12 @@ type AdditionalMeta = {
codeHikeAncestorParent: Parent | null
}
+const booleanValidator = z.union([z.boolean(), z.string(), z.undefined()]).transform((v) => {
+ if (typeof v === 'boolean') return v
+ if (typeof v === 'string') return v === 'true'
+ return false
+})
+
const codeSampleExternalSchema = z.object({
external: z.coerce.boolean().refine((v) => v === true),
org: z.enum(ALLOW_LISTED_GITHUB_ORGS, {
@@ -80,6 +90,7 @@ const codeSampleExternalSchema = z.object({
lines: linesValidator,
meta: z.string().optional(),
hideElidedLines: z.coerce.boolean().default(false),
+ convertToJs: booleanValidator,
})
type ICodeSampleExternal = z.infer & AdditionalMeta
@@ -92,6 +103,7 @@ const codeSampleInternalSchema = z.object({
lines: linesValidator,
meta: z.string().optional(),
hideElidedLines: z.coerce.boolean().default(false),
+ convertToJs: booleanValidator,
})
type ICodeSampleInternal = z.infer & AdditionalMeta
@@ -114,7 +126,7 @@ interface Dependencies {
export function codeSampleRemark(deps: Dependencies) {
return async function transform(tree: Root) {
const contentMap = await fetchSourceCodeContent(tree, deps)
- rewriteNodes(contentMap)
+ await rewriteNodes(contentMap)
return tree
}
@@ -154,6 +166,7 @@ async function fetchSourceCodeContent(tree: Root, deps: Dependencies) {
const hideElidedLines = getAttributeValueExpression(
getAttributeValue(node, 'hideElidedLines')
)
+ const convertToJs = getAttributeValueExpression(getAttributeValue(node, 'convertToJs'))
const result = codeSampleExternalSchema.safeParse({
external: isExternal,
@@ -164,6 +177,7 @@ async function fetchSourceCodeContent(tree: Root, deps: Dependencies) {
lines,
meta,
hideElidedLines,
+ convertToJs,
})
if (!result.success) {
@@ -197,6 +211,7 @@ async function fetchSourceCodeContent(tree: Root, deps: Dependencies) {
const hideElidedLines = getAttributeValueExpression(
getAttributeValue(node, 'hideElidedLines')
)
+ const convertToJs = getAttributeValueExpression(getAttributeValue(node, 'convertToJs'))
const result = codeSampleInternalSchema.safeParse({
external: isExternal,
@@ -204,6 +219,7 @@ async function fetchSourceCodeContent(tree: Root, deps: Dependencies) {
lines,
meta,
hideElidedLines,
+ convertToJs,
})
if (!result.success) {
@@ -234,15 +250,30 @@ async function fetchSourceCodeContent(tree: Root, deps: Dependencies) {
return nodeContentMap
}
-function rewriteNodes(contentMap: Map) {
+async function rewriteNodes(contentMap: Map) {
for (const [node, [meta, content]] of contentMap) {
- const lang = matchLang(meta.path.split('.').pop() || '')
+ let lang = matchLang(meta.path.split('.').pop() || '')
const source = isExternalSource(meta)
? `https://github.com/${meta.org}/${meta.repo}/blob/${meta.commit}${meta.path}`
: `https://github.com/supabase/supabase/blob/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA ?? 'master'}/examples${meta.path}`
- const elidedContent = redactLines(content, meta.lines, lang, meta.hideElidedLines)
+ let processedContent = content
+ if (meta.convertToJs) {
+ processedContent = await removeTypes(content)
+ // Convert TypeScript/TSX language to JavaScript/JSX when converting types
+ assert(
+ lang === 'typescript' || lang === 'tsx',
+ 'Type stripping to JS is only supported for TypeScript and TSX'
+ )
+ if (lang === 'typescript') {
+ lang = 'javascript'
+ } else if (lang === 'tsx') {
+ lang = 'jsx'
+ }
+ }
+
+ const elidedContent = redactLines(processedContent, meta.lines, lang, meta.hideElidedLines)
const replacementContent: MdxJsxFlowElement | Code = meta.codeHikeAncestor
? {
diff --git a/apps/docs/package.json b/apps/docs/package.json
index 96c1cdb065a5c..c3a310ff02699 100644
--- a/apps/docs/package.json
+++ b/apps/docs/package.json
@@ -107,6 +107,7 @@
"remark-emoji": "^3.1.2",
"remark-gfm": "^3.0.1",
"remark-math": "^6.0.0",
+ "remove-types": "1.0.0",
"server-only": "^0.0.1",
"shared-data": "workspace:*",
"ui": "workspace:*",
diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt
index 5024bf43949fa..1d2fe38b68bdf 100644
--- a/apps/docs/public/humans.txt
+++ b/apps/docs/public/humans.txt
@@ -97,6 +97,7 @@ Pavel Borisov
Paweł Gulbinowicz
Peter Lyn
Qiao Han
+Rafael Chacón
Raminder Singh
Riccardo Busetti
Rodrigo Mansueli
diff --git a/apps/docs/public/img/sso-dashboard-configure-attributes-azure.png b/apps/docs/public/img/sso-dashboard-configure-attributes-azure.png
new file mode 100644
index 0000000000000..09fa6ebb70eef
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-attributes-azure.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-attributes-generic.png b/apps/docs/public/img/sso-dashboard-configure-attributes-generic.png
new file mode 100644
index 0000000000000..761a6ecc42724
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-attributes-generic.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-attributes.png b/apps/docs/public/img/sso-dashboard-configure-attributes.png
new file mode 100644
index 0000000000000..d7b2749cc389d
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-attributes.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-autojoin-disabled.png b/apps/docs/public/img/sso-dashboard-configure-autojoin-disabled.png
new file mode 100644
index 0000000000000..513b42d86c9d4
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-autojoin-disabled.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-autojoin-enabled-role.png b/apps/docs/public/img/sso-dashboard-configure-autojoin-enabled-role.png
new file mode 100644
index 0000000000000..fc0e8e8bc8e0b
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-autojoin-enabled-role.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-autojoin-enabled.png b/apps/docs/public/img/sso-dashboard-configure-autojoin-enabled.png
new file mode 100644
index 0000000000000..4a9c3e2e2d33c
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-autojoin-enabled.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-domain-multi.png b/apps/docs/public/img/sso-dashboard-configure-domain-multi.png
new file mode 100644
index 0000000000000..c837a27161979
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-domain-multi.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-domain.png b/apps/docs/public/img/sso-dashboard-configure-domain.png
new file mode 100644
index 0000000000000..f7ca8ca955911
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-domain.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-metadata-azure.png b/apps/docs/public/img/sso-dashboard-configure-metadata-azure.png
new file mode 100644
index 0000000000000..e81e5b1b6a7bf
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-metadata-azure.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-metadata-gsuite.png b/apps/docs/public/img/sso-dashboard-configure-metadata-gsuite.png
new file mode 100644
index 0000000000000..675dc596da46d
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-metadata-gsuite.png differ
diff --git a/apps/docs/public/img/sso-dashboard-configure-metadata-okta.png b/apps/docs/public/img/sso-dashboard-configure-metadata-okta.png
new file mode 100644
index 0000000000000..5a633113abbb5
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-configure-metadata-okta.png differ
diff --git a/apps/docs/public/img/sso-dashboard-disabled.png b/apps/docs/public/img/sso-dashboard-disabled.png
new file mode 100644
index 0000000000000..0a5df2efc10fd
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-disabled.png differ
diff --git a/apps/docs/public/img/sso-dashboard-enabled.png b/apps/docs/public/img/sso-dashboard-enabled.png
new file mode 100644
index 0000000000000..9a911494b5464
Binary files /dev/null and b/apps/docs/public/img/sso-dashboard-enabled.png differ
diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.constants.ts b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.constants.ts
index 41bf5a5864478..505a373fa6f9c 100644
--- a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.constants.ts
+++ b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.constants.ts
@@ -12,7 +12,7 @@ export const INVOCATION_TABS: InvocationTab[] = [
label: 'cURL',
language: 'bash',
code: (functionUrl, _, apiKey) => `curl -L -X POST '${functionUrl}' \\
- -H 'Authorization: Bearer ${apiKey}' \\ ${apiKey.includes('publishable') ? `\n -H 'apikey: ${apiKey}' \\` : ''}
+ -H 'Authorization: Bearer ${apiKey}' \\${apiKey.includes('publishable') ? `\n -H 'apikey: ${apiKey}' \\` : ''}
-H 'Content-Type: application/json' \\
--data '{"name":"Functions"}'`,
},
diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx
index 441aead5490d6..fca0edd2a2404 100644
--- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx
+++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx
@@ -20,38 +20,50 @@ export interface UpcomingInvoiceProps {
const usageBillingDocsLink: { [K in PricingMetric]?: string } = {
[PricingMetric.MONTHLY_ACTIVE_USERS]:
- '/docs/guides/platform/manage-your-usage/monthly-active-users',
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/monthly-active-users',
[PricingMetric.MONTHLY_ACTIVE_SSO_USERS]:
- '/docs/guides/platform/manage-your-usage/monthly-active-users-sso',
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/monthly-active-users-sso',
[PricingMetric.MONTHLY_ACTIVE_THIRD_PARTY_USERS]:
- '/docs/guides/platform/manage-your-usage/monthly-active-users-third-party',
- [PricingMetric.AUTH_MFA_PHONE]: '/docs/guides/platform/manage-your-usage/advanced-mfa-phone',
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/monthly-active-users-third-party',
+ [PricingMetric.AUTH_MFA_PHONE]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/advanced-mfa-phone',
- [PricingMetric.EGRESS]: '/docs/guides/platform/manage-your-usage/egress',
+ [PricingMetric.EGRESS]: 'https://supabase.com/docs/guides/platform/manage-your-usage/egress',
[PricingMetric.FUNCTION_INVOCATIONS]:
- '/docs/guides/platform/manage-your-usage/edge-function-invocations',
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/edge-function-invocations',
- [PricingMetric.STORAGE_SIZE]: '/docs/guides/platform/manage-your-usage/storage-size',
+ [PricingMetric.STORAGE_SIZE]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/storage-size',
[PricingMetric.STORAGE_IMAGES_TRANSFORMED]:
- '/docs/guides/platform/manage-your-usage/storage-image-transformations',
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/storage-image-transformations',
[PricingMetric.REALTIME_MESSAGE_COUNT]:
- '/docs/guides/platform/manage-your-usage/realtime-messages',
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/realtime-messages',
[PricingMetric.REALTIME_PEAK_CONNECTIONS]:
- '/docs/guides/platform/manage-your-usage/realtime-peak-connections',
-
- [PricingMetric.CUSTOM_DOMAIN]: '/docs/guides/platform/manage-your-usage/custom-domains',
- [PricingMetric.IPV4]: '/docs/guides/platform/manage-your-usage/ipv4',
- [PricingMetric.PITR_7]: '/docs/guides/platform/manage-your-usage/point-in-time-recovery',
- [PricingMetric.PITR_14]: '/docs/guides/platform/manage-your-usage/point-in-time-recovery',
- [PricingMetric.PITR_28]: '/docs/guides/platform/manage-your-usage/point-in-time-recovery',
- [PricingMetric.DISK_SIZE_GB_HOURS_GP3]: '/docs/guides/platform/manage-your-usage/disk-size',
- [PricingMetric.DISK_SIZE_GB_HOURS_IO2]: '/docs/guides/platform/manage-your-usage/disk-size',
- [PricingMetric.DISK_IOPS_GP3]: '/docs/guides/platform/manage-your-usage/disk-iops',
- [PricingMetric.DISK_IOPS_IO2]: '/docs/guides/platform/manage-your-usage/disk-iops',
- [PricingMetric.DISK_THROUGHPUT_GP3]: '/docs/guides/platform/manage-your-usage/disk-throughput',
- [PricingMetric.LOG_DRAIN]: '/docs/guides/platform/manage-your-usage/log-drains',
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/realtime-peak-connections',
+
+ [PricingMetric.CUSTOM_DOMAIN]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/custom-domains',
+ [PricingMetric.IPV4]: 'https://supabase.com/docs/guides/platform/manage-your-usage/ipv4',
+ [PricingMetric.PITR_7]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/point-in-time-recovery',
+ [PricingMetric.PITR_14]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/point-in-time-recovery',
+ [PricingMetric.PITR_28]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/point-in-time-recovery',
+ [PricingMetric.DISK_SIZE_GB_HOURS_GP3]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/disk-size',
+ [PricingMetric.DISK_SIZE_GB_HOURS_IO2]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/disk-size',
+ [PricingMetric.DISK_IOPS_GP3]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/disk-iops',
+ [PricingMetric.DISK_IOPS_IO2]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/disk-iops',
+ [PricingMetric.DISK_THROUGHPUT_GP3]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/disk-throughput',
+ [PricingMetric.LOG_DRAIN]:
+ 'https://supabase.com/docs/guides/platform/manage-your-usage/log-drains',
}
const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
@@ -133,7 +145,7 @@ const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
compute costs starting at $10/month, independent
of activity. See{' '}
docs
@@ -153,7 +165,9 @@ const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
Compute, Disk Size, provisioned Disk IOPS, provisioned Disk Throughput, and
IPv4. See{' '}
docs
diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx
index cc82d7fc1bfd0..3a5ff8dca18bc 100644
--- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx
+++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx
@@ -351,7 +351,9 @@ export const SubscriptionPlanUpdateDialog = ({
Credits; additional projects start at $10
/month regardless of usage.{' '}
Learn more
@@ -587,7 +589,7 @@ export const SubscriptionPlanUpdateDialog = ({
/month regardless of usage.{' '}