From fe068260ef5a9db2cafef43b45fb0d2341e622bf Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Thu, 4 Sep 2025 18:29:47 +0800 Subject: [PATCH 1/7] feat: v1 migrations endpoint for self hosted (#38344) --- .../v1/projects/[ref]/database/migrations.ts | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 apps/studio/pages/api/v1/projects/[ref]/database/migrations.ts diff --git a/apps/studio/pages/api/v1/projects/[ref]/database/migrations.ts b/apps/studio/pages/api/v1/projects/[ref]/database/migrations.ts new file mode 100644 index 0000000000000..22d43e313b6d8 --- /dev/null +++ b/apps/studio/pages/api/v1/projects/[ref]/database/migrations.ts @@ -0,0 +1,100 @@ +import { NextApiRequest, NextApiResponse } from 'next' + +import { fetchPost } from 'data/fetchers' +import { constructHeaders } from 'lib/api/apiHelpers' +import apiWrapper from 'lib/api/apiWrapper' +import { PG_META_URL } from 'lib/constants' +import { makeRandomString } from 'lib/helpers' + +export default (req: NextApiRequest, res: NextApiResponse) => + apiWrapper(req, res, handler, { withAuth: true }) + +async function handler(req: NextApiRequest, res: NextApiResponse) { + const { method } = req + + switch (method) { + case 'GET': + return handleGetAll(req, res) + case 'POST': + return handlePost(req, res) + default: + res.setHeader('Allow', ['GET', 'POST']) + res.status(405).json({ error: { message: `Method ${method} Not Allowed` } }) + } +} +const listMigrationVersions = + 'select version, name from supabase_migrations.schema_migrations order by version' + +const handleGetAll = async (req: NextApiRequest, res: NextApiResponse) => { + const headers = constructHeaders(req.headers) + + const response = await fetchPost( + `${PG_META_URL}/query`, + { query: listMigrationVersions }, + { headers } + ) + + if (response.error) { + const { code, message } = response.error + return res.status(code).json({ message }) + } else { + return res.status(200).json(response) + } +} + +export const initialiseHistoryTable = `begin; + +create schema if not exists supabase_migrations; +create table if not exists supabase_migrations.schema_migrations (version text not null primary key); +alter table supabase_migrations.schema_migrations add column if not exists statements text[]; +alter table supabase_migrations.schema_migrations add column if not exists name text; + +commit;` + +export function applyAndTrackMigrations(query: string, name?: string) { + // Escapes literals using postgres dollar quoted string + const dollar = `$${makeRandomString(20)}$` + const quote = (s?: string) => (s ? dollar + s + dollar : `''`) + return `begin; + +-- apply sql from post body +${query}; + +-- track statements in history table +insert into supabase_migrations.schema_migrations (version, name, statements) +values ( + to_char(current_timestamp, 'YYYYMMDDHHMISS'), + ${quote(name)}, + array[${quote(query)}] +); + +commit;` +} + +const handlePost = async (req: NextApiRequest, res: NextApiResponse) => { + const headers = constructHeaders(req.headers) + + const { error } = await fetchPost( + `${PG_META_URL}/query`, + { query: initialiseHistoryTable }, + { headers } + ) + if (error) { + const { code, message } = error + return res.status(code).json({ message, formattedError: message }) + } + + const { query, name } = req.body + const response = await fetchPost( + `${PG_META_URL}/query`, + { query: applyAndTrackMigrations(query, name) }, + { headers } + ) + + if (response.error) { + const { code, message } = response.error + return res.status(code).json({ message, formattedError: message }) + } else { + return res.status(200).json(response) + } +} From c1edff35e2d1df44c59ac0885e0c97ca6c0039b0 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Thu, 4 Sep 2025 20:34:32 +0700 Subject: [PATCH 2/7] Fix missing loading prop in ConfirmationModal for disabling extension (#38430) --- .../components/interfaces/Database/Extensions/ExtensionRow.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx b/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx index 17e78b1368f8f..45e3985ef839e 100644 --- a/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx +++ b/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx @@ -193,6 +193,7 @@ export const ExtensionRow = ({ extension }: ExtensionRowProps) => { confirmLabel="Disable" variant="destructive" confirmLabelLoading="Disabling" + loading={isDisabling} onCancel={() => setIsDisableModalOpen(false)} onConfirm={() => onConfirmDisable()} > From 5e4cc212aa96d4cc3f5ffa7a688ea673f5c5da9b Mon Sep 17 00:00:00 2001 From: Riccardo Busetti Date: Thu, 4 Sep 2025 15:47:01 +0200 Subject: [PATCH 3/7] feat(replication): Add ability to copy pipeline id and filter logs by pipeline id (#38407) --- .../Database/Replication/DestinationPanel.tsx | 11 +- .../Database/Replication/DestinationRow.tsx | 20 ++- .../Database/Replication/Pipeline.utils.ts | 3 +- .../Database/Replication/PipelineStatus.tsx | 15 +- .../Replication/ReplicationPipelineStatus.tsx | 142 +++++++++++------- .../Database/Replication/RowMenu.tsx | 102 +++++++++---- .../Settings/Logs/Logs.constants.ts | 4 + .../update-destination-pipeline-mutation.ts | 6 +- 8 files changed, 209 insertions(+), 94 deletions(-) diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx index 68199c42788cf..2d4aea160f4a1 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx @@ -74,6 +74,7 @@ interface DestinationPanelProps { destinationId: number pipelineId?: number enabled: boolean + statusName?: string } } @@ -178,7 +179,8 @@ export const DestinationPanel = ({ sourceId, }) // Set request status only right before starting, then fire and close - const snapshot = existingDestination.enabled ? 'started' : 'stopped' + const snapshot = + existingDestination.statusName ?? (existingDestination.enabled ? 'started' : 'stopped') if (existingDestination.enabled) { setRequestStatus( existingDestination.pipelineId, @@ -244,6 +246,13 @@ export const DestinationPanel = ({ } }, [destinationData, pipelineData, editMode, defaultValues, form]) + // Ensure the form always reflects the freshest data whenever the panel opens + useEffect(() => { + if (visible) { + form.reset(defaultValues) + } + }, [visible, defaultValues, form]) + return sourceId ? ( <> diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx index 16437c30b5b8a..28da065f287bb 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx @@ -125,7 +125,20 @@ export const DestinationRow = ({ )} {isPipelineSuccess && ( - {isPipelineLoading ? : destinationName} + + {isPipelineLoading ? ( + + ) : pipeline?.id ? ( + + + {destinationName} + + Pipeline ID: {pipeline.id} + + ) : ( + destinationName + )} + {isPipelineLoading ? : type} {isPipelineLoading || !pipeline ? ( @@ -138,6 +151,7 @@ export const DestinationRow = ({ isError={isPipelineStatusError} isSuccess={isPipelineStatusSuccess} requestStatus={requestStatus} + pipelineId={pipeline?.id} /> )} @@ -195,7 +209,9 @@ export const DestinationRow = ({ sourceId, destinationId: destinationId, pipelineId: pipeline?.id, - enabled: statusName === PipelineStatusName.STARTED, + enabled: + statusName === PipelineStatusName.STARTED || statusName === PipelineStatusName.FAILED, + statusName, }} /> diff --git a/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts b/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts index eb69b88e689d0..8a4733175b727 100644 --- a/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts +++ b/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts @@ -1,5 +1,6 @@ import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query' import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status' +import { PipelineStatusName } from './PipelineStatus' export const PIPELINE_ERROR_MESSAGES = { RETRIEVE_PIPELINE: 'Failed to retrieve pipeline information', @@ -24,7 +25,7 @@ export const getStatusName = ( export const PIPELINE_ENABLE_ALLOWED_FROM = ['stopped'] as const export const PIPELINE_DISABLE_ALLOWED_FROM = ['started', 'failed'] as const -export const PIPELINE_ACTIONABLE_STATES = ['failed', 'started', 'stopped'] as const +export const PIPELINE_ACTIONABLE_STATES = ['failed', 'started', 'stopped'] as PipelineStatusName[] const PIPELINE_STATE_MESSAGES = { enabling: { diff --git a/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx index 14e8b9a99696c..e0804c42dfaed 100644 --- a/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx +++ b/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx @@ -1,7 +1,8 @@ +import { AlertTriangle, Loader2 } from 'lucide-react' + import { useParams } from 'common' import { InlineLink } from 'components/ui/InlineLink' import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query' -import { AlertTriangle, Loader2 } from 'lucide-react' import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status' import { ResponseError } from 'types' import { cn, Tooltip, TooltipContent, TooltipTrigger, WarningIcon } from 'ui' @@ -23,6 +24,7 @@ interface PipelineStatusProps { isError: boolean isSuccess: boolean requestStatus?: PipelineStatusRequestStatus + pipelineId?: number } export const PipelineStatus = ({ @@ -32,6 +34,7 @@ export const PipelineStatus = ({ isError, isSuccess, requestStatus, + pipelineId, }: PipelineStatusProps) => { const { ref } = useParams() @@ -129,6 +132,12 @@ export const PipelineStatus = ({ const statusConfig = getStatusConfig() + const pipelineLogsUrl = pipelineId + ? `/project/${ref}/logs/etl-replication-logs?f=${encodeURIComponent( + JSON.stringify({ pipeline_id: pipelineId }) + )}` + : `/project/${ref}/logs/etl-replication-logs` + return ( <> {isLoading && } @@ -155,9 +164,7 @@ export const PipelineStatus = ({ {['unknown', 'failed'].includes(pipelineStatus?.name ?? '') && ( <> {' '} - Check the{' '} - logs for - more information. + Check the logs for more information. )} diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx index c3e4ca8117e1e..0bf7a556003a8 100644 --- a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx +++ b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx @@ -1,4 +1,14 @@ -import { Activity, ChevronLeft, ExternalLink, Search, X } from 'lucide-react' +import { + Activity, + Ban, + ChevronLeft, + ExternalLink, + Pause, + Play, + RotateCcw, + Search, + X, +} from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { toast } from 'sonner' @@ -22,12 +32,10 @@ import { Input } from 'ui-patterns/DataInputs/Input' import { ErroredTableDetails } from './ErroredTableDetails' import { PIPELINE_ACTIONABLE_STATES, - PIPELINE_DISABLE_ALLOWED_FROM, - PIPELINE_ENABLE_ALLOWED_FROM, PIPELINE_ERROR_MESSAGES, getStatusName, } from './Pipeline.utils' -import { PipelineStatus } from './PipelineStatus' +import { PipelineStatus, PipelineStatusName } from './PipelineStatus' import { STATUS_REFRESH_FREQUENCY_MS } from './Replication.constants' import { TableState } from './ReplicationPipelineStatus.types' import { getDisabledStateConfig, getStatusConfig } from './ReplicationPipelineStatus.utils' @@ -104,21 +112,44 @@ export const ReplicationPipelineStatus = () => { requestStatus === PipelineStatusRequestStatus.RestartRequested const showDisabledState = !isPipelineRunning || isEnablingDisabling - const onTogglePipeline = async () => { - if (!projectRef) { - return console.error('Project ref is required') - } - if (!pipeline) { - return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) - } + const logsUrl = `/project/${projectRef}/logs/etl-replication-logs${ + pipelineId ? `?f=${encodeURIComponent(JSON.stringify({ pipeline_id: pipelineId }))}` : '' + }` + + const label = + statusName === 'stopped' + ? 'Start' + : statusName === 'started' + ? 'Stop' + : statusName === 'failed' + ? 'Restart' + : 'Action unavailable' + + const icon = + statusName === 'stopped' ? ( + + ) : statusName === 'started' ? ( + + ) : statusName === 'failed' ? ( + + ) : ( + + ) + + const onPrimaryAction = async () => { + if (!projectRef) return console.error('Project ref is required') + if (!pipeline) return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) try { - if (PIPELINE_ENABLE_ALLOWED_FROM.includes(statusName as any)) { + if (statusName === 'stopped') { setRequestStatus(pipeline.id, PipelineStatusRequestStatus.StartRequested, statusName) await startPipeline({ projectRef, pipelineId: pipeline.id }) - } else if (PIPELINE_DISABLE_ALLOWED_FROM.includes(statusName as any)) { + } else if (statusName === 'started') { setRequestStatus(pipeline.id, PipelineStatusRequestStatus.StopRequested, statusName) await stopPipeline({ projectRef, pipelineId: pipeline.id }) + } else if (statusName === 'failed') { + setRequestStatus(pipeline.id, PipelineStatusRequestStatus.RestartRequested, statusName) + await startPipeline({ projectRef, pipelineId: pipeline.id }) } } catch (error) { toast.error(PIPELINE_ERROR_MESSAGES.ENABLE_DESTINATION) @@ -136,57 +167,57 @@ export const ReplicationPipelineStatus = () => { -
-
-

{destinationName || 'Pipeline'}

- -
+
+

{destinationName || 'Pipeline'}

+
-
- - setFilterString(e.target.value)} - actions={ - filterString.length > 0 - ? [ - setFilterString('')} - />, - ] - : undefined - } - /> -
+ } + className="pl-7 h-[26px] text-xs" + placeholder="Search for tables" + value={filterString} + disabled={isPipelineError} + onChange={(e) => setFilterString(e.target.value)} + actions={ + filterString.length > 0 + ? [ + setFilterString('')} + />, + ] + : undefined + } + /> + +
@@ -231,6 +262,7 @@ export const ReplicationPipelineStatus = () => { )}
+ {/* [Joshen] Should update to use new Table components next time */} Table, diff --git a/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx b/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx index 7e083058ed5c0..203aa40bae676 100644 --- a/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx +++ b/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx @@ -1,8 +1,9 @@ -import { Edit, MoreVertical, Pause, Play, Trash } from 'lucide-react' +import { Edit, MoreVertical, Pause, Play, RotateCcw, Trash } from 'lucide-react' import { toast } from 'sonner' import { useParams } from 'common' import AlertError from 'components/ui/AlertError' +import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query' import { Pipeline } from 'data/replication/pipelines-query' import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation' import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation' @@ -21,6 +22,7 @@ import { } from 'ui' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' import { + PIPELINE_ACTIONABLE_STATES, PIPELINE_DISABLE_ALLOWED_FROM, PIPELINE_ENABLE_ALLOWED_FROM, PIPELINE_ERROR_MESSAGES, @@ -30,7 +32,7 @@ import { PipelineStatusName } from './PipelineStatus' interface RowMenuProps { pipeline: Pipeline | undefined - pipelineStatus: any + pipelineStatus?: ReplicationPipelineStatusData['status'] error: ResponseError | null isLoading: boolean isError: boolean @@ -48,21 +50,42 @@ export const RowMenu = ({ onDeleteClick, }: RowMenuProps) => { const { ref: projectRef } = useParams() - const statusName = getStatusName(pipelineStatus) - const pipelineEnabled = statusName !== PipelineStatusName.STOPPED const { mutateAsync: startPipeline } = useStartPipelineMutation() const { mutateAsync: stopPipeline } = useStopPipelineMutation() - const { setRequestStatus: setGlobalRequestStatus } = usePipelineRequestStatus() + const { getRequestStatus, setRequestStatus: setGlobalRequestStatus } = usePipelineRequestStatus() + const requestStatus = pipeline?.id + ? getRequestStatus(pipeline.id) + : PipelineStatusRequestStatus.None + + const hasPipelineAction = + requestStatus === PipelineStatusRequestStatus.None && + [PipelineStatusName.STOPPED, PipelineStatusName.STARTED, PipelineStatusName.FAILED].includes( + statusName as PipelineStatusName + ) + + const pipelineActionIcon = + statusName === PipelineStatusName.STOPPED ? ( + + ) : statusName === PipelineStatusName.STARTED ? ( + + ) : statusName === PipelineStatusName.FAILED ? ( + + ) : null + + const pipelineActionLabel = + statusName === PipelineStatusName.STOPPED + ? 'Start pipeline' + : statusName === PipelineStatusName.STARTED + ? 'Stop pipeline' + : statusName === PipelineStatusName.FAILED + ? 'Restart pipeline' + : null const onEnablePipeline = async () => { - if (!projectRef) { - return console.error('Project ref is required') - } - if (!pipeline) { - return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) - } + if (!projectRef) return console.error('Project ref is required') + if (!pipeline) return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) try { // Only show 'enabling' when transitioning from allowed states @@ -77,14 +100,8 @@ export const RowMenu = ({ } const onDisablePipeline = async () => { - if (!projectRef) { - console.error('Project ref is required') - return - } - if (!pipeline) { - toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) - return - } + if (!projectRef) return console.error('Project ref is required') + if (!pipeline) return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) try { // Only show 'disabling' when transitioning from allowed states @@ -98,9 +115,23 @@ export const RowMenu = ({ } } + const onRestartPipeline = async () => { + if (!projectRef) return console.error('Project ref is required') + if (!pipeline) return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) + + try { + setGlobalRequestStatus(pipeline.id, PipelineStatusRequestStatus.RestartRequested, statusName) + await startPipeline({ projectRef, pipelineId: pipeline.id }) + } catch (error) { + setGlobalRequestStatus(pipeline.id, PipelineStatusRequestStatus.None) + toast.error(PIPELINE_ERROR_MESSAGES.ENABLE_DESTINATION) + } + } + return (
{isLoading && } + {isError && ( )} @@ -109,19 +140,30 @@ export const RowMenu = ({
- {clientLibraries.map((library) => { - return ( - - - - ) - })} + {clientLibraries + .filter((library) => library.enabled) + + .map((library) => { + return ( + + + + ) + })}
diff --git a/apps/docs/components/HomePageCover.tsx b/apps/docs/components/HomePageCover.tsx index 25bd3b32a484a..d89453b466b81 100644 --- a/apps/docs/components/HomePageCover.tsx +++ b/apps/docs/components/HomePageCover.tsx @@ -3,12 +3,17 @@ import { ChevronRight, Play, Sparkles } from 'lucide-react' import Link from 'next/link' -import { useBreakpoint } from 'common' +import { isFeatureEnabled, useBreakpoint } from 'common' import { cn, IconBackground } from 'ui' import { IconPanel } from 'ui-patterns/IconPanel' import DocsCoverLogo from './DocsCoverLogo' +const { sdkDart: sdkDartEnabled, sdkKotlin: sdkKotlinEnabled } = isFeatureEnabled([ + 'sdk:dart', + 'sdk:kotlin', +]) + function AiPrompt({ className }: { className?: string }) { return ( { tooltip: 'Flutter', icon: '/docs/img/icons/flutter-icon', href: '/guides/getting-started/quickstarts/flutter', + enabled: sdkDartEnabled, }, { tooltip: 'Android Kotlin', icon: '/docs/img/icons/kotlin-icon', href: '/guides/getting-started/quickstarts/kotlin', + enabled: sdkKotlinEnabled, }, { tooltip: 'SvelteKit', @@ -109,16 +116,18 @@ const HomePageCover = (props) => {
- {frameworks.map((framework, i) => ( - - - - ))} + {frameworks + .filter((framework) => framework.enabled !== false) + .map((framework, i) => ( + + + + ))}
@@ -135,7 +144,7 @@ const HomePageCover = (props) => {

{props.title}

Learn how to get up and running with Supabase through tutorials, APIs and platform - resources. + resources. Differences TBD.

diff --git a/apps/docs/components/Navigation/Navigation.types.ts b/apps/docs/components/Navigation/Navigation.types.ts index ce2c2dec84195..7918a38e68e64 100644 --- a/apps/docs/components/Navigation/Navigation.types.ts +++ b/apps/docs/components/Navigation/Navigation.types.ts @@ -11,6 +11,7 @@ export interface NavMenuSection { name: string url?: `/${string}` | `https://${string}` items: Partial[] + enabled?: boolean } type MenuItem = { @@ -20,6 +21,7 @@ type MenuItem = { level?: string hasLightIcon?: boolean community?: boolean + enabled?: boolean } export type DropdownMenuItem = MenuItem & { diff --git a/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx index ce620239cf0b5..8b57fd7c29e81 100644 --- a/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx @@ -22,7 +22,7 @@ import { GLOBAL_MENU_ITEMS } from './NavigationMenu.constants' /** * Get TopNav active label based on current pathname */ -export const useActiveMenuLabel = (GLOBAL_MENU_ITEMS) => { +export const useActiveMenuLabel = (menu: typeof GLOBAL_MENU_ITEMS) => { const pathname = usePathname() const [activeLabel, setActiveLabel] = useState('') @@ -32,8 +32,10 @@ export const useActiveMenuLabel = (GLOBAL_MENU_ITEMS) => { return setActiveLabel('Home') } - for (let index = 0; index < GLOBAL_MENU_ITEMS.length; index++) { - const section = GLOBAL_MENU_ITEMS[index] + for (let index = 0; index < menu.length; index++) { + const section = menu[index] + if (section[0].enabled === false) continue + // check if first level menu items match beginning of url if (section[0].href?.startsWith(pathname)) { return setActiveLabel(section[0].label) @@ -41,13 +43,15 @@ export const useActiveMenuLabel = (GLOBAL_MENU_ITEMS) => { // check if second level menu items match beginning of url if (section[0].menuItems) { section[0].menuItems.map((menuItemGroup) => - menuItemGroup.map( - (menuItem) => menuItem.href?.startsWith(pathname) && setActiveLabel(section[0].label) - ) + menuItemGroup + .filter((menuItem) => menuItem.enabled !== false) + .map( + (menuItem) => menuItem.href?.startsWith(pathname) && setActiveLabel(section[0].label) + ) ) } } - }, [pathname]) + }, [pathname, menu]) return activeLabel } @@ -67,65 +71,14 @@ const GlobalNavigationMenu: FC = () => { viewportClassName="mt-0 max-w-screen overflow-hidden border-0 rounded-none mt-1.5 rounded-md !border-x" > - {GLOBAL_MENU_ITEMS.map((section, sectionIdx) => - section[0].menuItems ? ( - - section[0].enabled !== false).map( + (section, sectionIdx) => + section[0].menuItems ? ( + - {section[0].label === 'Home' ? ( - - ) : ( - section[0].label - )} - - -
- {section[0].menuItems?.map((menuItem, menuItemIndex) => ( - - {menuItemIndex !== 0 && } - {menuItem.map((item, itemIdx) => - !item.href ? ( -
- {item.label} -
- ) : ( - - - - ) - )} -
- ))} -
-
-
- ) : ( - - - { ) : ( section[0].label )} - - - - ) + + +
+ {section[0].menuItems?.map((menuItem, menuItemIndex) => ( + + {menuItemIndex !== 0 && } + {menuItem + .filter((item) => item.enabled !== false) + .map((item, itemIdx) => + !item.href ? ( +
+ {item.label} +
+ ) : ( + + + + ) + )} +
+ ))} +
+
+ + ) : ( + + + + {section[0].label === 'Home' ? ( + + ) : ( + section[0].label + )} + + + + ) )}
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 87102c911f3e9..ea41b02c326d8 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -1,9 +1,32 @@ import type { ComponentProps } from 'react' +import { isFeatureEnabled } from 'common' import type { IconPanel } from 'ui-patterns/IconPanel' - import type { GlobalMenuItems, NavMenuConstant, NavMenuSection } from '../Navigation.types' +const { + authenticationShowProviders: allAuthProvidersEnabled, + billingAll: billingEnabled, + 'docsSelf-hosting': selfHostingEnabled, + sdkCsharp: sdkCsharpEnabled, + sdkDart: sdkDartEnabled, + sdkKotlin: sdkKotlinEnabled, + sdkPython: sdkPythonEnabled, + sdkSwift: sdkSwiftEnabled, +} = isFeatureEnabled([ + 'authentication:show_providers', + 'billing:all', + 'docs:self-hosting', + 'sdk:csharp', + 'sdk:dart', + 'sdk:kotlin', + 'sdk:python', + 'sdk:swift', +]) + +const jsOnly = + !sdkCsharpEnabled && !sdkDartEnabled && !sdkKotlinEnabled && !sdkPythonEnabled && !sdkSwiftEnabled + export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ [ { @@ -21,31 +44,31 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ { label: 'Database', icon: 'database', - href: '/guides/database/overview', + href: '/guides/database/overview' as `/${string}`, level: 'database', }, { label: 'Auth', icon: 'auth', - href: '/guides/auth', + href: '/guides/auth' as `/${string}`, level: 'auth', }, { label: 'Storage', icon: 'storage', - href: '/guides/storage', + href: '/guides/storage' as `/${string}`, level: 'storage', }, { label: 'Edge Functions', icon: 'edge-functions', - href: '/guides/functions', + href: '/guides/functions' as `/${string}`, level: 'functions', }, { label: 'Realtime', icon: 'realtime', - href: '/guides/realtime', + href: '/guides/realtime' as `/${string}`, level: 'realtime', }, ], @@ -54,19 +77,19 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ { label: 'AI & Vectors', icon: 'ai', - href: '/guides/ai', + href: '/guides/ai' as `/${string}`, level: 'ai', }, { label: 'Cron', icon: 'cron', - href: '/guides/cron', + href: '/guides/cron' as `/${string}`, level: 'cron', }, { label: 'Queues', icon: 'queues', - href: '/guides/queues', + href: '/guides/queues' as `/${string}`, level: 'queues', }, ], @@ -81,26 +104,27 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ { label: 'Local Development & CLI', icon: 'dev-cli', - href: '/guides/local-development', + href: '/guides/local-development' as `/${string}`, level: 'local_development', }, { label: 'Deployment', icon: 'deployment', - href: '/guides/deployment', + href: '/guides/deployment' as `/${string}`, level: 'deployment', }, { label: 'Self-Hosting', icon: 'self-hosting', - href: '/guides/self-hosting', + href: '/guides/self-hosting' as `/${string}`, level: 'self_hosting', + enabled: selfHostingEnabled, }, { label: 'Integrations', icon: 'integrations', hasLightIcon: true, - href: '/guides/integrations', + href: '/guides/integrations' as `/${string}`, level: 'integrations', }, ], @@ -115,25 +139,26 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ { label: 'Platform Management', icon: 'platform', - href: '/guides/platform', + href: '/guides/platform' as `/${string}`, level: 'platform', }, { label: 'Security & Compliance', icon: 'security', - href: '/guides/security', + href: '/guides/security' as `/${string}`, level: 'security', }, { label: 'Telemetry', icon: 'telemetry', - href: '/guides/telemetry', + href: '/guides/telemetry' as `/${string}`, level: 'telemetry', }, { label: 'Troubleshooting', icon: 'troubleshooting', - href: '/guides/troubleshooting', + href: '/guides/troubleshooting' as `/${string}`, + level: 'troubleshooting', }, ], ], @@ -150,53 +175,58 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ { label: 'JavaScript', icon: 'reference-javascript', - href: '/reference/javascript', + href: '/reference/javascript' as `/${string}`, level: 'reference_javascript', }, { label: 'Flutter', icon: 'reference-dart', - href: '/reference/dart', + href: '/reference/dart' as `/${string}`, level: 'reference_dart', + enabled: sdkDartEnabled, }, { label: 'Swift', icon: 'reference-swift', - href: '/reference/swift', + href: '/reference/swift' as `/${string}`, level: 'reference_swift', + enabled: sdkSwiftEnabled, }, { label: 'Python', icon: 'reference-python', - href: '/reference/python', + href: '/reference/python' as `/${string}`, level: 'reference_python', + enabled: sdkPythonEnabled, }, { label: 'C#', icon: 'reference-csharp', - href: '/reference/csharp', + href: '/reference/csharp' as `/${string}`, level: 'reference_csharp', community: true, + enabled: sdkCsharpEnabled, }, { label: 'Kotlin', icon: 'reference-kotlin', - href: '/reference/kotlin', + href: '/reference/kotlin' as `/${string}`, level: 'reference_kotlin', community: true, + enabled: sdkKotlinEnabled, }, ], [ { label: 'CLI Commands', icon: 'reference-cli', - href: '/reference/cli/introduction', + href: '/reference/cli/introduction' as `/${string}`, level: 'reference_javascript', }, { label: 'Management API', icon: 'reference-api', - href: '/reference/api/introduction', + href: '/reference/api/introduction' as `/${string}`, level: 'reference_javascript', }, ], @@ -205,13 +235,13 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ { label: 'REST', icon: 'rest', - href: '/guides/api', + href: '/guides/api' as `/${string}`, level: 'api', }, { label: 'GraphQL', icon: 'graphql', - href: '/guides/graphql', + href: '/guides/graphql' as `/${string}`, level: 'graphql', }, ], @@ -226,14 +256,14 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ { label: 'Glossary', icon: 'resources', - href: '/guides/resources/glossary', + href: '/guides/resources/glossary' as `/${string}`, level: 'resources', }, { label: 'Changelog', icon: 'changelog', hasLightIcon: true, - href: 'https://supabase.com/changelog', + href: 'https://supabase.com/changelog' as `/${string}`, level: 'changelog', }, { @@ -267,28 +297,40 @@ export const gettingstarted: NavMenuConstant = { { name: 'Nuxt', url: '/guides/getting-started/quickstarts/nuxtjs' }, { name: 'Vue', url: '/guides/getting-started/quickstarts/vue' }, { name: 'Hono', url: '/guides/getting-started/quickstarts/hono' }, - { name: 'Flutter', url: '/guides/getting-started/quickstarts/flutter' }, - { name: 'iOS SwiftUI', url: '/guides/getting-started/quickstarts/ios-swiftui' }, + { + name: 'Flutter', + url: '/guides/getting-started/quickstarts/flutter', + enabled: sdkDartEnabled, + }, + { + name: 'iOS SwiftUI', + url: '/guides/getting-started/quickstarts/ios-swiftui', + enabled: sdkSwiftEnabled, + }, { name: 'Android Kotlin', - url: '/guides/getting-started/quickstarts/kotlin', + url: '/guides/getting-started/quickstarts/kotlin' as `/${string}`, + enabled: sdkKotlinEnabled, }, { name: 'SvelteKit', - url: '/guides/getting-started/quickstarts/sveltekit', + url: '/guides/getting-started/quickstarts/sveltekit' as `/${string}`, }, { name: 'Laravel PHP', - url: '/guides/getting-started/quickstarts/laravel', + url: '/guides/getting-started/quickstarts/laravel' as `/${string}`, + enabled: !jsOnly, }, { name: 'Ruby on Rails', - url: '/guides/getting-started/quickstarts/ruby-on-rails', + url: '/guides/getting-started/quickstarts/ruby-on-rails' as `/${string}`, + enabled: !jsOnly, }, + { name: 'SolidJS', url: '/guides/getting-started/quickstarts/solidjs' }, { name: 'RedwoodJS', - url: '/guides/getting-started/quickstarts/redwoodjs', + url: '/guides/getting-started/quickstarts/redwoodjs' as `/${string}`, }, { name: 'refine', url: '/guides/getting-started/quickstarts/refine' }, ], @@ -298,43 +340,43 @@ export const gettingstarted: NavMenuConstant = { items: [ { name: 'Next.js', - url: '/guides/getting-started/tutorials/with-nextjs', + url: '/guides/getting-started/tutorials/with-nextjs' as `/${string}`, }, { name: 'React', - url: '/guides/getting-started/tutorials/with-react', + url: '/guides/getting-started/tutorials/with-react' as `/${string}`, }, { name: 'Vue 3', - url: '/guides/getting-started/tutorials/with-vue-3', + url: '/guides/getting-started/tutorials/with-vue-3' as `/${string}`, }, { name: 'Nuxt 3', - url: '/guides/getting-started/tutorials/with-nuxt-3', + url: '/guides/getting-started/tutorials/with-nuxt-3' as `/${string}`, }, { name: 'Angular', - url: '/guides/getting-started/tutorials/with-angular', + url: '/guides/getting-started/tutorials/with-angular' as `/${string}`, }, { name: 'RedwoodJS', - url: '/guides/getting-started/tutorials/with-redwoodjs', + url: '/guides/getting-started/tutorials/with-redwoodjs' as `/${string}`, }, { name: 'SolidJS', - url: '/guides/getting-started/tutorials/with-solidjs', + url: '/guides/getting-started/tutorials/with-solidjs' as `/${string}`, }, { name: 'Svelte', - url: '/guides/getting-started/tutorials/with-svelte', + url: '/guides/getting-started/tutorials/with-svelte' as `/${string}`, }, { name: 'SvelteKit', - url: '/guides/getting-started/tutorials/with-sveltekit', + url: '/guides/getting-started/tutorials/with-sveltekit' as `/${string}`, }, { name: 'refine', - url: '/guides/getting-started/tutorials/with-refine', + url: '/guides/getting-started/tutorials/with-refine' as `/${string}`, }, ], }, @@ -343,31 +385,36 @@ export const gettingstarted: NavMenuConstant = { items: [ { name: 'Flutter', - url: '/guides/getting-started/tutorials/with-flutter', + url: '/guides/getting-started/tutorials/with-flutter' as `/${string}`, + enabled: sdkDartEnabled, }, + { name: 'Expo React Native', - url: '/guides/getting-started/tutorials/with-expo-react-native', + url: '/guides/getting-started/tutorials/with-expo-react-native' as `/${string}`, }, { name: 'Android Kotlin', - url: '/guides/getting-started/tutorials/with-kotlin', + url: '/guides/getting-started/tutorials/with-kotlin' as `/${string}`, + enabled: sdkKotlinEnabled, }, + { name: 'Ionic React', - url: '/guides/getting-started/tutorials/with-ionic-react', + url: '/guides/getting-started/tutorials/with-ionic-react' as `/${string}`, }, { name: 'Ionic Vue', - url: '/guides/getting-started/tutorials/with-ionic-vue', + url: '/guides/getting-started/tutorials/with-ionic-vue' as `/${string}`, }, { name: 'Ionic Angular', - url: '/guides/getting-started/tutorials/with-ionic-angular', + url: '/guides/getting-started/tutorials/with-ionic-angular' as `/${string}`, }, { name: 'Swift', - url: '/guides/getting-started/tutorials/with-swift', + url: '/guides/getting-started/tutorials/with-swift' as `/${string}`, + enabled: sdkSwiftEnabled, }, ], }, @@ -377,11 +424,11 @@ export const gettingstarted: NavMenuConstant = { items: [ { name: 'Prompts', - url: '/guides/getting-started/ai-prompts', + url: '/guides/getting-started/ai-prompts' as `/${string}`, }, { name: 'Model context protocol (MCP)', - url: '/guides/getting-started/mcp', + url: '/guides/getting-started/mcp' as `/${string}`, }, ], }, @@ -561,12 +608,12 @@ export const auth = { items: [ { name: 'Next.js', - url: '/guides/auth/quickstarts/nextjs', + url: '/guides/auth/quickstarts/nextjs' as `/${string}`, }, { name: 'React', url: '/guides/auth/quickstarts/react', items: [] }, { name: 'React Native', - url: '/guides/auth/quickstarts/react-native', + url: '/guides/auth/quickstarts/react-native' as `/${string}`, }, ], }, @@ -596,46 +643,68 @@ export const auth = { { name: 'Next.js guide', url: '/guides/auth/server-side/nextjs' }, { name: 'SvelteKit guide', - url: '/guides/auth/server-side/sveltekit', + url: '/guides/auth/server-side/sveltekit' as `/${string}`, }, { name: 'Creating a client', url: '/guides/auth/server-side/creating-a-client' }, { name: 'Migrating from Auth Helpers', - url: '/guides/auth/server-side/migrating-to-ssr-from-auth-helpers', + url: '/guides/auth/server-side/migrating-to-ssr-from-auth-helpers' as `/${string}`, }, { name: 'Advanced guide', - url: '/guides/auth/server-side/advanced-guide', + url: '/guides/auth/server-side/advanced-guide' as `/${string}`, }, ], }, - { name: 'Password-based', url: '/guides/auth/passwords' }, - { name: 'Email (Magic Link or OTP)', url: '/guides/auth/auth-email-passwordless' }, + { + name: 'Password-based', + url: '/guides/auth/passwords', + enabled: allAuthProvidersEnabled, + }, + { + name: 'Email (Magic Link or OTP)', + url: '/guides/auth/auth-email-passwordless', + enabled: allAuthProvidersEnabled, + }, { name: 'Phone Login', - url: '/guides/auth/phone-login', + url: '/guides/auth/phone-login' as `/${string}`, + enabled: allAuthProvidersEnabled, }, + { name: 'Social Login (OAuth)', url: '/guides/auth/social-login', items: [...SocialLoginItems], + enabled: allAuthProvidersEnabled, }, + { name: 'Enterprise SSO', url: '/guides/auth/enterprise-sso', + enabled: allAuthProvidersEnabled, items: [ { name: 'SAML 2.0', - url: '/guides/auth/enterprise-sso/auth-sso-saml', + url: '/guides/auth/enterprise-sso/auth-sso-saml' as `/${string}`, }, ], }, - { name: 'Anonymous Sign-Ins', url: '/guides/auth/auth-anonymous' }, - { name: 'Web3 (Sign in with Solana)', url: '/guides/auth/auth-web3' }, + + { + name: 'Anonymous Sign-Ins', + url: '/guides/auth/auth-anonymous', + enabled: allAuthProvidersEnabled, + }, + { + name: 'Web3 (Sign in with Solana)', + url: '/guides/auth/auth-web3', + enabled: allAuthProvidersEnabled, + }, { name: 'Mobile Deep Linking', url: '/guides/auth/native-mobile-deep-linking' }, { name: 'Identity Linking', - url: '/guides/auth/auth-identity-linking', + url: '/guides/auth/auth-identity-linking' as `/${string}`, }, { name: 'Multi-Factor Authentication', @@ -647,7 +716,7 @@ export const auth = { }, { name: 'Signout', - url: '/guides/auth/signout', + url: '/guides/auth/signout' as `/${string}`, }, ], }, @@ -660,6 +729,7 @@ export const auth = { }, { name: 'Third-party auth', + enabled: allAuthProvidersEnabled, items: [ { name: 'Overview', url: '/guides/auth/third-party/overview' }, { name: 'Clerk', url: '/guides/auth/third-party/clerk' }, @@ -674,12 +744,12 @@ export const auth = { items: [ { name: 'General Configuration', - url: '/guides/auth/general-configuration', + url: '/guides/auth/general-configuration' as `/${string}`, }, { name: 'Email Templates', url: '/guides/auth/auth-email-templates' }, { name: 'Redirect URLs', - url: '/guides/auth/redirect-urls', + url: '/guides/auth/redirect-urls' as `/${string}`, }, { name: 'Auth Hooks', @@ -687,27 +757,27 @@ export const auth = { items: [ { name: 'Custom access token hook', - url: '/guides/auth/auth-hooks/custom-access-token-hook', + url: '/guides/auth/auth-hooks/custom-access-token-hook' as `/${string}`, }, { name: 'Send SMS hook', - url: '/guides/auth/auth-hooks/send-sms-hook', + url: '/guides/auth/auth-hooks/send-sms-hook' as `/${string}`, }, { name: 'Send email hook', - url: '/guides/auth/auth-hooks/send-email-hook', + url: '/guides/auth/auth-hooks/send-email-hook' as `/${string}`, }, { name: 'MFA verification hook', - url: '/guides/auth/auth-hooks/mfa-verification-hook', + url: '/guides/auth/auth-hooks/mfa-verification-hook' as `/${string}`, }, { name: 'Password verification hook', - url: '/guides/auth/auth-hooks/password-verification-hook', + url: '/guides/auth/auth-hooks/password-verification-hook' as `/${string}`, }, { name: 'Before User Created hook', - url: '/guides/auth/auth-hooks/before-user-created-hook', + url: '/guides/auth/auth-hooks/before-user-created-hook' as `/${string}`, }, ], }, @@ -730,22 +800,23 @@ export const auth = { { name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' }, { name: 'Column Level Security', - url: '/guides/database/postgres/column-level-security', + url: '/guides/database/postgres/column-level-security' as `/${string}`, }, { name: 'Custom Claims & RBAC', - url: '/guides/database/postgres/custom-claims-and-role-based-access-control-rbac', + url: '/guides/database/postgres/custom-claims-and-role-based-access-control-rbac' as `/${string}`, }, ], }, { name: 'Auth UI', url: undefined, + enabled: allAuthProvidersEnabled, items: [ { name: 'Auth UI (Deprecated)', url: '/guides/auth/auth-helpers/auth-ui' }, { name: 'Flutter Auth UI', - url: '/guides/auth/auth-helpers/flutter-auth-ui', + url: '/guides/auth/auth-helpers/flutter-auth-ui' as `/${string}`, }, ], }, @@ -762,7 +833,7 @@ const ormQuickstarts: NavMenuSection = { items: [ { name: 'Prisma troubleshooting', - url: '/guides/database/prisma/prisma-troubleshooting', + url: '/guides/database/prisma/prisma-troubleshooting' as `/${string}`, }, ], }, @@ -816,7 +887,7 @@ export const database: NavMenuConstant = { items: [ { name: 'Connecting to your database', - url: '/guides/database/connecting-to-postgres', + url: '/guides/database/connecting-to-postgres' as `/${string}`, }, { name: 'Importing data', url: '/guides/database/import-data' }, { name: 'Securing your data', url: '/guides/database/secure-data' }, @@ -828,16 +899,16 @@ export const database: NavMenuConstant = { items: [ { name: 'Managing tables, views, and data', - url: '/guides/database/tables', + url: '/guides/database/tables' as `/${string}`, }, { name: 'Working with arrays', - url: '/guides/database/arrays', + url: '/guides/database/arrays' as `/${string}`, }, { name: 'Managing indexes', url: '/guides/database/postgres/indexes' }, { name: 'Querying joins and nested tables', - url: '/guides/database/joins-and-nesting', + url: '/guides/database/joins-and-nesting' as `/${string}`, }, { name: 'JSON and unstructured data', url: '/guides/database/json' }, ], @@ -848,32 +919,32 @@ export const database: NavMenuConstant = { items: [ { name: 'Implementing cascade deletes', - url: '/guides/database/postgres/cascade-deletes', + url: '/guides/database/postgres/cascade-deletes' as `/${string}`, }, { name: 'Managing enums', url: '/guides/database/postgres/enums' }, { name: 'Managing database functions', - url: '/guides/database/functions', + url: '/guides/database/functions' as `/${string}`, }, { name: 'Managing database triggers', - url: '/guides/database/postgres/triggers', + url: '/guides/database/postgres/triggers' as `/${string}`, }, { name: 'Managing database webhooks', - url: '/guides/database/webhooks', + url: '/guides/database/webhooks' as `/${string}`, }, { name: 'Using Full Text Search', - url: '/guides/database/full-text-search', + url: '/guides/database/full-text-search' as `/${string}`, }, { name: 'Partitioning your tables', - url: '/guides/database/partitions', + url: '/guides/database/partitions' as `/${string}`, }, { name: 'Managing connections', - url: '/guides/database/connection-management', + url: '/guides/database/connection-management' as `/${string}`, }, ], }, @@ -883,7 +954,7 @@ export const database: NavMenuConstant = { items: [ { name: 'Overview', - url: '/guides/database/orioledb', + url: '/guides/database/orioledb' as `/${string}`, }, ], }, @@ -893,32 +964,32 @@ export const database: NavMenuConstant = { items: [ { name: 'Row Level Security', - url: '/guides/database/postgres/row-level-security', + url: '/guides/database/postgres/row-level-security' as `/${string}`, }, { name: 'Column Level Security', - url: '/guides/database/postgres/column-level-security', + url: '/guides/database/postgres/column-level-security' as `/${string}`, }, { name: 'Hardening the Data API', - url: '/guides/database/hardening-data-api', + url: '/guides/database/hardening-data-api' as `/${string}`, }, { name: 'Custom Claims & RBAC', - url: '/guides/database/postgres/custom-claims-and-role-based-access-control-rbac', + url: '/guides/database/postgres/custom-claims-and-role-based-access-control-rbac' as `/${string}`, }, { name: 'Managing Postgres Roles', - url: '/guides/database/postgres/roles', + url: '/guides/database/postgres/roles' as `/${string}`, }, { name: 'Using Custom Postgres Roles', - url: '/guides/storage/schema/custom-roles', + url: '/guides/storage/schema/custom-roles' as `/${string}`, }, { name: 'Managing secrets with Vault', url: '/guides/database/vault' }, { name: 'Superuser Access and Unsupported Operations', - url: '/guides/database/postgres/roles-superuser', + url: '/guides/database/postgres/roles-superuser' as `/${string}`, }, ], }, @@ -928,20 +999,20 @@ export const database: NavMenuConstant = { items: [ { name: 'Database configuration', - url: '/guides/database/postgres/configuration', + url: '/guides/database/postgres/configuration' as `/${string}`, }, { name: 'Query optimization', - url: '/guides/database/query-optimization', + url: '/guides/database/query-optimization' as `/${string}`, }, { name: 'Database Advisors', - url: '/guides/database/database-advisors', + url: '/guides/database/database-advisors' as `/${string}`, }, { name: 'Testing your database', url: '/guides/database/testing' }, { name: 'Customizing Postgres config', - url: '/guides/database/custom-postgres-config', + url: '/guides/database/custom-postgres-config' as `/${string}`, }, ], }, @@ -951,23 +1022,23 @@ export const database: NavMenuConstant = { items: [ { name: 'Timeouts', - url: '/guides/database/postgres/timeouts', + url: '/guides/database/postgres/timeouts' as `/${string}`, }, { name: 'Debugging and monitoring', - url: '/guides/database/inspect', + url: '/guides/database/inspect' as `/${string}`, }, { name: 'Debugging performance issues', - url: '/guides/database/debugging-performance', + url: '/guides/database/debugging-performance' as `/${string}`, }, { name: 'Supavisor', - url: '/guides/database/supavisor', + url: '/guides/database/supavisor' as `/${string}`, }, { name: 'Troubleshooting', - url: '/guides/database/troubleshooting', + url: '/guides/database/troubleshooting' as `/${string}`, }, ], }, @@ -980,11 +1051,11 @@ export const database: NavMenuConstant = { { name: 'Overview', url: '/guides/database/replication' }, { name: 'Setting up replication', - url: '/guides/database/replication/setting-up-replication', + url: '/guides/database/replication/setting-up-replication' as `/${string}`, }, { name: 'Monitoring replication', - url: '/guides/database/replication/monitoring-replication', + url: '/guides/database/replication/monitoring-replication' as `/${string}`, }, { name: 'FAQ', url: '/guides/database/replication/faq' }, ], @@ -996,107 +1067,107 @@ export const database: NavMenuConstant = { { name: 'Overview', url: '/guides/database/extensions' }, { name: 'HypoPG: Hypothetical indexes', - url: '/guides/database/extensions/hypopg', + url: '/guides/database/extensions/hypopg' as `/${string}`, }, { name: 'plv8 (deprecated)', - url: '/guides/database/extensions/plv8', + url: '/guides/database/extensions/plv8' as `/${string}`, }, { name: 'http: RESTful Client', - url: '/guides/database/extensions/http', + url: '/guides/database/extensions/http' as `/${string}`, }, { name: 'index_advisor: Query optimization', - url: '/guides/database/extensions/index_advisor', + url: '/guides/database/extensions/index_advisor' as `/${string}`, }, { name: 'PGAudit: Postgres Auditing', - url: '/guides/database/extensions/pgaudit', + url: '/guides/database/extensions/pgaudit' as `/${string}`, }, { name: 'pgjwt (deprecated)', - url: '/guides/database/extensions/pgjwt', + url: '/guides/database/extensions/pgjwt' as `/${string}`, }, { name: 'PGroonga: Multilingual Full Text Search', - url: '/guides/database/extensions/pgroonga', + url: '/guides/database/extensions/pgroonga' as `/${string}`, }, { name: 'pgRouting: Geospatial Routing', - url: '/guides/database/extensions/pgrouting', + url: '/guides/database/extensions/pgrouting' as `/${string}`, }, { name: 'pg_cron: Schedule Recurring Jobs', - url: '/guides/database/extensions/pg_cron', + url: '/guides/database/extensions/pg_cron' as `/${string}`, }, { name: 'pg_graphql: GraphQL Support', - url: '/guides/database/extensions/pg_graphql', + url: '/guides/database/extensions/pg_graphql' as `/${string}`, }, { name: 'pg_hashids: Short UIDs', - url: '/guides/database/extensions/pg_hashids', + url: '/guides/database/extensions/pg_hashids' as `/${string}`, }, { name: 'pg_jsonschema: JSON Schema Validation', - url: '/guides/database/extensions/pg_jsonschema', + url: '/guides/database/extensions/pg_jsonschema' as `/${string}`, }, { name: 'pg_net: Async Networking', - url: '/guides/database/extensions/pg_net', + url: '/guides/database/extensions/pg_net' as `/${string}`, }, { name: 'pg_plan_filter: Restrict Total Cost', - url: '/guides/database/extensions/pg_plan_filter', + url: '/guides/database/extensions/pg_plan_filter' as `/${string}`, }, { name: 'postgres_fdw: query data from an external Postgres server', - url: '/guides/database/extensions/postgres_fdw', + url: '/guides/database/extensions/postgres_fdw' as `/${string}`, }, { name: 'pgvector: Embeddings and vector similarity', - url: '/guides/database/extensions/pgvector', + url: '/guides/database/extensions/pgvector' as `/${string}`, }, { name: 'pg_stat_statements: SQL Planning and Execution Statistics', - url: '/guides/database/extensions/pg_stat_statements', + url: '/guides/database/extensions/pg_stat_statements' as `/${string}`, }, { name: 'pg_repack: Storage Optimization', - url: '/guides/database/extensions/pg_repack', + url: '/guides/database/extensions/pg_repack' as `/${string}`, }, { name: 'PostGIS: Geo queries', - url: '/guides/database/extensions/postgis', + url: '/guides/database/extensions/postgis' as `/${string}`, }, { name: 'pgmq: Queues', - url: '/guides/database/extensions/pgmq', + url: '/guides/database/extensions/pgmq' as `/${string}`, }, { name: 'pgsodium (pending deprecation): Encryption Features', - url: '/guides/database/extensions/pgsodium', + url: '/guides/database/extensions/pgsodium' as `/${string}`, }, { name: 'pgTAP: Unit Testing', - url: '/guides/database/extensions/pgtap', + url: '/guides/database/extensions/pgtap' as `/${string}`, }, { name: 'plpgsql_check: PL/pgSQL Linter', - url: '/guides/database/extensions/plpgsql_check', + url: '/guides/database/extensions/plpgsql_check' as `/${string}`, }, { name: 'timescaledb (deprecated)', - url: '/guides/database/extensions/timescaledb', + url: '/guides/database/extensions/timescaledb' as `/${string}`, }, { name: 'uuid-ossp: Unique Identifiers', - url: '/guides/database/extensions/uuid-ossp', + url: '/guides/database/extensions/uuid-ossp' as `/${string}`, }, { name: 'RUM: inverted index for full-text search', - url: '/guides/database/extensions/rum', + url: '/guides/database/extensions/rum' as `/${string}`, }, ], }, @@ -1106,75 +1177,75 @@ export const database: NavMenuConstant = { items: [ { name: 'Overview', - url: '/guides/database/extensions/wrappers/overview', + url: '/guides/database/extensions/wrappers/overview' as `/${string}`, }, { name: 'Connecting to Auth0', - url: '/guides/database/extensions/wrappers/auth0', + url: '/guides/database/extensions/wrappers/auth0' as `/${string}`, }, { name: 'Connecting to Airtable', - url: '/guides/database/extensions/wrappers/airtable', + url: '/guides/database/extensions/wrappers/airtable' as `/${string}`, }, { name: 'Connecting to AWS Cognito', - url: '/guides/database/extensions/wrappers/cognito', + url: '/guides/database/extensions/wrappers/cognito' as `/${string}`, }, { name: 'Connecting to AWS S3', - url: '/guides/database/extensions/wrappers/s3', + url: '/guides/database/extensions/wrappers/s3' as `/${string}`, }, { name: 'Connecting to BigQuery', - url: '/guides/database/extensions/wrappers/bigquery', + url: '/guides/database/extensions/wrappers/bigquery' as `/${string}`, }, { name: 'Connecting to Clerk', - url: '/guides/database/extensions/wrappers/clerk', + url: '/guides/database/extensions/wrappers/clerk' as `/${string}`, }, { name: 'Connecting to ClickHouse', - url: '/guides/database/extensions/wrappers/clickhouse', + url: '/guides/database/extensions/wrappers/clickhouse' as `/${string}`, }, { name: 'Connecting to DuckDB', - url: '/guides/database/extensions/wrappers/duckdb', + url: '/guides/database/extensions/wrappers/duckdb' as `/${string}`, }, { name: 'Connecting to Firebase', - url: '/guides/database/extensions/wrappers/firebase', + url: '/guides/database/extensions/wrappers/firebase' as `/${string}`, }, { name: 'Connecting to Iceberg', - url: '/guides/database/extensions/wrappers/iceberg', + url: '/guides/database/extensions/wrappers/iceberg' as `/${string}`, }, { name: 'Connecting to Logflare', - url: '/guides/database/extensions/wrappers/logflare', + url: '/guides/database/extensions/wrappers/logflare' as `/${string}`, }, { name: 'Connecting to MSSQL', - url: '/guides/database/extensions/wrappers/mssql', + url: '/guides/database/extensions/wrappers/mssql' as `/${string}`, }, { name: 'Connecting to Notion', - url: '/guides/database/extensions/wrappers/notion', + url: '/guides/database/extensions/wrappers/notion' as `/${string}`, }, { name: 'Connecting to Paddle', - url: '/guides/database/extensions/wrappers/paddle', + url: '/guides/database/extensions/wrappers/paddle' as `/${string}`, }, { name: 'Connecting to Redis', - url: '/guides/database/extensions/wrappers/redis', + url: '/guides/database/extensions/wrappers/redis' as `/${string}`, }, { name: 'Connecting to Snowflake', - url: '/guides/database/extensions/wrappers/snowflake', + url: '/guides/database/extensions/wrappers/snowflake' as `/${string}`, }, { name: 'Connecting to Stripe', - url: '/guides/database/extensions/wrappers/stripe', + url: '/guides/database/extensions/wrappers/stripe' as `/${string}`, }, ], }, @@ -1184,19 +1255,19 @@ export const database: NavMenuConstant = { items: [ { name: 'Drop All Tables in Schema', - url: '/guides/database/postgres/dropping-all-tables-in-schema', + url: '/guides/database/postgres/dropping-all-tables-in-schema' as `/${string}`, }, { name: 'Select First Row per Group', - url: '/guides/database/postgres/first-row-in-group', + url: '/guides/database/postgres/first-row-in-group' as `/${string}`, }, { name: 'Print PostgreSQL Version', - url: '/guides/database/postgres/which-version-of-postgres', + url: '/guides/database/postgres/which-version-of-postgres' as `/${string}`, }, { name: 'Replicating from Supabase to External Postgres', - url: '/guides/database/postgres/setup-replication-external', + url: '/guides/database/postgres/setup-replication-external' as `/${string}`, }, ], }, @@ -1284,35 +1355,35 @@ export const api: NavMenuConstant = { items: [ { name: 'Managing tables, views, and data', - url: '/guides/database/tables', + url: '/guides/database/tables' as `/${string}`, }, { name: 'Querying joins and nested tables', - url: '/guides/database/joins-and-nesting', + url: '/guides/database/joins-and-nesting' as `/${string}`, }, { name: 'JSON and unstructured data', - url: '/guides/database/json', + url: '/guides/database/json' as `/${string}`, }, { name: 'Managing database functions', - url: '/guides/database/functions', + url: '/guides/database/functions' as `/${string}`, }, { name: 'Using full-text search', - url: '/guides/database/full-text-search', + url: '/guides/database/full-text-search' as `/${string}`, }, { name: 'Debugging performance issues', - url: '/guides/database/debugging-performance', + url: '/guides/database/debugging-performance' as `/${string}`, }, { name: 'Using custom schemas', - url: '/guides/api/using-custom-schemas', + url: '/guides/api/using-custom-schemas' as `/${string}`, }, { name: 'Converting from SQL to JavaScript API', - url: '/guides/api/sql-to-api', + url: '/guides/api/sql-to-api' as `/${string}`, }, ], }, @@ -1359,15 +1430,15 @@ export const functions: NavMenuConstant = { items: [ { name: 'Quickstart (Dashboard)', - url: '/guides/functions/quickstart-dashboard', + url: '/guides/functions/quickstart-dashboard' as `/${string}`, }, { name: 'Quickstart (CLI)', - url: '/guides/functions/quickstart', + url: '/guides/functions/quickstart' as `/${string}`, }, { name: 'Development Environment', - url: '/guides/functions/development-environment', + url: '/guides/functions/development-environment' as `/${string}`, }, ], }, @@ -1388,7 +1459,7 @@ export const functions: NavMenuConstant = { { name: 'Routing', url: '/guides/functions/routing' }, { name: 'Deploy to Production', - url: '/guides/functions/deploy', + url: '/guides/functions/deploy' as `/${string}`, }, ], }, @@ -1398,19 +1469,19 @@ export const functions: NavMenuConstant = { items: [ { name: 'Local Debugging with DevTools', - url: '/guides/functions/debugging-tools', + url: '/guides/functions/debugging-tools' as `/${string}`, }, { name: 'Testing your Functions', - url: '/guides/functions/unit-test', + url: '/guides/functions/unit-test' as `/${string}`, }, { name: 'Logging', - url: '/guides/functions/logging', + url: '/guides/functions/logging' as `/${string}`, }, { name: 'Troubleshooting', - url: '/guides/functions/troubleshooting', + url: '/guides/functions/troubleshooting' as `/${string}`, }, ], }, @@ -1420,19 +1491,20 @@ export const functions: NavMenuConstant = { items: [ { name: 'Regional invocations', - url: '/guides/functions/regional-invocation', + url: '/guides/functions/regional-invocation' as `/${string}`, }, { name: 'Status codes', - url: '/guides/functions/status-codes', + url: '/guides/functions/status-codes' as `/${string}`, }, { name: 'Limits', - url: '/guides/functions/limits', + url: '/guides/functions/limits' as `/${string}`, }, { name: 'Pricing', - url: '/guides/functions/pricing', + url: '/guides/functions/pricing' as `/${string}`, + enabled: billingEnabled, }, ], }, @@ -1463,63 +1535,63 @@ export const functions: NavMenuConstant = { items: [ { name: 'Auth Send Email Hook', - url: '/guides/functions/examples/auth-send-email-hook-react-email-resend', + url: '/guides/functions/examples/auth-send-email-hook-react-email-resend' as `/${string}`, }, { name: 'CORS support for invoking from the browser', - url: '/guides/functions/cors', + url: '/guides/functions/cors' as `/${string}`, }, { name: 'Scheduling Functions', - url: '/guides/functions/schedule-functions', + url: '/guides/functions/schedule-functions' as `/${string}`, }, { name: 'Sending Push Notifications', - url: '/guides/functions/examples/push-notifications', + url: '/guides/functions/examples/push-notifications' as `/${string}`, }, { name: 'Generating AI images', - url: '/guides/functions/examples/amazon-bedrock-image-generator', + url: '/guides/functions/examples/amazon-bedrock-image-generator' as `/${string}`, }, { name: 'Generating OG images ', - url: '/guides/functions/examples/og-image', + url: '/guides/functions/examples/og-image' as `/${string}`, }, { name: 'Semantic AI Search', - url: '/guides/functions/examples/semantic-search', + url: '/guides/functions/examples/semantic-search' as `/${string}`, }, { name: 'CAPTCHA support with Cloudflare Turnstile', - url: '/guides/functions/examples/cloudflare-turnstile', + url: '/guides/functions/examples/cloudflare-turnstile' as `/${string}`, }, { name: 'Building a Discord Bot', - url: '/guides/functions/examples/discord-bot', + url: '/guides/functions/examples/discord-bot' as `/${string}`, }, { name: 'Building a Telegram Bot', - url: '/guides/functions/examples/telegram-bot', + url: '/guides/functions/examples/telegram-bot' as `/${string}`, }, { name: 'Handling Stripe Webhooks ', - url: '/guides/functions/examples/stripe-webhooks', + url: '/guides/functions/examples/stripe-webhooks' as `/${string}`, }, { name: 'Rate-limiting with Redis', - url: '/guides/functions/examples/rate-limiting', + url: '/guides/functions/examples/rate-limiting' as `/${string}`, }, { name: 'Taking Screenshots with Puppeteer', - url: '/guides/functions/examples/screenshots', + url: '/guides/functions/examples/screenshots' as `/${string}`, }, { name: 'Slack Bot responding to mentions', - url: '/guides/functions/examples/slack-bot-mention', + url: '/guides/functions/examples/slack-bot-mention' as `/${string}`, }, { name: 'Image Transformation & Optimization', - url: '/guides/functions/examples/image-manipulation', + url: '/guides/functions/examples/image-manipulation' as `/${string}`, }, ], }, @@ -1530,40 +1602,40 @@ export const functions: NavMenuConstant = { { name: 'Dart Edge on Supabase', url: '/guides/functions/dart-edge' }, { name: 'Browserless.io', - url: '/guides/functions/examples/screenshots', + url: '/guides/functions/examples/screenshots' as `/${string}`, }, { name: 'Hugging Face', - url: '/guides/ai/examples/huggingface-image-captioning', + url: '/guides/ai/examples/huggingface-image-captioning' as `/${string}`, }, { name: 'Monitoring with Sentry', - url: '/guides/functions/examples/sentry-monitoring', + url: '/guides/functions/examples/sentry-monitoring' as `/${string}`, }, { name: 'OpenAI API', url: '/guides/ai/examples/openai' }, { name: 'React Email', - url: '/guides/functions/examples/auth-send-email-hook-react-email-resend', + url: '/guides/functions/examples/auth-send-email-hook-react-email-resend' as `/${string}`, }, { name: 'Sending Emails with Resend', - url: '/guides/functions/examples/send-emails', + url: '/guides/functions/examples/send-emails' as `/${string}`, }, { name: 'Upstash Redis', - url: '/guides/functions/examples/upstash-redis', + url: '/guides/functions/examples/upstash-redis' as `/${string}`, }, { name: 'Type-Safe SQL with Kysely', - url: '/guides/functions/kysely-postgres', + url: '/guides/functions/kysely-postgres' as `/${string}`, }, { name: 'Text To Speech with ElevenLabs', - url: '/guides/functions/examples/elevenlabs-generate-speech-stream', + url: '/guides/functions/examples/elevenlabs-generate-speech-stream' as `/${string}`, }, { name: 'Speech Transcription with ElevenLabs', - url: '/guides/functions/examples/elevenlabs-transcribe-speech', + url: '/guides/functions/examples/elevenlabs-transcribe-speech' as `/${string}`, }, ], }, @@ -1591,7 +1663,7 @@ export const realtime: NavMenuConstant = { { name: 'Presence', url: '/guides/realtime/presence' }, { name: 'Postgres Changes', - url: '/guides/realtime/postgres-changes', + url: '/guides/realtime/postgres-changes' as `/${string}`, }, { name: 'Settings', url: '/guides/realtime/settings' }, ], @@ -1607,19 +1679,19 @@ export const realtime: NavMenuConstant = { items: [ { name: 'Subscribing to Database Changes', - url: '/guides/realtime/subscribing-to-database-changes', + url: '/guides/realtime/subscribing-to-database-changes' as `/${string}`, }, { name: 'Using Realtime with Next.js', - url: '/guides/realtime/realtime-with-nextjs', + url: '/guides/realtime/realtime-with-nextjs' as `/${string}`, }, { name: 'Using Realtime Presence with Flutter', - url: '/guides/realtime/realtime-user-presence', + url: '/guides/realtime/realtime-user-presence' as `/${string}`, }, { name: 'Listening to Postgres Changes with Flutter', - url: '/guides/realtime/realtime-listening-flutter', + url: '/guides/realtime/realtime-listening-flutter' as `/${string}`, }, ], }, @@ -1628,7 +1700,11 @@ export const realtime: NavMenuConstant = { url: undefined, items: [ { name: 'Quotas', url: '/guides/realtime/quotas' }, - { name: 'Pricing', url: '/guides/realtime/pricing' }, + { + name: 'Pricing', + url: '/guides/realtime/pricing' as `/${string}`, + enabled: billingEnabled, + }, { name: 'Architecture', url: '/guides/realtime/architecture' }, { name: 'Protocol', url: '/guides/realtime/protocol', items: [] }, { name: 'Benchmarks', url: '/guides/realtime/benchmarks' }, @@ -1659,7 +1735,7 @@ export const storage: NavMenuConstant = { { name: 'Fundamentals', url: '/guides/storage/buckets/fundamentals' }, { name: 'Creating Buckets', - url: '/guides/storage/buckets/creating-buckets', + url: '/guides/storage/buckets/creating-buckets' as `/${string}`, }, ], }, @@ -1669,11 +1745,11 @@ export const storage: NavMenuConstant = { items: [ { name: 'Ownership', - url: '/guides/storage/security/ownership', + url: '/guides/storage/security/ownership' as `/${string}`, }, { name: 'Access Control', - url: '/guides/storage/security/access-control', + url: '/guides/storage/security/access-control' as `/${string}`, }, ], }, @@ -1683,15 +1759,15 @@ export const storage: NavMenuConstant = { items: [ { name: 'Standard Uploads', - url: '/guides/storage/uploads/standard-uploads', + url: '/guides/storage/uploads/standard-uploads' as `/${string}`, }, { name: 'Resumable Uploads', - url: '/guides/storage/uploads/resumable-uploads', + url: '/guides/storage/uploads/resumable-uploads' as `/${string}`, }, { name: 'S3 Uploads', - url: '/guides/storage/uploads/s3-uploads', + url: '/guides/storage/uploads/s3-uploads' as `/${string}`, }, { name: 'Limits', url: '/guides/storage/uploads/file-limits' }, ], @@ -1703,11 +1779,11 @@ export const storage: NavMenuConstant = { { name: 'Serving assets', url: '/guides/storage/serving/downloads' }, { name: 'Image Transformations', - url: '/guides/storage/serving/image-transformations', + url: '/guides/storage/serving/image-transformations' as `/${string}`, }, { name: 'Bandwidth & Storage Egress', - url: '/guides/storage/serving/bandwidth', + url: '/guides/storage/serving/bandwidth' as `/${string}`, }, ], }, @@ -1717,7 +1793,11 @@ export const storage: NavMenuConstant = { items: [ { name: 'Copy / Move Objects', url: '/guides/storage/management/copy-move-objects' }, { name: 'Delete Objects', url: '/guides/storage/management/delete-objects' }, - { name: 'Pricing', url: '/guides/storage/management/pricing' }, + { + name: 'Pricing', + url: '/guides/storage/management/pricing' as `/${string}`, + enabled: billingEnabled, + }, ], }, { @@ -1735,15 +1815,15 @@ export const storage: NavMenuConstant = { { name: 'Introduction', url: '/guides/storage/analytics/introduction' }, { name: 'Creating Analytics Buckets', - url: '/guides/storage/analytics/creating-analytics-buckets', + url: '/guides/storage/analytics/creating-analytics-buckets' as `/${string}`, }, { name: 'Connecting to Analytics Buckets', - url: '/guides/storage/analytics/connecting-to-analytics-bucket', + url: '/guides/storage/analytics/connecting-to-analytics-bucket' as `/${string}`, }, { name: 'Limits', - url: '/guides/storage/analytics/limits', + url: '/guides/storage/analytics/limits' as `/${string}`, }, ], }, @@ -1772,7 +1852,7 @@ export const storage: NavMenuConstant = { { name: 'Database Design', url: '/guides/storage/schema/design' }, { name: 'Helper Functions', - url: '/guides/storage/schema/helper-functions', + url: '/guides/storage/schema/helper-functions' as `/${string}`, }, { name: 'Custom Roles', url: '/guides/storage/schema/custom-roles' }, ], @@ -1819,20 +1899,20 @@ export const ai = { }, { name: 'Automatic embeddings', - url: '/guides/ai/automatic-embeddings', + url: '/guides/ai/automatic-embeddings' as `/${string}`, }, { name: 'Engineering for scale', - url: '/guides/ai/engineering-for-scale', + url: '/guides/ai/engineering-for-scale' as `/${string}`, }, { name: 'Choosing Compute Add-on', - url: '/guides/ai/choosing-compute-addon', + url: '/guides/ai/choosing-compute-addon' as `/${string}`, }, { name: 'Going to Production', url: '/guides/ai/going-to-prod' }, { name: 'RAG with Permissions', - url: '/guides/ai/rag-with-permissions', + url: '/guides/ai/rag-with-permissions' as `/${string}`, }, ], }, @@ -1851,25 +1931,25 @@ export const ai = { items: [ { name: 'OpenAI completions using Edge Functions', - url: '/guides/ai/examples/openai', + url: '/guides/ai/examples/openai' as `/${string}`, }, { name: 'Generate image captions using Hugging Face', - url: '/guides/ai/examples/huggingface-image-captioning', + url: '/guides/ai/examples/huggingface-image-captioning' as `/${string}`, }, { name: 'Generate Embeddings', - url: '/guides/ai/quickstarts/generate-text-embeddings', + url: '/guides/ai/quickstarts/generate-text-embeddings' as `/${string}`, }, { name: 'Adding generative Q&A to your documentation', - url: '/guides/ai/examples/headless-vector-search', + url: '/guides/ai/examples/headless-vector-search' as `/${string}`, }, { name: 'Adding generative Q&A to your Next.js site', - url: '/guides/ai/examples/nextjs-vector-search', + url: '/guides/ai/examples/nextjs-vector-search' as `/${string}`, }, ], }, @@ -1890,32 +1970,32 @@ export const ai = { items: [ { name: 'Developing locally with Vecs', - url: '/guides/ai/vecs-python-client', + url: '/guides/ai/vecs-python-client' as `/${string}`, }, { name: 'Creating and managing collections', - url: '/guides/ai/quickstarts/hello-world', + url: '/guides/ai/quickstarts/hello-world' as `/${string}`, }, { name: 'Text Deduplication', - url: '/guides/ai/quickstarts/text-deduplication', + url: '/guides/ai/quickstarts/text-deduplication' as `/${string}`, }, { name: 'Face similarity search', - url: '/guides/ai/quickstarts/face-similarity', + url: '/guides/ai/quickstarts/face-similarity' as `/${string}`, }, { name: 'Image search with OpenAI CLIP', - url: '/guides/ai/examples/image-search-openai-clip', + url: '/guides/ai/examples/image-search-openai-clip' as `/${string}`, }, { name: 'Semantic search with Amazon Titan', - url: '/guides/ai/examples/semantic-image-search-amazon-titan', + url: '/guides/ai/examples/semantic-image-search-amazon-titan' as `/${string}`, }, { name: 'Building ChatGPT Plugins', - url: '/guides/ai/examples/building-chatgpt-plugins', + url: '/guides/ai/examples/building-chatgpt-plugins' as `/${string}`, }, ], }, @@ -1925,31 +2005,31 @@ export const ai = { items: [ { name: 'LangChain', - url: '/guides/ai/langchain', + url: '/guides/ai/langchain' as `/${string}`, }, { name: 'Hugging Face', - url: '/guides/ai/hugging-face', + url: '/guides/ai/hugging-face' as `/${string}`, }, { name: 'Google Colab', - url: '/guides/ai/google-colab', + url: '/guides/ai/google-colab' as `/${string}`, }, { name: 'LlamaIndex', - url: '/guides/ai/integrations/llamaindex', + url: '/guides/ai/integrations/llamaindex' as `/${string}`, }, { name: 'Roboflow', - url: '/guides/ai/integrations/roboflow', + url: '/guides/ai/integrations/roboflow' as `/${string}`, }, { name: 'Amazon Bedrock', - url: '/guides/ai/integrations/amazon-bedrock', + url: '/guides/ai/integrations/amazon-bedrock' as `/${string}`, }, { name: 'Mixpeek', - url: '/guides/ai/examples/mixpeek-video-search', + url: '/guides/ai/examples/mixpeek-video-search' as `/${string}`, }, ], }, @@ -1978,23 +2058,23 @@ export const local_development: NavMenuConstant = { { name: 'Getting started', url: '/guides/local-development/overview' }, { name: 'Declarative database schemas', - url: '/guides/local-development/declarative-database-schemas', + url: '/guides/local-development/declarative-database-schemas' as `/${string}`, }, { name: 'Seeding your database', - url: '/guides/local-development/seeding-your-database', + url: '/guides/local-development/seeding-your-database' as `/${string}`, }, { name: 'Managing config and secrets', - url: '/guides/local-development/managing-config', + url: '/guides/local-development/managing-config' as `/${string}`, }, { name: 'Restoring downloaded backup', - url: '/guides/local-development/restoring-downloaded-backup', + url: '/guides/local-development/restoring-downloaded-backup' as `/${string}`, }, { name: 'Customizing email templates', - url: '/guides/local-development/customizing-email-templates', + url: '/guides/local-development/customizing-email-templates' as `/${string}`, }, ], }, @@ -2005,12 +2085,12 @@ export const local_development: NavMenuConstant = { { name: 'Getting started', url: '/guides/local-development/testing/overview' }, { name: 'pgTAP advanced guide', - url: '/guides/local-development/testing/pgtap-extended', + url: '/guides/local-development/testing/pgtap-extended' as `/${string}`, }, { name: 'Database testing', url: '/guides/database/testing' }, { name: 'RLS policies testing', - url: '/guides/database/extensions/pgtap#testing-rls-policies', + url: '/guides/database/extensions/pgtap#testing-rls-policies' as `/${string}`, }, ], }, @@ -2120,7 +2200,7 @@ export const security: NavMenuConstant = { { name: 'Production Checklist', url: '/guides/deployment/going-into-prod' }, { name: 'Shared Responsibility Model', - url: '/guides/deployment/shared-responsibility-model', + url: '/guides/deployment/shared-responsibility-model' as `/${string}`, }, { name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' }, { name: 'Hardening the Data API', url: '/guides/database/hardening-data-api' }, @@ -2155,11 +2235,11 @@ export const platform: NavMenuConstant = { items: [ { name: 'Restore Dashboard backup', - url: '/guides/platform/migrating-within-supabase/dashboard-restore', + url: '/guides/platform/migrating-within-supabase/dashboard-restore' as `/${string}`, }, { name: 'Backup and restore using CLI', - url: '/guides/platform/migrating-within-supabase/backup-restore', + url: '/guides/platform/migrating-within-supabase/backup-restore' as `/${string}`, }, ], }, @@ -2176,7 +2256,7 @@ export const platform: NavMenuConstant = { items: [ { name: 'Access Control', - url: '/guides/platform/access-control', + url: '/guides/platform/access-control' as `/${string}`, }, { name: 'Multi-factor Authentication', @@ -2184,13 +2264,13 @@ export const platform: NavMenuConstant = { items: [ { name: 'Enforce MFA on organization', - url: '/guides/platform/mfa/org-mfa-enforcement', + url: '/guides/platform/mfa/org-mfa-enforcement' as `/${string}`, }, ], }, { name: 'Transfer Project', - url: '/guides/platform/project-transfer', + url: '/guides/platform/project-transfer' as `/${string}`, }, { name: 'Restore to a new project', @@ -2203,7 +2283,7 @@ export const platform: NavMenuConstant = { { name: 'SSO with Azure AD', url: '/guides/platform/sso/azure' }, { name: 'SSO with Google Workspace', - url: '/guides/platform/sso/gsuite', + url: '/guides/platform/sso/gsuite' as `/${string}`, }, { name: 'SSO with Okta', url: '/guides/platform/sso/okta' }, ], @@ -2214,137 +2294,143 @@ export const platform: NavMenuConstant = { name: 'Platform Configuration', url: undefined, items: [ - { name: 'Regions', url: '/guides/platform/regions' }, - { name: 'Compute and Disk', url: '/guides/platform/compute-and-disk' }, - { name: 'Database Size', url: '/guides/platform/database-size' }, - { name: 'HIPAA Projects', url: '/guides/platform/hipaa-projects' }, + { name: 'Regions', url: '/guides/platform/regions' as `/${string}` }, + { name: 'Compute and Disk', url: '/guides/platform/compute-and-disk' as `/${string}` }, + { name: 'Database Size', url: '/guides/platform/database-size' as `/${string}` }, + { name: 'HIPAA Projects', url: '/guides/platform/hipaa-projects' as `/${string}` }, { name: 'Network Restrictions', - url: '/guides/platform/network-restrictions', + url: '/guides/platform/network-restrictions' as `/${string}`, + }, + { name: 'Performance Tuning', url: '/guides/platform/performance' as `/${string}` }, + { name: 'SSL Enforcement', url: '/guides/platform/ssl-enforcement' as `/${string}` }, + { + name: 'Default Platform Permissions', + url: '/guides/platform/permissions' as `/${string}`, }, - { name: 'Performance Tuning', url: '/guides/platform/performance' }, - { name: 'SSL Enforcement', url: '/guides/platform/ssl-enforcement' }, - { name: 'Default Platform Permissions', url: '/guides/platform/permissions' }, - { name: 'PrivateLink', url: '/guides/platform/privatelink' }, + { name: 'PrivateLink', url: '/guides/platform/privatelink' as `/${string}` }, ], }, { name: 'Billing', url: undefined, + enabled: billingEnabled, items: [ { name: 'About billing on Supabase', - url: '/guides/platform/billing-on-supabase', + url: '/guides/platform/billing-on-supabase' as `/${string}`, }, { name: 'Get set up for billing', - url: '/guides/platform/get-set-up-for-billing', + url: '/guides/platform/get-set-up-for-billing' as `/${string}`, }, { name: 'Manage your subscription', - url: '/guides/platform/manage-your-subscription', + url: '/guides/platform/manage-your-subscription' as `/${string}`, }, { name: 'Manage your usage', - url: '/guides/platform/manage-your-usage', + url: '/guides/platform/manage-your-usage' as `/${string}`, items: [ + { name: 'Usage limits', url: '/guides/platform/usage-limits' as `/${string}` }, + { name: 'Overages', url: '/guides/platform/overages' as `/${string}` }, { name: 'Compute', - url: '/guides/platform/manage-your-usage/compute', + url: '/guides/platform/manage-your-usage/compute' as `/${string}`, }, { name: 'Egress', - url: '/guides/platform/manage-your-usage/egress', + url: '/guides/platform/manage-your-usage/egress' as `/${string}`, }, { name: 'Disk Size', - url: '/guides/platform/manage-your-usage/disk-size', + url: '/guides/platform/manage-your-usage/disk-size' as `/${string}`, }, { name: 'Disk Throughput', - url: '/guides/platform/manage-your-usage/disk-throughput', + url: '/guides/platform/manage-your-usage/disk-throughput' as `/${string}`, }, { name: 'Disk IOPS', - url: '/guides/platform/manage-your-usage/disk-iops', + url: '/guides/platform/manage-your-usage/disk-iops' as `/${string}`, }, { name: 'Monthly Active Users', - url: '/guides/platform/manage-your-usage/monthly-active-users', + url: '/guides/platform/manage-your-usage/monthly-active-users' as `/${string}`, }, { name: 'Monthly Active Third-Party Users', - url: '/guides/platform/manage-your-usage/monthly-active-users-third-party', + url: '/guides/platform/manage-your-usage/monthly-active-users-third-party' as `/${string}`, }, { name: 'Monthly Active SSO Users', - url: '/guides/platform/manage-your-usage/monthly-active-users-sso', + url: '/guides/platform/manage-your-usage/monthly-active-users-sso' as `/${string}`, }, { name: 'Storage Size', - url: '/guides/platform/manage-your-usage/storage-size', + url: '/guides/platform/manage-your-usage/storage-size' as `/${string}`, }, { name: 'Storage Image Transformations', - url: '/guides/platform/manage-your-usage/storage-image-transformations', + url: '/guides/platform/manage-your-usage/storage-image-transformations' as `/${string}`, }, { name: 'Edge Function Invocations', - url: '/guides/platform/manage-your-usage/edge-function-invocations', + url: '/guides/platform/manage-your-usage/edge-function-invocations' as `/${string}`, }, { name: 'Realtime Messages', - url: '/guides/platform/manage-your-usage/realtime-messages', + url: '/guides/platform/manage-your-usage/realtime-messages' as `/${string}`, }, { name: 'Realtime Peak Connections', - url: '/guides/platform/manage-your-usage/realtime-peak-connections', + url: '/guides/platform/manage-your-usage/realtime-peak-connections' as `/${string}`, }, { name: 'Custom Domains', - url: '/guides/platform/manage-your-usage/custom-domains', + url: '/guides/platform/manage-your-usage/custom-domains' as `/${string}`, }, { name: 'Point-in-Time Recovery', - url: '/guides/platform/manage-your-usage/point-in-time-recovery', + url: '/guides/platform/manage-your-usage/point-in-time-recovery' as `/${string}`, }, { name: 'IPv4', - url: '/guides/platform/manage-your-usage/ipv4', + url: '/guides/platform/manage-your-usage/ipv4' as `/${string}`, }, { name: 'MFA Phone', - url: '/guides/platform/manage-your-usage/advanced-mfa-phone', + url: '/guides/platform/manage-your-usage/advanced-mfa-phone' as `/${string}`, }, { name: 'Read Replicas', - url: '/guides/platform/manage-your-usage/read-replicas', + url: '/guides/platform/manage-your-usage/read-replicas' as `/${string}`, }, { name: 'Branching', - url: '/guides/platform/manage-your-usage/branching', + url: '/guides/platform/manage-your-usage/branching' as `/${string}`, }, { name: 'Log Drains', - url: '/guides/platform/manage-your-usage/log-drains', + url: '/guides/platform/manage-your-usage/log-drains' as `/${string}`, }, ], }, { name: 'Your monthly invoice', - url: '/guides/platform/your-monthly-invoice', + url: '/guides/platform/your-monthly-invoice' as `/${string}`, }, { name: 'Control your costs', - url: '/guides/platform/cost-control', + url: '/guides/platform/cost-control' as `/${string}`, }, { name: 'Credits', - url: '/guides/platform/credits', + url: '/guides/platform/credits' as `/${string}`, }, { name: 'Billing FAQ', - url: '/guides/platform/billing-faq', + url: '/guides/platform/billing-faq' as `/${string}`, }, ], }, @@ -2363,27 +2449,27 @@ export const telemetry: NavMenuConstant = { items: [ { name: 'Logging', - url: '/guides/telemetry/logs', + url: '/guides/telemetry/logs' as `/${string}`, }, { name: 'Advanced log filtering', - url: '/guides/telemetry/advanced-log-filtering', + url: '/guides/telemetry/advanced-log-filtering' as `/${string}`, }, { name: 'Log drains', - url: '/guides/telemetry/log-drains', + url: '/guides/telemetry/log-drains' as `/${string}`, }, { name: 'Reports', - url: '/guides/telemetry/reports', + url: '/guides/telemetry/reports' as `/${string}`, }, { name: 'Metrics', - url: '/guides/telemetry/metrics', + url: '/guides/telemetry/metrics' as `/${string}`, }, { name: 'Sentry integration', - url: '/guides/telemetry/sentry-monitoring', + url: '/guides/telemetry/sentry-monitoring' as `/${string}`, }, ], }, @@ -2416,7 +2502,7 @@ export const self_hosting: NavMenuConstant = { items: [ { name: 'Reference', - url: '/reference/self-hosting-storage/introduction', + url: '/reference/self-hosting-storage/introduction' as `/${string}`, }, { name: 'Configuration', url: '/guides/self-hosting/storage/config' }, ], @@ -2426,7 +2512,7 @@ export const self_hosting: NavMenuConstant = { items: [ { name: 'Reference', - url: '/reference/self-hosting-realtime/introduction', + url: '/reference/self-hosting-realtime/introduction' as `/${string}`, }, { name: 'Configuration', url: '/guides/self-hosting/realtime/config' }, ], @@ -2480,12 +2566,12 @@ export const deployment: NavMenuConstant = { { name: 'Branching via GitHub', url: '/guides/deployment/branching/github-integration' }, { name: 'Branching via dashboard', - url: '/guides/deployment/branching/dashboard', + url: '/guides/deployment/branching/dashboard' as `/${string}`, }, { name: 'Working with branches', - url: '/guides/deployment/branching/working-with-branches', + url: '/guides/deployment/branching/working-with-branches' as `/${string}`, }, { name: 'Configuration', url: '/guides/deployment/branching/configuration' }, { name: 'Integrations', url: '/guides/deployment/branching/integrations' }, @@ -2506,7 +2592,7 @@ export const deployment: NavMenuConstant = { items: [ { name: 'Shared responsibility model', - url: '/guides/deployment/shared-responsibility-model', + url: '/guides/deployment/shared-responsibility-model' as `/${string}`, }, { name: 'Maturity model', url: '/guides/deployment/maturity-model' }, { name: 'Production checklist', url: '/guides/deployment/going-into-prod' }, @@ -2518,7 +2604,7 @@ export const deployment: NavMenuConstant = { items: [ { name: 'Generate types from your database', - url: '/guides/deployment/ci/generating-types', + url: '/guides/deployment/ci/generating-types' as `/${string}`, }, { name: 'Automated testing', url: '/guides/deployment/ci/testing' }, { name: 'Back up your database', url: '/guides/deployment/ci/backups' }, @@ -2554,7 +2640,7 @@ export const integrations: NavMenuConstant = { items: [ { name: 'OAuth scopes', - url: '/guides/integrations/build-a-supabase-integration/oauth-scopes', + url: '/guides/integrations/build-a-supabase-integration/oauth-scopes' as `/${string}`, }, ], }, @@ -2575,39 +2661,44 @@ export const reference = { name: 'supabase-js', url: '/reference/javascript/start', level: 'reference_javascript', - icon: '/img/icons/menu/reference-javascript', + icon: '/img/icons/menu/reference-javascript' as `/${string}`, }, { name: 'supabase-dart', url: '/reference/dart/start', level: 'reference_dart', - icon: '/img/icons/menu/reference-dart', + icon: '/img/icons/menu/reference-dart' as `/${string}`, + enabled: sdkDartEnabled, }, { name: 'supabase-csharp', url: '/reference/csharp/start', level: 'reference_csharp', - icon: '/img/icons/menu/reference-csharp', + icon: '/img/icons/menu/reference-csharp' as `/${string}`, + enabled: sdkCsharpEnabled, }, { name: 'supbase-python', url: '/reference/python/start', level: 'reference_python', - icon: '/img/icons/menu/reference-python', + icon: '/img/icons/menu/reference-python' as `/${string}`, + enabled: sdkPythonEnabled, }, { name: 'supbase-swift', url: '/reference/swift/start', level: 'reference_swift', items: [], - icon: '/img/icons/menu/reference-swift', + icon: '/img/icons/menu/reference-swift' as `/${string}`, + enabled: sdkSwiftEnabled, }, { name: 'supabase-kt', url: '/reference/kotlin/start', level: 'reference_kotlin', items: [], - icon: '/img/icons/menu/reference-kotlin', + icon: '/img/icons/menu/reference-kotlin' as `/${string}`, + enabled: sdkKotlinEnabled, }, ], }, @@ -2617,12 +2708,12 @@ export const reference = { { name: 'Supabase CLI', url: '/reference/cli/start', - icon: '/img/icons/menu/reference-cli', + icon: '/img/icons/menu/reference-cli' as `/${string}`, }, { name: 'Management API', url: '/reference/javascript', - icon: '/img/icons/menu/reference-api', + icon: '/img/icons/menu/reference-api' as `/${string}`, }, ], }, @@ -2651,6 +2742,7 @@ export const reference_javascript_v2 = { }, } +// TODO: How to? export const reference_dart_v1 = { icon: 'reference-dart', title: 'Flutter', @@ -2829,6 +2921,7 @@ export const references = [ description: 'something about the reference', icon: '/docs/img/icons/python-icon.svg', url: '/reference/python/start', + enabled: sdkPythonEnabled, }, { label: 'supabase-dart', @@ -2836,6 +2929,7 @@ export const references = [ description: 'something about the reference', icon: '/docs/img/icons/dart-icon.svg', url: '/reference/dart/start', + enabled: sdkDartEnabled, }, { label: 'supabase-csharp', @@ -2843,6 +2937,7 @@ export const references = [ description: 'something about the reference', icon: '/docs/img/icons/c-sharp-icon.svg', url: '/reference/csharp/start', + enabled: sdkCsharpEnabled, }, { label: 'supabase-swift', @@ -2850,6 +2945,7 @@ export const references = [ description: 'something about the reference', icon: '/docs/img/icons/swift-icon.svg', url: '/reference/swift/start', + enabled: sdkSwiftEnabled, }, { label: 'supabase-kt', @@ -2857,6 +2953,7 @@ export const references = [ description: 'something about the reference', icon: '/docs/img/icons/kotlin-icon.svg', url: '/reference/kotlin/start', + enabled: sdkKotlinEnabled, }, ], }, diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx index cc6c052a369eb..83e26413c3834 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx @@ -94,23 +94,25 @@ const ContentAccordionLink = React.memo(function ContentAccordionLink(props: any {props.subItem.items && props.subItem.items.length > 0 && ( - {props.subItem.items.map((subSubItem) => { - return ( -
  • - - {subSubItem.name} - -
  • - ) - })} + {props.subItem.items + .filter((subItem) => subItem.enabled !== false) + .map((subSubItem) => { + return ( +
  • + + {subSubItem.name} + +
  • + ) + })}
    )} @@ -153,28 +155,30 @@ const Content = (props) => { - {menu.items.map((x) => { - return ( -
    - {x.items && x.items.length > 0 ? ( -
    - {x.items.map((subItem, subItemIndex) => { - return ( - - ) - })} -
    - ) : x.url ? ( - - ) : null} -
    - ) - })} + {menu.items + .filter((item) => item.enabled !== false) + .map((x) => { + return ( +
    + {x.items && x.items.length > 0 ? ( +
    + {x.items.map((subItem, subItemIndex) => { + return ( + + ) + })} +
    + ) : x.url ? ( + + ) : null} +
    + ) + })} ) } diff --git a/apps/docs/content/_partials/billing/pricing/pricing_custom_domains.mdx b/apps/docs/content/_partials/billing/pricing/pricing_custom_domains.mdx new file mode 100644 index 0000000000000..3e5d3b686d5af --- /dev/null +++ b/apps/docs/content/_partials/billing/pricing/pricing_custom_domains.mdx @@ -0,0 +1,3 @@ +## Pricing + +For a detailed breakdown of how charges are calculated, refer to [Manage Custom Domain usage](/docs/guides/platform/manage-your-usage/custom-domains). diff --git a/apps/docs/content/_partials/billing/pricing/pricing_ipv4.mdx b/apps/docs/content/_partials/billing/pricing/pricing_ipv4.mdx new file mode 100644 index 0000000000000..e07ea856d3d5b --- /dev/null +++ b/apps/docs/content/_partials/billing/pricing/pricing_ipv4.mdx @@ -0,0 +1,3 @@ +## Pricing + +For a detailed breakdown of how charges are calculated, refer to [Manage IPv4 usage](/docs/guides/platform/manage-your-usage/ipv4). diff --git a/apps/docs/content/_partials/billing/pricing/pricing_mau_sso.mdx b/apps/docs/content/_partials/billing/pricing/pricing_mau_sso.mdx index bf5d2628bd6be..8ba7741e9b91a 100644 --- a/apps/docs/content/_partials/billing/pricing/pricing_mau_sso.mdx +++ b/apps/docs/content/_partials/billing/pricing/pricing_mau_sso.mdx @@ -1,2 +1,6 @@ +## Pricing + per SSO MAU. You are only charged for usage exceeding your subscription plan's quota. + +For a detailed breakdown of how charges are calculated, refer to [Manage Monthly Active SSO Users usage](/docs/guides/platform/manage-your-usage/monthly-active-users-sso). diff --git a/apps/docs/content/_partials/billing/pricing/pricing_mau_third_party.mdx b/apps/docs/content/_partials/billing/pricing/pricing_mau_third_party.mdx index aa7576751bd67..cbfccef3877ff 100644 --- a/apps/docs/content/_partials/billing/pricing/pricing_mau_third_party.mdx +++ b/apps/docs/content/_partials/billing/pricing/pricing_mau_third_party.mdx @@ -1,2 +1,6 @@ +## Pricing + per Third-Party MAU. You are only charged for usage exceeding your subscription plan's quota. + +For a detailed breakdown of how charges are calculated, refer to [Manage Monthly Active Third-Party Users usage](/docs/guides/platform/manage-your-usage/monthly-active-users-third-party). diff --git a/apps/docs/content/_partials/billing/pricing/pricing_mfa_phone.mdx b/apps/docs/content/_partials/billing/pricing/pricing_mfa_phone.mdx index 5635b9067061b..6708ec7a9d0d9 100644 --- a/apps/docs/content/_partials/billing/pricing/pricing_mfa_phone.mdx +++ b/apps/docs/content/_partials/billing/pricing/pricing_mfa_phone.mdx @@ -1,3 +1,5 @@ +## Pricing + per hour ( per month) for the first project. per hour ( per month) for every additional project. @@ -6,3 +8,5 @@ hour ( per month) for every additional project. | Pro | | | | | Team | | | | | Enterprise | Custom | Custom | Custom | + +For a detailed breakdown of how charges are calculated, refer to [Manage Advanced MFA Phone usage](/docs/guides/platform/manage-your-usage/advanced-mfa-phone). diff --git a/apps/docs/content/_partials/billing/pricing/pricing_pitr.mdx b/apps/docs/content/_partials/billing/pricing/pricing_pitr.mdx index 36b2c8801f144..98559804a695b 100644 --- a/apps/docs/content/_partials/billing/pricing/pricing_pitr.mdx +++ b/apps/docs/content/_partials/billing/pricing/pricing_pitr.mdx @@ -1,3 +1,5 @@ +### Pricing + Pricing depends on the recovery retention period, which determines how many days back you can restore data to any chosen point of up to seconds in granularity. | Recovery Retention Period in Days | Hourly Price USD | Monthly Price USD | @@ -5,3 +7,5 @@ Pricing depends on the recovery retention period, which determines how many days | 7 | | | | 14 | | | | 28 | | | + +For a detailed breakdown of how charges are calculated, refer to [Manage Point-in-Time Recovery usage](/docs/guides/platform/manage-your-usage/point-in-time-recovery). diff --git a/apps/docs/content/_partials/billing/pricing/pricing_storage_image_transformations.mdx b/apps/docs/content/_partials/billing/pricing/pricing_storage_image_transformations.mdx index 4c4d4d429dbbd..f8d93e45ab6a0 100644 --- a/apps/docs/content/_partials/billing/pricing/pricing_storage_image_transformations.mdx +++ b/apps/docs/content/_partials/billing/pricing/pricing_storage_image_transformations.mdx @@ -1,3 +1,5 @@ +## Pricing + per 1,000 origin images. You are only charged for usage exceeding your subscription plan's quota. @@ -12,3 +14,5 @@ The count resets at the start of each billing cycle. | Pro | 100 | per 1,000 origin images | | Team | 100 | per 1,000 origin images | | Enterprise | Custom | Custom | + +For a detailed breakdown of how charges are calculated, refer to [Manage Storage Image Transformations usage](/docs/guides/platform/manage-your-usage/storage-image-transformations). diff --git a/apps/docs/content/_partials/providers.mdx b/apps/docs/content/_partials/providers.mdx new file mode 100644 index 0000000000000..4f4fe7f2def20 --- /dev/null +++ b/apps/docs/content/_partials/providers.mdx @@ -0,0 +1,11 @@ +## Providers + +Supabase Auth works with many popular Auth methods, including Social and Phone Auth using third-party providers. See the following sections for a list of supported third-party providers. + +### Social Auth + + + +### Phone Auth + + diff --git a/apps/docs/content/guides/api/quickstart.mdx b/apps/docs/content/guides/api/quickstart.mdx index 7ba5b9b8809d5..432838a2aa8fc 100644 --- a/apps/docs/content/guides/api/quickstart.mdx +++ b/apps/docs/content/guides/api/quickstart.mdx @@ -156,6 +156,7 @@ const { data, error } = await supabase.from('todos').select() ``` +<$Show if="sdk:dart"> ```dart @@ -163,6 +164,8 @@ final data = await supabase.from('todos').select('*'); ``` + +<$Show if="sdk:python"> ```python @@ -170,6 +173,8 @@ response = supabase.table('todos').select("*").execute() ``` + +<$Show if="sdk:swift"> ```swift @@ -177,4 +182,5 @@ let response = try await supabase.from("todos").select() ``` + diff --git a/apps/docs/content/guides/api/using-custom-schemas.mdx b/apps/docs/content/guides/api/using-custom-schemas.mdx index 5f9fe4770d5ee..55af9da01d28f 100644 --- a/apps/docs/content/guides/api/using-custom-schemas.mdx +++ b/apps/docs/content/guides/api/using-custom-schemas.mdx @@ -57,8 +57,8 @@ const { data: todos, error } = await supabase.schema('myschema').from('todos').s ``` +<$Show if="sdk:dart"> - ```dart // Initialize the Flutter client await Supabase.initialize( @@ -73,9 +73,10 @@ final data = await supabase.from('todos').select(); // You can also change the target schema on a per-query basis final data = await supabase.schema('myschema').from('todos').select(); -``` +```` + ```bash @@ -94,7 +95,7 @@ curl -X POST '/rest/v1/todos' \ -H "Content-Type: application/json" \ -H "Content-Profile: myschema" \ -d '{"column_name": "value"}' -``` +```` diff --git a/apps/docs/content/guides/auth.mdx b/apps/docs/content/guides/auth.mdx index 213d1b91a1ce2..849e30219a0d1 100644 --- a/apps/docs/content/guides/auth.mdx +++ b/apps/docs/content/guides/auth.mdx @@ -27,17 +27,9 @@ Auth uses your project's Postgres database under the hood, storing user data and Auth also enables access control to your database's automatically generated [REST API](/docs/guides/api). When using Supabase SDKs, your data requests are automatically sent with the user's Auth Token. The Auth Token scopes database access on a row-by-row level when used along with [RLS policies](/docs/guides/database/postgres/row-level-security). -## Providers - -Supabase Auth works with many popular Auth methods, including Social and Phone Auth using third-party providers. See the following sections for a list of supported third-party providers. - -### Social Auth - - - -### Phone Auth - - +<$Show if="authentication:show_providers"> +<$Partial path="providers.mdx" /> + ## Pricing diff --git a/apps/docs/content/guides/auth/auth-anonymous.mdx b/apps/docs/content/guides/auth/auth-anonymous.mdx index 30b6af585925f..f6c16268b0f17 100644 --- a/apps/docs/content/guides/auth/auth-anonymous.mdx +++ b/apps/docs/content/guides/auth/auth-anonymous.mdx @@ -64,6 +64,7 @@ const { data, error } = await supabase.auth.signInAnonymously() ``` +<$Show if="sdk:dart"> Call the [`signInAnonymously()`](/docs/reference/dart/auth-signinanonymously) method: @@ -73,6 +74,8 @@ await supabase.auth.signInAnonymously(); ``` + +<$Show if="sdk:swift"> Call the [`signInAnonymously()`](/docs/reference/swift/auth-signinanonymously) method: @@ -82,6 +85,8 @@ let session = try await supabase.auth.signInAnonymously() ``` + +<$Show if="sdk:kotlin"> Call the [`signInAnonymously()`](/docs/reference/kotlin/auth-signinanonymously) method: @@ -91,6 +96,8 @@ supabase.auth.signInAnonymously() ``` + +<$Show if="sdk:python"> Call the [`sign_in_anonymously()`](/docs/reference/python/auth-signinanonymously) method: @@ -100,6 +107,7 @@ response = supabase.auth.sign_in_anonymously() ``` + ## Convert an anonymous user to a permanent user @@ -138,6 +146,7 @@ const { data: updatePasswordData, error: updatePasswordError } = await supabase. ``` +<$Show if="sdk:dart"> You can use the [`updateUser()`](/docs/reference/dart/auth-updateuser) method to link an email or phone identity to the anonymous user. @@ -147,6 +156,8 @@ await supabase.auth.updateUser(UserAttributes(email: 'valid.email@supabase.io')) ``` + +<$Show if="sdk:swift"> You can use the [`updateUser()`](/docs/reference/swift/auth-updateuser) method to link an email or phone identity to the anonymous user. @@ -158,6 +169,8 @@ try await supabase.auth.updateUser( ``` + +<$Show if="sdk:kotlin"> You can use the [`updateUser()`](/docs/reference/kotlin/auth-updateuser) method to link an email or phone identity to the anonymous user. @@ -169,6 +182,8 @@ supabase.auth.updateUser { ``` + +<$Show if="sdk:python"> You can use the [`update_user()`](/docs/reference/python/auth-updateuser) method to link an email or phone identity to the anonymous user. To add a password for the anonymous user, the user's email or phone number needs to be verified first. @@ -188,6 +203,7 @@ response = supabase.auth.update_user({ ``` + ### Link an OAuth identity @@ -212,6 +228,7 @@ const { data, error } = await supabase.auth.linkIdentity({ provider: 'google' }) ``` +<$Show if="sdk:dart"> You can use the [`linkIdentity()`](/docs/reference/dart/auth-linkidentity) method to link an OAuth identity to the anonymous user. @@ -221,6 +238,8 @@ await supabase.auth.linkIdentity(OAuthProvider.google); ``` + +<$Show if="sdk:swift"> You can use the [`linkIdentity()`](/docs/reference/swift/auth-linkidentity) method to link an OAuth identity to the anonymous user. @@ -230,6 +249,8 @@ try await supabase.auth.linkIdentity(provider: .google) ``` + +<$Show if="sdk:kotlin"> You can use the [`linkIdentity()`](/docs/reference/kotlin/auth-linkidentity) method to link an OAuth identity to the anonymous user. @@ -239,6 +260,8 @@ supabase.auth.linkIdentity(Google) ``` + +<$Show if="sdk:python"> You can use the [`link_identity()`](/docs/reference/python/auth-linkidentity) method to link an OAuth identity to the anonymous user. @@ -248,6 +271,7 @@ response = supabase.auth.link_identity({'provider': 'google'}) ``` + ## Access control diff --git a/apps/docs/content/guides/auth/auth-email-passwordless.mdx b/apps/docs/content/guides/auth/auth-email-passwordless.mdx index c6f2a6af289b4..ee2fc051a055a 100644 --- a/apps/docs/content/guides/auth/auth-email-passwordless.mdx +++ b/apps/docs/content/guides/auth/auth-email-passwordless.mdx @@ -81,6 +81,7 @@ const { error } = await supabase.auth.signInWithOtp({ Read the [Deep Linking Documentation](/docs/guides/auth/native-mobile-deep-linking) to learn how to handle deep linking. +<$Show if="sdk:dart"> ```dart @@ -90,6 +91,8 @@ Future signInWithEmail() async { ``` + +<$Show if="sdk:swift"> ```swift @@ -102,6 +105,8 @@ try await supabase.auth.signInWithOTP( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -113,6 +118,8 @@ suspend fun signInWithEmail() { ``` + +<$Show if="sdk:python"> ```python @@ -127,6 +134,7 @@ response = supabase.auth.sign_in_with_otp({ ``` + {/* supa-mdx-lint-disable-next-line Rule004ExcludeWords */} @@ -207,6 +215,7 @@ const { data, error } = await supabase.auth.signInWithOtp({ ``` +<$Show if="sdk:dart"> ```dart @@ -216,6 +225,8 @@ Future signInWithEmailOtp() async { ``` + +<$Show if="sdk:swift"> ```swift @@ -227,6 +238,8 @@ try await supabase.auth.signInWithOTP( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -238,6 +251,8 @@ suspend fun signInWithEmailOtp() { ``` + +<$Show if="sdk:python"> ```python @@ -251,6 +266,7 @@ response = supabase.auth.sign_in_with_otp({ ``` + If the request is successful, you receive a response with `error: null` and a `data` object where both `user` and `session` are null. Let the user know to check their email inbox. @@ -296,6 +312,7 @@ const { ``` +<$Show if="sdk:swift"> ```swift @@ -307,6 +324,8 @@ try await supabase.auth.verifyOTP( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -314,6 +333,8 @@ supabase.auth.verifyEmailOtp(type = OtpType.Email.EMAIL, email = "email", token ``` + +<$Show if="sdk:python"> ```python @@ -325,6 +346,7 @@ response = supabase.auth.verify_otp({ ``` + If successful, the user is now logged in, and you receive a valid session that looks like: diff --git a/apps/docs/content/guides/auth/auth-identity-linking.mdx b/apps/docs/content/guides/auth/auth-identity-linking.mdx index 43198263a2c6d..8ccdb45aae473 100644 --- a/apps/docs/content/guides/auth/auth-identity-linking.mdx +++ b/apps/docs/content/guides/auth/auth-identity-linking.mdx @@ -42,6 +42,7 @@ const { data, error } = await supabase.auth.linkIdentity({ provider: 'google' }) ``` +<$Show if="sdk:dart"> Supabase Auth allows a user to initiate identity linking with a different email address when they are logged in. To link an OAuth identity to the user, call [`linkIdentity()`](/docs/reference/dart/auth-linkidentity): @@ -51,6 +52,8 @@ await supabase.auth.linkIdentity(OAuthProvider.google); ``` + +<$Show if="sdk:swift"> Supabase Auth allows a user to initiate identity linking with a different email address when they are logged in. To link an OAuth identity to the user, call [`linkIdentity()`](/docs/reference/swift/auth-linkidentity): @@ -60,6 +63,8 @@ try await supabase.auth.linkIdentity(provider: .google) ``` + +<$Show if="sdk:kotlin"> Supabase Auth allows a user to initiate identity linking with a different email address when they are logged in. To link an OAuth identity to the user, call [`linkIdentity()`](/docs/reference/kotlin/auth-linkidentity): @@ -69,6 +74,8 @@ supabase.auth.linkIdentity(Google) ``` + +<$Show if="sdk:python"> Supabase Auth allows a user to initiate identity linking with a different email address when they are logged in. To link an OAuth identity to the user, call [`link_identity()`](/docs/reference/python/auth-linkidentity): @@ -78,6 +85,7 @@ response = supabase.auth.link_identity({'provider': 'google'}) ``` + In the example above, the user will be redirected to Google to complete the OAuth2.0 flow. Once the OAuth2.0 flow has completed successfully, the user will be redirected back to the application and the Google identity will be linked to the user. You can enable manual linking from your project's authentication [configuration options](/dashboard/project/_/settings/auth) or by setting the environment variable `GOTRUE_SECURITY_MANUAL_LINKING_ENABLED: true` when self-hosting. @@ -115,6 +123,7 @@ if (!identitiesError) { ``` +<$Show if="sdk:dart"> You can use [`getUserIdentities()`](/docs/reference/dart/auth-getuseridentities) to fetch all the identities linked to a user. Then, call [`unlinkIdentity()`](/docs/reference/dart/auth-unlinkidentity) to unlink the identity. The user needs to be logged in and have at least 2 linked identities in order to unlink an existing identity. @@ -132,6 +141,8 @@ await supabase.auth.unlinkIdentity(googleIdentity); ``` + +<$Show if="sdk:swift"> You can use [`getUserIdentities()`](/docs/reference/swift/auth-getuseridentities) to fetch all the identities linked to a user. Then, call [`unlinkIdentity()`](/docs/reference/swift/auth-unlinkidentity) to unlink the identity. The user needs to be logged in and have at least 2 linked identities in order to unlink an existing identity. @@ -148,6 +159,8 @@ try await supabase.auth.unlinkIdentity(googleIdentity) ``` + +<$Show if="sdk:kotlin"> You can use [`currentIdentitiesOrNull()`](/docs/reference/kotlin/auth-getuseridentities) to get all the identities linked to a user. Then, call [`unlinkIdentity()`](/docs/reference/kotlin/auth-unlinkidentity) to unlink the identity. The user needs to be logged in and have at least 2 linked identities in order to unlink an existing identity. @@ -164,6 +177,8 @@ supabase.auth.unlinkIdentity(googleIdentity.identityId!!) ``` + +<$Show if="sdk:python"> You can use [`get_user_identities()`](/docs/reference/python/auth-getuseridentities) to fetch all the identities linked to a user. Then, call [`unlink_identity()`](/docs/reference/python/auth-unlinkidentity) to unlink the identity. The user needs to be logged in and have at least 2 linked identities in order to unlink an existing identity. @@ -181,6 +196,7 @@ if google_identity: ``` + ## Frequently asked questions diff --git a/apps/docs/content/guides/auth/auth-mfa/phone.mdx b/apps/docs/content/guides/auth/auth-mfa/phone.mdx index 35292146ba8d1..f29187d7227bb 100644 --- a/apps/docs/content/guides/auth/auth-mfa/phone.mdx +++ b/apps/docs/content/guides/auth/auth-mfa/phone.mdx @@ -310,8 +310,6 @@ Each code is valid for up to 5 minutes, after which a new one can be sent. Succe Be aware that Phone MFA is vulnerable to SIM swap attacks where an attacker will call a mobile provider and ask to port the target's phone number to a new SIM card and then use the said SIM card to intercept an MFA code. Evaluate the your application's tolerance for such an attack. You can read more about SIM swapping attacks [here](https://en.wikipedia.org/wiki/SIM_swap_scam) -## Pricing - +<$Show if="billing:all"> <$Partial path="billing/pricing/pricing_mfa_phone.mdx" /> - -For a detailed breakdown of how charges are calculated, refer to [Manage Advanced MFA Phone usage](/docs/guides/platform/manage-your-usage/advanced-mfa-phone). + diff --git a/apps/docs/content/guides/auth/debugging/error-codes.mdx b/apps/docs/content/guides/auth/debugging/error-codes.mdx index 43dd3a762d2f0..9e0b429fcfca3 100644 --- a/apps/docs/content/guides/auth/debugging/error-codes.mdx +++ b/apps/docs/content/guides/auth/debugging/error-codes.mdx @@ -36,6 +36,7 @@ Error objects are split in a few classes: Errors originating from the server API classed as `AuthApiError` always have a `code` property that can be used to identify the error returned by the server. The `status` property is also present, encoding the HTTP status code received in the response. +<$Show if="sdk:dart"> All errors originating from the `supabase.auth` namespace of the client library will be wrapped by the `AuthException` class. @@ -44,12 +45,16 @@ Error objects are split in a few classes. `AuthApiException` is an exception whi Errors originating from the server API classed as `AuthApiException` always have a `code` property that can be used to identify the error returned by the server. The `statusCode` property is also present, encoding the HTTP status code received in the response. + +<$Show if="sdk:swift"> All errors originating from the `supabase.auth` namespace of the client library will be a case of the `AuthError` enum. The `api(message:errorCode:underlyingData:underlyingResponse:)` case is a special case for errors which originates from the Supabase Auth API, this error always have an `errorCode` property that can be used to identify the error returned by the server. + +<$Show if="sdk:python"> All errors originating from the `supabase.auth` namespace of the client library will be wrapped by the `AuthError` class. @@ -58,6 +63,8 @@ Error objects are split in a few classes. `AuthApiError` is an error which origi Errors originating from the server API classed as `AuthApiError` always have a `code` property that can be used to identify the error returned by the server. The `status` property is also present, encoding the HTTP status code received in the response. + +<$Show if="sdk:kotlin"> All exceptions originating from the `supabase.auth` namespace of the Kotlin client library will be a subclass of `RestException`. @@ -70,6 +77,7 @@ Rest exceptions are split into a few classes: All instances and subclasses of a `AuthRestException` have a `errorCode` property that can be used to identify the error returned by the server. + diff --git a/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx b/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx index 77144658e1237..653b7a4c6e7d9 100644 --- a/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx +++ b/apps/docs/content/guides/auth/enterprise-sso/auth-sso-saml.mdx @@ -206,6 +206,7 @@ supabase.auth.signInWithSSO({ Calling [`signInWithSSO`](/docs/reference/javascript/auth-signinwithsso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead. +<$Show if="sdk:dart"> ```dart @@ -217,6 +218,8 @@ await supabase.auth.signInWithSSO( Calling [`signInWithSSO`](/docs/reference/dart/auth-signinwithsso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead. + +<$Show if="sdk:swift"> ```swift @@ -228,6 +231,8 @@ try await supabase.auth.signInWithSSO( Calling [`signInWithSSO`](/docs/reference/swift/auth-signinwithsso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead. + +<$Show if="sdk:kotlin"> ```kotlin @@ -239,6 +244,7 @@ supabase.auth.signInWith(SSO) { Calling [`signInWith(SSO)`](/docs/reference/kotlin/auth-signinwithsso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead. + ### Understanding attribute mappings @@ -384,11 +390,9 @@ supabase sso show --project-ref You can use the `-o json` flag to output the information as JSON, should you need to. Other formats may be supported, use `--help` to see all available options. -## Pricing - +<$Show if="billing:all"> <$Partial path="billing/pricing/pricing_mau_sso.mdx" /> - -For a detailed breakdown of how charges are calculated, refer to [Manage Monthly Active SSO Users usage](/docs/guides/platform/manage-your-usage/monthly-active-users-sso). + ## Frequently asked questions diff --git a/apps/docs/content/guides/auth/jwts.mdx b/apps/docs/content/guides/auth/jwts.mdx index cc98b5825f244..bbdf43a77389b 100644 --- a/apps/docs/content/guides/auth/jwts.mdx +++ b/apps/docs/content/guides/auth/jwts.mdx @@ -106,6 +106,8 @@ const supabase = createClient( +<$Show if="sdk:dart"> + ```dart @@ -120,6 +122,9 @@ await Supabase.initialize( ``` + + +<$Show if="sdk:swift"> @@ -140,6 +145,9 @@ let supabase = SupabaseClient( ``` + + +<$Show if="sdk:kotlin"> @@ -155,6 +163,7 @@ val supabase = createSupabaseClient( ``` + diff --git a/apps/docs/content/guides/auth/managing-user-data.mdx b/apps/docs/content/guides/auth/managing-user-data.mdx index 150c6069ab209..625f6434d5d7f 100644 --- a/apps/docs/content/guides/auth/managing-user-data.mdx +++ b/apps/docs/content/guides/auth/managing-user-data.mdx @@ -85,6 +85,7 @@ const { data, error } = await supabase.auth.signUp({ ``` +<$Show if="sdk:dart"> ```dart @@ -99,6 +100,8 @@ final res = await supabase.auth.signUp( ``` + +<$Show if="sdk:swift"> ```swift @@ -113,6 +116,8 @@ try await supabase.auth.signUp( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -127,6 +132,7 @@ val data = supabase.auth.signUpWith(Email) { ``` + User metadata is stored on the `raw_user_meta_data` column of the `auth.users` table. To view the metadata: @@ -152,6 +158,7 @@ let metadata = user?.user_metadata ``` +<$Show if="sdk:dart"> ```dart @@ -160,6 +167,8 @@ final Map? metadata = user?.userMetadata; ``` + +<$Show if="sdk:swift"> ```swift @@ -168,6 +177,8 @@ let metadata = user.userMetadata ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -178,6 +189,7 @@ val metadata = user?.userMetadata ``` + ## Deleting users diff --git a/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx b/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx index 28058e5fe4077..c8473e7dc08f1 100644 --- a/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx +++ b/apps/docs/content/guides/auth/native-mobile-deep-linking.mdx @@ -304,6 +304,8 @@ With Deep Linking, you can configure this redirect to open a specific page. This +<$Show if="sdk:swift"> + ### Deep link config @@ -342,6 +344,8 @@ With Deep Linking, you can configure this redirect to open a specific page. This ``` + + <$Show if="sdk:kotlin"> ### Deep link config @@ -396,4 +400,5 @@ With Deep Linking, you can configure this redirect to open a specific page. This The user will now be authenticated when your app receives a valid deep link! + diff --git a/apps/docs/content/guides/auth/passwords.mdx b/apps/docs/content/guides/auth/passwords.mdx index 218d675e6da4b..2ff8353c07766 100644 --- a/apps/docs/content/guides/auth/passwords.mdx +++ b/apps/docs/content/guides/auth/passwords.mdx @@ -72,6 +72,7 @@ async function signUpNewUser() { ``` +<$Show if="sdk:dart"> To sign up the user, call [signUp()](/docs/reference/dart/auth-signup) with their email address and password: @@ -86,6 +87,8 @@ Future signUpNewUser() async { ``` + +<$Show if="sdk:swift"> To sign up the user, call [signUp()](/docs/reference/swift/auth-signup) with their email address and password. @@ -103,6 +106,8 @@ let response = try await supabase.auth.signUp( ``` + +<$Show if="sdk:kotlin"> To sign up the user, call [signUpWith(Email)](/docs/reference/kotlin/auth-signup) with their email address and password: @@ -117,6 +122,8 @@ suspend fun signUpNewUser() { ``` + +<$Show if="sdk:python"> To sign up the user, call [signUp()](/docs/reference/python/auth-signup) with their email address and password. @@ -136,6 +143,7 @@ data = await supabase.auth.sign_up({ ``` + @@ -409,6 +417,7 @@ async function signUpNewUser() { ``` +<$Show if="sdk:dart"> To sign up the user, call [signUp()](/docs/reference/dart/auth-signup) with their email address and password: @@ -423,6 +432,8 @@ Future signUpNewUser() async { ``` + +<$Show if="sdk:swift"> To sign up the user, call [signUp()](/docs/reference/swift/auth-signup) with their email address and password: @@ -435,6 +446,8 @@ let response = try await supabase.auth.signUp( ``` + +<$Show if="sdk:kotlin"> To sign up the user, call [signUpWith(Email)](/docs/reference/kotlin/auth-signup) with their email address and password: @@ -449,6 +462,8 @@ suspend fun signUpNewUser() { ``` + +<$Show if="sdk:python"> To sign up the user, call [signUp()](/docs/reference/python/auth-signup) with their email address and password: @@ -461,6 +476,7 @@ data = supabase.auth.sign_up({ ``` + @@ -493,6 +509,7 @@ async function signInWithEmail() { ``` +<$Show if="sdk:dart"> When your user signs in, call [`signInWithPassword()`](/docs/reference/dart/auth-signinwithpassword) with their email address and password: @@ -507,6 +524,8 @@ Future signInWithEmail() async { ``` + +<$Show if="sdk:swift"> When your user signs in, call [signIn(email:password:)](/docs/reference/swift/auth-signinwithpassword) with their email address and password: @@ -519,6 +538,8 @@ try await supabase.auth.signIn( ``` + +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Email)](/docs/reference/kotlin/auth-signinwithpassword) with their email address and password: @@ -533,6 +554,8 @@ suspend fun signInWithEmail() { ``` + +<$Show if="sdk:python"> When your user signs in, call [sign_in_with_password()](/docs/reference/python/auth-signinwithpassword) with their email address and password: @@ -545,6 +568,7 @@ data = client.auth.sign_in_with_password({ ``` + ### Resetting a password @@ -583,6 +607,7 @@ await supabase.auth.resetPasswordForEmail('valid.email@supabase.io', { ``` +<$Show if="sdk:swift"> ```swift @@ -593,6 +618,8 @@ try await supabase.auth.resetPasswordForEmail( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -605,6 +632,8 @@ supabase.auth.resetPasswordForEmail( If you are on one of the Kotlin targets that have built-in support for redirect URL handling, such as Android, see [OAuth and OTP link verification](https://supabase.com/docs/reference/kotlin/initializing). + +<$Show if="sdk:python"> ```python @@ -615,6 +644,7 @@ client.auth.reset_password_email( ``` + #### Step 2: Create a change password page @@ -880,6 +910,7 @@ async function resetPassword() { ``` +<$Show if="sdk:swift"> ```swift @@ -887,6 +918,8 @@ try await supabase.auth.resetPasswordForEmail("valid.email@supabase.io") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -896,6 +929,8 @@ supabase.gotrue.sendRecoveryEmail( ``` + +<$Show if="sdk:python"> ```python @@ -903,6 +938,7 @@ supabase.auth.reset_password_email('valid.email@supabase.io') ``` + Once you have a session, collect the user's new password and call `updateUser` to update their password. @@ -929,6 +965,7 @@ await supabase.auth.updateUser({ password: 'new_password' }) ``` +<$Show if="sdk:swift"> ```swift @@ -936,6 +973,8 @@ try await supabase.auth.updateUser(user: UserAttributes(password: newPassword)) ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -945,6 +984,8 @@ supabase.auth.updateUser { ``` + +<$Show if="sdk:python"> ```python @@ -952,6 +993,7 @@ supabase.auth.update_user({'password': 'new_password'}) ``` + ### Email sending @@ -1022,6 +1064,7 @@ const { data, error } = await supabase.auth.signUp({ ``` +<$Show if="sdk:swift"> ```swift @@ -1032,6 +1075,8 @@ try await supabase.auth.signUp( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1042,6 +1087,8 @@ supabase.auth.signUpWith(Phone) { ``` + +<$Show if="sdk:python"> ```python @@ -1052,6 +1099,7 @@ supabase.auth.sign_up({ ``` + ```bash @@ -1096,6 +1144,7 @@ const { ``` +<$Show if="sdk:swift"> You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOTP`: @@ -1109,6 +1158,8 @@ try await supabase.auth.verifyOTP( ``` + +<$Show if="sdk:kotlin"> You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyPhoneOtp`: @@ -1122,6 +1173,8 @@ supabase.auth.verifyPhoneOtp( ``` + +<$Show if="sdk:python"> You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verify_otp`: @@ -1135,6 +1188,7 @@ supabase.auth.verify_otp({ ``` + ```bash @@ -1176,6 +1230,7 @@ const { data, error } = await supabase.auth.signInWithPassword({ ``` +<$Show if="sdk:swift"> ```swift @@ -1186,6 +1241,8 @@ try await supabase.auth.signIn( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1196,6 +1253,8 @@ supabase.auth.signInWith(Phone) { ``` + +<$Show if="sdk:python"> ```python @@ -1206,6 +1265,7 @@ supabase.auth.sign_in_with_password({ ``` + ```bash diff --git a/apps/docs/content/guides/auth/phone-login.mdx b/apps/docs/content/guides/auth/phone-login.mdx index 5f6279b5a132d..fab354ef5a29c 100644 --- a/apps/docs/content/guides/auth/phone-login.mdx +++ b/apps/docs/content/guides/auth/phone-login.mdx @@ -57,6 +57,7 @@ const { data, error } = await supabase.auth.signInWithOtp({ ``` +<$Show if="sdk:swift"> ```swift @@ -66,6 +67,8 @@ try await supabase.auth.signInWithOTP( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -75,6 +78,8 @@ supabase.auth.signInWith(OTP) { ``` + +<$Show if="sdk:python"> ```python @@ -84,6 +89,7 @@ response = supabase.auth.sign_in_with_otp({ ``` + ```bash @@ -131,6 +137,7 @@ const { ``` +<$Show if="sdk:swift"> You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyOTP`: @@ -144,6 +151,8 @@ try await supabase.auth.verifyOTP( ``` + +<$Show if="sdk:kotlin"> You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyPhoneOtp`: @@ -157,6 +166,8 @@ supabase.auth.verifyPhoneOtp( ``` + +<$Show if="sdk:python"> You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verify_otp`: @@ -170,6 +181,7 @@ response = supabase.auth.verify_otp({ ``` + ```bash @@ -223,6 +235,7 @@ const { data, error } = await supabase.auth.updateUser({ ``` +<$Show if="sdk:swift"> ```swift @@ -234,6 +247,8 @@ try await supabase.auth.updateUser( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -243,6 +258,8 @@ supabase.auth.updateUser { ``` + +<$Show if="sdk:python"> ```python @@ -252,6 +269,7 @@ response = supabase.auth.update_user({ ``` + The user receives an SMS with a 6-digit pin that you must [verify](#verifying-a-phone-otp) within 60 seconds. diff --git a/apps/docs/content/guides/auth/signout.mdx b/apps/docs/content/guides/auth/signout.mdx index 66cbd826f1148..932c0bb9b71b6 100644 --- a/apps/docs/content/guides/auth/signout.mdx +++ b/apps/docs/content/guides/auth/signout.mdx @@ -30,6 +30,7 @@ async function signOut() { ``` +<$Show if="sdk:dart"> ```dart @@ -39,6 +40,8 @@ Future signOut() async { ``` + +<$Show if="sdk:swift"> ```swift @@ -46,6 +49,8 @@ try await supabase.auth.signOut() ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -55,6 +60,8 @@ suspend fun logout() { ``` + +<$Show if="sdk:python"> ```python @@ -62,6 +69,7 @@ supabase.auth.sign_out() ``` + ## Sign out and scopes @@ -99,6 +107,7 @@ await supabase.auth.signOut({ scope: 'local' }) ``` +<$Show if="sdk:dart"> ```dart @@ -110,6 +119,8 @@ await supabase.auth.signOut(scope: SignOutScope.global); ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -121,6 +132,7 @@ supabase.auth.signOut(SignOutScope.GLOBAL) ``` + Upon sign out, all refresh tokens and potentially other database objects related to the affected sessions are destroyed and the client library removes the session stored in the local storage medium. diff --git a/apps/docs/content/guides/auth/social-login/auth-apple.mdx b/apps/docs/content/guides/auth/social-login/auth-apple.mdx index 98d33c6856c75..53c5927a33361 100644 --- a/apps/docs/content/guides/auth/social-login/auth-apple.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-apple.mdx @@ -320,6 +320,8 @@ curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \ +<$Show if="sdk:swift"> + ## Using native sign in with Apple in Swift @@ -378,6 +380,9 @@ curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \ + + +<$Show if="sdk:kotlin"> @@ -419,4 +424,5 @@ curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \ ``` + diff --git a/apps/docs/content/guides/auth/social-login/auth-azure.mdx b/apps/docs/content/guides/auth/social-login/auth-azure.mdx index c9176e917ee4a..9967206d72d88 100644 --- a/apps/docs/content/guides/auth/social-login/auth-azure.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-azure.mdx @@ -180,6 +180,7 @@ Future signInWithAzure() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Azure` as the `Provider`: @@ -193,6 +194,7 @@ suspend fun signInWithAzure() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -230,6 +232,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -241,6 +244,7 @@ suspend fun signOut() { ``` + ## Obtain the provider refresh token @@ -284,6 +288,7 @@ Future signInWithAzure() async { ``` +<$Show if="sdk:kotlin"> ```kotlin @@ -295,6 +300,7 @@ suspend fun signInWithAzure() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx b/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx index 24eb3432ac0d9..d41f167382562 100644 --- a/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-bitbucket.mdx @@ -91,6 +91,7 @@ Future signInWithBitbucket() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Bitbucket` as the `Provider`: @@ -102,6 +103,7 @@ suspend fun signInWithBitbucket() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -142,6 +144,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -153,6 +156,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-discord.mdx b/apps/docs/content/guides/auth/social-login/auth-discord.mdx index 927854fdef292..6e6fdc7a7028c 100644 --- a/apps/docs/content/guides/auth/social-login/auth-discord.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-discord.mdx @@ -108,6 +108,7 @@ Future signInWithDiscord() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Discord` as the `Provider`: @@ -119,6 +120,7 @@ suspend fun signInWithDiscord() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -161,6 +163,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -172,6 +175,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-facebook.mdx b/apps/docs/content/guides/auth/social-login/auth-facebook.mdx index 5412fefbec71b..5ae0b7271b1a2 100644 --- a/apps/docs/content/guides/auth/social-login/auth-facebook.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-facebook.mdx @@ -171,6 +171,7 @@ Make sure to configure your Facebook app properly and add the required permissio +<$Show if="sdk:swift"> When your user signs in, call [`signInWithOAuth()`](/docs/reference/swift/auth-signinwithoauth) with `facebook` as the `provider`: @@ -191,6 +192,8 @@ func signInWithFacebook() async throws { ``` + +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Facebook` as the `Provider`: @@ -202,6 +205,7 @@ suspend fun signInWithFacebook() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -239,6 +243,7 @@ Future signOut() async { ``` +<$Show if="sdk:swift"> When your user signs out, call [signOut()](/docs/reference/swift/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -250,6 +255,8 @@ func signOut() async throws { ``` + +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -261,6 +268,7 @@ suspend fun signOut() { ``` + Now, you should be able to login with Facebook and alert you to `Submit for Login Review` when users try to sign into your app. Follow the instructions there to make your app go live for full features and products. diff --git a/apps/docs/content/guides/auth/social-login/auth-figma.mdx b/apps/docs/content/guides/auth/social-login/auth-figma.mdx index 6fd11bdc0c9d2..f934234e9c814 100644 --- a/apps/docs/content/guides/auth/social-login/auth-figma.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-figma.mdx @@ -88,6 +88,7 @@ Future signInWithFigma() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Figma` as the `Provider`: @@ -99,6 +100,7 @@ suspend fun signInWithFigma() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -136,6 +138,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -147,6 +150,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-github.mdx b/apps/docs/content/guides/auth/social-login/auth-github.mdx index a23a426d2f0a4..2fdb18965059a 100644 --- a/apps/docs/content/guides/auth/social-login/auth-github.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-github.mdx @@ -103,6 +103,7 @@ Future signInWithGithub() async { ``` +<$Show if="sdk:swift"> When your user signs in, call [`signInWithOAuth`](/docs/reference/swift/auth-signinwithoauth) with `.github` as the `Provider`: @@ -117,6 +118,8 @@ func signInWithGithub() async throws { ``` + +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with [`Github`](https://github.com/supabase-community/supabase-kt/blob/master/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/providers/Providers.kt#L16-L20) as the `Provider`: @@ -128,6 +131,7 @@ suspend fun signInWithGithub() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -168,6 +172,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -179,6 +184,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx b/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx index 56df4e93f7278..91c74107c493f 100644 --- a/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-gitlab.mdx @@ -88,6 +88,7 @@ Future signInWithGitLab() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Gitlab` as the `Provider`: @@ -99,6 +100,7 @@ suspend fun signInWithGitLab() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -139,6 +141,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -150,6 +153,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-google.mdx b/apps/docs/content/guides/auth/social-login/auth-google.mdx index be691bad7f72c..42be9b5d23cb2 100644 --- a/apps/docs/content/guides/auth/social-login/auth-google.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-google.mdx @@ -145,6 +145,8 @@ To use Google's pre-built signin buttons: +<$Show if="sdk:swift"> + Google sign-in with Supabase is done through the [`GoogleSignIn-iOS`](https://github.com/google/GoogleSignIn-iOS) package. @@ -189,6 +191,7 @@ To use Google's pre-built signin buttons: 3. Add web client ID and iOS client ID from step 1 in the [Google provider on the Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers), under _Client IDs_, separated by a comma. Enable the `Skip nonce check` option. + diff --git a/apps/docs/content/guides/auth/social-login/auth-kakao.mdx b/apps/docs/content/guides/auth/social-login/auth-kakao.mdx index 950c50f67bb67..97da18051a799 100644 --- a/apps/docs/content/guides/auth/social-login/auth-kakao.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-kakao.mdx @@ -112,6 +112,7 @@ Future signInWithKakao() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Kakao` as the `Provider`: @@ -123,6 +124,7 @@ suspend fun signInWithKakao() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -156,6 +158,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -167,6 +170,7 @@ suspend fun signOut() { ``` + ## Using Kakao Login JS SDK diff --git a/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx b/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx index 73ec7c240af2d..df911c2794f61 100644 --- a/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-keycloak.mdx @@ -106,6 +106,7 @@ Future signInWithKeycloak() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Keycloak` as the `Provider`: @@ -119,6 +120,7 @@ suspend fun signInWithKeycloak() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -152,6 +154,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -167,6 +170,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx index 8a04410f56376..beee52a0ba9f0 100644 --- a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx @@ -108,6 +108,7 @@ Future signInWithLinkedIn() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `LinkedIn` as the `Provider`: @@ -119,6 +120,7 @@ suspend fun signInWithKaLinkedIn() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -156,6 +158,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -167,6 +170,7 @@ suspend fun signOut() { ``` + ## LinkedIn Open ID Connect (OIDC) diff --git a/apps/docs/content/guides/auth/social-login/auth-notion.mdx b/apps/docs/content/guides/auth/social-login/auth-notion.mdx index 4b3b115b9dca2..6a4db97cbe7c6 100644 --- a/apps/docs/content/guides/auth/social-login/auth-notion.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-notion.mdx @@ -88,6 +88,7 @@ Future signInWithNotion() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Notion` as the `Provider`: @@ -99,6 +100,7 @@ suspend fun signInWithNotion() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -136,6 +138,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -147,6 +150,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-slack.mdx b/apps/docs/content/guides/auth/social-login/auth-slack.mdx index e1715289ba5cd..172eed7207d39 100644 --- a/apps/docs/content/guides/auth/social-login/auth-slack.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-slack.mdx @@ -124,6 +124,7 @@ Future signInWithSlack() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `SlackOIDC` as the `Provider`: @@ -135,6 +136,7 @@ suspend fun signInWithSlack() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -172,6 +174,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -183,6 +186,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-spotify.mdx b/apps/docs/content/guides/auth/social-login/auth-spotify.mdx index 6c74d83480c0d..57cd5f5f6a792 100644 --- a/apps/docs/content/guides/auth/social-login/auth-spotify.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-spotify.mdx @@ -118,6 +118,7 @@ Future signInWithSpotify() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Spotify` as the `Provider`: @@ -129,6 +130,7 @@ suspend fun signInWithSpotify() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -166,6 +168,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -177,6 +180,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-twitch.mdx b/apps/docs/content/guides/auth/social-login/auth-twitch.mdx index ad356b654aff6..3a4a2b8e978bd 100644 --- a/apps/docs/content/guides/auth/social-login/auth-twitch.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-twitch.mdx @@ -103,6 +103,7 @@ Future signInWithTwitch() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Twitch` as the `Provider`: @@ -114,6 +115,7 @@ suspend fun signInWithTwitch() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -151,6 +153,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -162,6 +165,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-twitter.mdx b/apps/docs/content/guides/auth/social-login/auth-twitter.mdx index b78996ceba325..6308bcc40d5dc 100644 --- a/apps/docs/content/guides/auth/social-login/auth-twitter.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-twitter.mdx @@ -115,6 +115,7 @@ Future signInWithTwitter() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Twitter` as the `Provider`: @@ -126,6 +127,7 @@ suspend fun signInWithTwitter() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -166,6 +168,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -177,6 +180,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/social-login/auth-zoom.mdx b/apps/docs/content/guides/auth/social-login/auth-zoom.mdx index 71bbe5d6f96f5..b1714a6f58dd5 100644 --- a/apps/docs/content/guides/auth/social-login/auth-zoom.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-zoom.mdx @@ -119,6 +119,7 @@ Future signInWithZoom() async { ``` +<$Show if="sdk:kotlin"> When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Zoom` as the `Provider`: @@ -130,6 +131,7 @@ suspend fun signInWithZoom() { ``` + <$Partial path="oauth_pkce_flow.mdx" /> @@ -167,6 +169,7 @@ Future signOut() async { ``` +<$Show if="sdk:kotlin"> When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: @@ -178,6 +181,7 @@ suspend fun signOut() { ``` + ## Resources diff --git a/apps/docs/content/guides/auth/third-party/auth0.mdx b/apps/docs/content/guides/auth/third-party/auth0.mdx index 76cf04be6437f..634378b437ab3 100644 --- a/apps/docs/content/guides/auth/third-party/auth0.mdx +++ b/apps/docs/content/guides/auth/third-party/auth0.mdx @@ -49,6 +49,8 @@ const supabase = createClient( +<$Show if="sdk:swift"> + ```swift @@ -73,6 +75,9 @@ let supabase = SupabaseClient( ``` + + +<$Show if="sdk:dart"> @@ -96,6 +101,9 @@ Future main() async { ``` + + +<$Show if="sdk:kotlin"> @@ -114,6 +122,7 @@ val supabase = createSupabaseClient( ``` + diff --git a/apps/docs/content/guides/auth/third-party/aws-cognito.mdx b/apps/docs/content/guides/auth/third-party/aws-cognito.mdx index d2ced1133a804..72e99ecf8a860 100644 --- a/apps/docs/content/guides/auth/third-party/aws-cognito.mdx +++ b/apps/docs/content/guides/auth/third-party/aws-cognito.mdx @@ -43,6 +43,8 @@ Hub.listen('auth', () => { +<$Show if="sdk:swift"> + ```swift @@ -72,6 +74,9 @@ let supabase = SupabaseClient( ``` + + +<$Show if="sdk:dart"> @@ -96,6 +101,9 @@ Future main() async { ``` + + +<$Show if="sdk:kotlin"> @@ -128,6 +136,7 @@ suspend fun getAccessToken(): String? { ``` + diff --git a/apps/docs/content/guides/auth/third-party/clerk.mdx b/apps/docs/content/guides/auth/third-party/clerk.mdx index f860db26d15cd..f7a809a7765e4 100644 --- a/apps/docs/content/guides/auth/third-party/clerk.mdx +++ b/apps/docs/content/guides/auth/third-party/clerk.mdx @@ -45,6 +45,8 @@ hideElidedLines={true} +<$Show if="sdk:dart"> + ```dart @@ -63,6 +65,9 @@ await Supabase.initialize( ``` + + +<$Show if="sdk:swift"> @@ -84,6 +89,7 @@ let supabase = SupabaseClient( ``` + diff --git a/apps/docs/content/guides/auth/third-party/firebase-auth.mdx b/apps/docs/content/guides/auth/third-party/firebase-auth.mdx index 438141cd5a0ef..a9a2b9246c00e 100644 --- a/apps/docs/content/guides/auth/third-party/firebase-auth.mdx +++ b/apps/docs/content/guides/auth/third-party/firebase-auth.mdx @@ -40,6 +40,8 @@ Make sure the all users in your application have the `role: 'authenticated'` [cu +<$Show if="sdk:dart"> + Creating a client for the Web is as easy as passing the `accessToken` async function. This function should [return the Firebase Auth JWT of the current user](https://firebase.google.com/docs/auth/admin/verify-id-tokens) (or null if no such user) is found. @@ -59,6 +61,9 @@ await Supabase.initialize( Make sure the all users in your application have the `role: 'authenticated'` [custom claim](https://firebase.google.com/docs/auth/admin/custom-claims) set. If you're using the `onCreate` Cloud Function to add this custom claim to newly signed up users, you will need to call `getIdToken(/* forceRefresh */ true)` immediately after sign up as the `onCreate` function does not run synchronously. + + +<$Show if="sdk:swift"> @@ -86,6 +91,9 @@ let supabase = SupabaseClient( ``` + + +<$Show if="sdk:kotlin"> @@ -106,6 +114,9 @@ val supabase = createSupabaseClient( ``` + + +<$Show if="sdk:kotlin"> @@ -126,6 +137,7 @@ val supabase = createSupabaseClient( ``` + diff --git a/apps/docs/content/guides/auth/third-party/overview.mdx b/apps/docs/content/guides/auth/third-party/overview.mdx index 77d950275da97..e110ec0e003c9 100644 --- a/apps/docs/content/guides/auth/third-party/overview.mdx +++ b/apps/docs/content/guides/auth/third-party/overview.mdx @@ -30,8 +30,6 @@ There are some limitations you should be aware of when using third-party authent 2. The JWT signing keys from the third-party provider are stored in the configuration of your project, and are checked for changes periodically. If you are rotating your keys (when supported) allow up to 30 minutes for the change to be picked up. 3. It is not possible to disable Supabase Auth at this time. -## Pricing - +<$Show if="billing:all"> <$Partial path="billing/pricing/pricing_mau_third_party.mdx" /> - -For a detailed breakdown of how charges are calculated, refer to [Manage Monthly Active Third-Party Users usage](/docs/guides/platform/manage-your-usage/monthly-active-users-third-party). + diff --git a/apps/docs/content/guides/database/arrays.mdx b/apps/docs/content/guides/database/arrays.mdx index 0faad66a9dd64..4d4aa9dfbec8c 100644 --- a/apps/docs/content/guides/database/arrays.mdx +++ b/apps/docs/content/guides/database/arrays.mdx @@ -73,6 +73,7 @@ const { data, error } = await supabase ``` +<$Show if="sdk:swift"> Insert a record from the Swift client: @@ -97,6 +98,8 @@ try await supabase ``` + +<$Show if="sdk:python"> Insert a record from the Python client: @@ -114,6 +117,7 @@ supabase.from_('arraytest').insert( ``` + ## View the results @@ -204,6 +208,7 @@ returns: ``` +<$Show if="sdk:swift"> This returns the entire array field: @@ -228,6 +233,7 @@ returns: ``` + ## Resources diff --git a/apps/docs/content/guides/database/extensions/plv8.mdx b/apps/docs/content/guides/database/extensions/plv8.mdx index eb49765550794..89e1a8a51b923 100644 --- a/apps/docs/content/guides/database/extensions/plv8.mdx +++ b/apps/docs/content/guides/database/extensions/plv8.mdx @@ -90,6 +90,7 @@ const { data, error } = supabase.rpc('function_name') ``` +<$Show if="sdk:kotlin"> ```kotlin @@ -97,6 +98,7 @@ val data = supabase.postgrest.rpc("function_name") ``` + ## Examples diff --git a/apps/docs/content/guides/database/extensions/postgis.mdx b/apps/docs/content/guides/database/extensions/postgis.mdx index c338c1ac912b4..fbdde09bdb56e 100644 --- a/apps/docs/content/guides/database/extensions/postgis.mdx +++ b/apps/docs/content/guides/database/extensions/postgis.mdx @@ -128,6 +128,7 @@ const { error } = await supabase.from('restaurants').insert([ ``` +<$Show if="sdk:dart"> ```dart @@ -148,6 +149,8 @@ await supabase.from('restaurants').insert([ ``` + +<$Show if="sdk:swift"> ```swift @@ -168,6 +171,8 @@ try await supabase.from("restaurants") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -187,6 +192,7 @@ val data = supabase.from("restaurants").insert(listOf( ``` + Notice the order in which you pass the latitude and longitude. Longitude comes first, and is because longitude represents the x-axis of the location. Another thing to watch for is when inserting data from the client library, there is no comma between the two values, just a single space. @@ -241,6 +247,7 @@ const { data, error } = await supabase.rpc('nearby_restaurants', { ``` +<$Show if="sdk:dart"> ```dart @@ -251,6 +258,8 @@ final data = await supabase.rpc('nearby_restaurants',params: { ``` + +<$Show if="sdk:swift"> ```swift @@ -279,6 +288,8 @@ let response: Response = try await supabase.rpc( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -292,6 +303,7 @@ val data = supabase.postgrest.rpc( ``` + ```json @@ -367,6 +379,7 @@ const { data, error } = await supabase.rpc('restaurants_in_view', { ``` +<$Show if="sdk:dart"> ```dart @@ -379,6 +392,8 @@ final data = await supabase.rpc('restaurants_in_view', params: { ``` + +<$Show if="sdk:swift"> ```swift @@ -403,6 +418,8 @@ let response: Response = try await supabase.rpc( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -418,6 +435,7 @@ val data = supabase.postgrest.rpc( ``` + ```json diff --git a/apps/docs/content/guides/database/full-text-search.mdx b/apps/docs/content/guides/database/full-text-search.mdx index e2f21d5a484ff..27a08579f17d4 100644 --- a/apps/docs/content/guides/database/full-text-search.mdx +++ b/apps/docs/content/guides/database/full-text-search.mdx @@ -129,6 +129,7 @@ const { data, error } = await supabase.from('books').select().eq('title', 'Harry ``` +<$Show if="sdk:dart"> ```dart @@ -139,6 +140,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -149,6 +152,8 @@ let response = try await supabase.from("books") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -160,6 +165,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -167,6 +174,7 @@ data = supabase.from_('books').select().eq('title', 'Harry').execute() ``` + The equality symbol above (`=`) is very "strict" on what it matches. In a full text search context, we might want to find all "Harry Potter" books and so we can rewrite the @@ -195,6 +203,7 @@ const { data, error } = await supabase.from('books').select().textSearch('title' ``` +<$Show if="sdk:dart"> ```dart @@ -205,6 +214,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -214,6 +225,8 @@ let response = try await supabase.from("books") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -225,6 +238,7 @@ val data = supabase.from("books").select { ``` + ## Basic full text queries @@ -260,6 +274,7 @@ const { data, error } = await supabase.from('books').select().textSearch('descri ``` +<$Show if="sdk:dart"> ```dart @@ -270,6 +285,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -280,6 +297,8 @@ let response = await client.from("books") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -291,6 +310,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -298,6 +319,7 @@ data = supabase.from_('books').select().text_search('description', "'big'").exec ``` + @@ -355,6 +377,7 @@ const { data, error } = await supabase ``` +<$Show if="sdk:dart"> ```sql @@ -371,6 +394,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```sql @@ -388,6 +413,8 @@ let response = try await client ``` + +<$Show if="sdk:kotlin"> ```sql @@ -405,6 +432,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```sql @@ -418,6 +447,7 @@ data = supabase.from_('books').select().text_search('title_description', "little ``` + {/* supa-mdx-lint-disable Rule003Spelling */} @@ -466,6 +496,7 @@ const { data, error } = await supabase ``` +<$Show if="sdk:dart"> ```dart @@ -476,6 +507,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -487,6 +520,8 @@ let response = try await client ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -498,6 +533,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -505,6 +542,7 @@ data = supabase.from_('books').select().text_search('description', "'little' & ' ``` + {/* supa-mdx-lint-disable Rule003Spelling */} @@ -552,6 +590,7 @@ const { data, error } = await supabase ``` +<$Show if="sdk:dart"> ```dart @@ -562,6 +601,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -573,6 +614,8 @@ let response = try await client ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -584,6 +627,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -591,6 +636,7 @@ response = client.from_('books').select().text_search('description', "'little' | ``` + {/* supa-mdx-lint-disable Rule003Spelling */} @@ -652,6 +698,7 @@ const { data, error } = await supabase.rpc('search_books_by_title_prefix', { pre ``` +<$Show if="sdk:dart"> ```dart @@ -659,6 +706,8 @@ final data = await supabase.rpc('search_books_by_title_prefix', params: { 'prefi ``` + +<$Show if="sdk:swift"> ```swift @@ -670,6 +719,8 @@ let response = try await supabase.rpc( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -678,6 +729,8 @@ val result = supabase.postgrest.rpc("search_books_by_title_prefix", rpcParams) ``` + +<$Show if="sdk:python"> ```python @@ -685,6 +738,7 @@ data = client.rpc('search_books_by_title_prefix', { 'prefix': 'Lit' }).execute() ``` + This function takes a prefix parameter and returns all books where the title contains a word starting with that prefix. The `:*` operator is used to denote a prefix match in the `to_tsquery()` function. @@ -777,6 +831,7 @@ const { data, error } = await supabase.from('books').select().textSearch('fts', ``` +<$Show if="sdk:dart"> ```dart @@ -787,6 +842,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -798,6 +855,8 @@ let response = try await client ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -809,6 +868,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -816,6 +877,7 @@ data = client.from_('books').select().text_search('fts', "'little' & 'big'").exe ``` + {/* supa-mdx-lint-disable Rule003Spelling */} @@ -868,6 +930,7 @@ const { data, error } = await supabase ``` +<$Show if="sdk:dart"> ```dart @@ -878,6 +941,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -889,6 +954,8 @@ let response = try await client ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -900,6 +967,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -907,6 +976,7 @@ data = client.from_('books').select().text_search('description', "'big' <-> 'dre ``` + We can also use the `<->` to find words within a certain distance of each other. For example to find `year` and `school` within 2 words of each other: @@ -940,6 +1010,7 @@ const { data, error } = await supabase ``` +<$Show if="sdk:dart"> ```dart @@ -950,6 +1021,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -961,6 +1034,8 @@ let response = try await supabase ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -972,6 +1047,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -979,6 +1056,7 @@ data = client.from_('books').select().text_search('description', "'year' <2> 'sc ``` + ### Negation: `!` [#negation] @@ -1015,6 +1093,7 @@ const { data, error } = await supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1025,6 +1104,8 @@ final result = await client ``` + +<$Show if="sdk:swift"> ```swift @@ -1036,6 +1117,8 @@ let response = try await client ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1047,6 +1130,8 @@ val data = supabase.from("books").select { ``` + +<$Show if="sdk:python"> ```python @@ -1054,6 +1139,7 @@ data = client.from_('books').select().text_search('description', "'big' & !'litt ``` + ## Resources diff --git a/apps/docs/content/guides/database/functions.mdx b/apps/docs/content/guides/database/functions.mdx index 8186c100eb4a1..3a6cac523b4eb 100644 --- a/apps/docs/content/guides/database/functions.mdx +++ b/apps/docs/content/guides/database/functions.mdx @@ -91,6 +91,7 @@ const { data, error } = await supabase.rpc('hello_world') Reference: [`rpc()`](../../reference/javascript/rpc) +<$Show if="sdk:dart"> ```dart @@ -101,6 +102,8 @@ final data = await supabase Reference: [`rpc()`](../../reference/dart/rpc) + +<$Show if="sdk:swift"> ```swift @@ -110,6 +113,8 @@ try await supabase.rpc("hello_world").execute() Reference: [`rpc()`](../../reference/swift/rpc) + +<$Show if="sdk:kotlin"> ```kotlin @@ -119,6 +124,8 @@ val data = supabase.postgrest.rpc("hello_world") Reference: [`rpc()`](../../reference/kotlin/rpc) + +<$Show if="sdk:python"> ```python @@ -128,6 +135,7 @@ data = supabase.rpc('hello_world').execute() Reference: [`rpc()`](../../reference/python/rpc) + ## Returning data sets @@ -236,6 +244,7 @@ const { data, error } = supabase.rpc('get_planets').eq('id', 1) ``` +<$Show if="sdk:dart"> ```dart @@ -245,6 +254,8 @@ final data = await supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -252,6 +263,8 @@ let response = try await supabase.rpc("get_planets").eq("id", value: 1).execute( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -263,6 +276,8 @@ val data = supabase.postgrest.rpc("get_planets") { ``` + +<$Show if="sdk:python"> ```python @@ -270,6 +285,7 @@ data = supabase.rpc('get_planets').eq('id', 1).execute() ``` + ## Passing parameters @@ -316,6 +332,7 @@ const { data, error } = await supabase.rpc('add_planet', { name: 'Jakku' }) ``` +<$Show if="sdk:dart"> ```dart @@ -324,6 +341,8 @@ final data = await supabase ``` + +<$Show if="sdk:swift"> Using `Encodable` type: @@ -351,6 +370,8 @@ try await supabase.rpc( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -363,6 +384,8 @@ val data = supabase.postgrest.rpc( ``` + +<$Show if="sdk:python"> ```python @@ -370,6 +393,7 @@ data = supabase.rpc('add_planet', params={'name': 'Jakku'}).execute() ``` + ## Suggestions diff --git a/apps/docs/content/guides/database/joins-and-nesting.mdx b/apps/docs/content/guides/database/joins-and-nesting.mdx index 8921f0e568de4..92f2a4a960f13 100644 --- a/apps/docs/content/guides/database/joins-and-nesting.mdx +++ b/apps/docs/content/guides/database/joins-and-nesting.mdx @@ -129,6 +129,7 @@ const sectionsWithInstruments: SectionsWithInstruments = data ``` +<$Show if="sdk:dart"> ```dart @@ -136,6 +137,8 @@ final data = await supabase.from('orchestral_sections').select('id, name, instru ``` + +<$Show if="sdk:swift"> ```swift @@ -158,6 +161,8 @@ let orchestralSections: [OrchestralSection] = try await supabase ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -165,6 +170,8 @@ val data = supabase.from("orchestral_sections").select(Columns.raw("id, name, in ``` + +<$Show if="sdk:python"> ```python @@ -172,6 +179,7 @@ data = supabase.from_('orchestral_sections').select('id, name, instruments(id, n ``` + ```javascript @@ -245,6 +253,7 @@ const { data, error } = await supabase.from('teams').select(` ``` +<$Show if="sdk:dart"> ```dart @@ -252,6 +261,8 @@ final data = await supabase.from('teams').select('id, team_name, users(id, name) ``` + +<$Show if="sdk:swift"> ```swift @@ -284,6 +295,8 @@ let teams [Team] = try await supabase ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -291,6 +304,8 @@ val data = supabase.from("teams").select(Columns.raw("id, team_name, users(id, n ``` + +<$Show if="sdk:python"> ```python @@ -298,6 +313,7 @@ data = supabase.from_('teams').select('id, team_name, users(id, name)').execute( ``` + ````javascript @@ -396,6 +412,7 @@ const { data, error } = await supabase.from('shifts').select( ``` +<$Show if="sdk:dart"> ```dart @@ -415,6 +432,8 @@ end_scan:scans!scan_id_end ( ``` + +<$Show if="sdk:swift"> ```swift @@ -466,6 +485,9 @@ let shifts: [Shift] = try await supabase ``` + + +<$Show if="sdk:kotlin"> @@ -486,6 +508,8 @@ end_scan:scans!scan_id_end ( ``` + +<$Show if="sdk:python"> ```python @@ -505,6 +529,7 @@ data = supabase.from_('shifts').select(""" ``` + ```javascript diff --git a/apps/docs/content/guides/database/json.mdx b/apps/docs/content/guides/database/json.mdx index 1619c015cb6c6..e062dd8fa01c8 100644 --- a/apps/docs/content/guides/database/json.mdx +++ b/apps/docs/content/guides/database/json.mdx @@ -183,6 +183,7 @@ const { data, error } = await supabase.from('books').insert([ ``` +<$Show if="sdk:dart"> ```dart @@ -237,6 +238,8 @@ await supabase.from('books').insert([ ``` + +<$Show if="sdk:swift"> Supabase Swift provides a convenience `AnyJSON` type. @@ -301,6 +304,8 @@ try await supabase.from("books") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -330,6 +335,8 @@ val data = supabase.from("books").insert(listOf( ``` + +<$Show if="sdk:python"> ```python @@ -384,6 +391,7 @@ supabase.from_('books').insert([ ``` + ## Query JSON data @@ -425,6 +433,7 @@ const { data, error } = await supabase.from('books').select(` ``` +<$Show if="sdk:swift"> ```swift @@ -443,6 +452,8 @@ try await supabase ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -456,6 +467,8 @@ val data = supabase.from("books").select(Columns.raw(""" ``` + +<$Show if="sdk:python"> ```python @@ -470,6 +483,7 @@ data = supabase.from_('books').select(""" ``` + | title | description | price | low_age | high_age | diff --git a/apps/docs/content/guides/database/tables.mdx b/apps/docs/content/guides/database/tables.mdx index 547d61ebb17d6..ad9db5e13a0c9 100644 --- a/apps/docs/content/guides/database/tables.mdx +++ b/apps/docs/content/guides/database/tables.mdx @@ -226,6 +226,7 @@ const { data, error } = await supabase.from('movies').insert([ ``` +<$Show if="sdk:dart"> ```dart @@ -241,6 +242,8 @@ await supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -263,6 +266,8 @@ try await supabase.from("movies") ``` + +<$Show if="sdk:python"> ```python @@ -279,6 +284,8 @@ client.from_("movies").insert([ ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -299,6 +306,7 @@ supabase ``` + ### Bulk data loading diff --git a/apps/docs/content/guides/getting-started.mdx b/apps/docs/content/guides/getting-started.mdx index 3e50b8ff57ba6..9024dc736f4ad 100644 --- a/apps/docs/content/guides/getting-started.mdx +++ b/apps/docs/content/guides/getting-started.mdx @@ -101,6 +101,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a React app.', icon: '/docs/img/icons/react-icon', + enabled: true, }, { title: 'Next.js', @@ -109,6 +110,7 @@ hideToc: true 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Next.js app.', icon: '/docs/img/icons/nextjs-icon', hasLightIcon: true, + enabled: true, }, { title: 'Nuxt', @@ -116,6 +118,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Nuxt app.', icon: '/docs/img/icons/nuxt-icon', + enabled: true, }, { title: 'Hono', @@ -123,6 +126,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, secure it with auth, and query the data from a Hono app.', icon: '/docs/img/icons/hono-icon', + enabled: true, }, { title: 'RedwoodJS', @@ -130,6 +134,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database using Prisma migration and seeds, and query the data from a RedwoodJS app.', icon: '/docs/img/icons/redwood-icon', + enabled: true, }, { title: 'Flutter', @@ -137,6 +142,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Flutter app.', icon: '/docs/img/icons/flutter-icon', + enabled: isFeatureEnabled('sdk:dart'), }, { title: 'iOS SwiftUI', @@ -144,6 +150,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from an iOS app.', icon: '/docs/img/icons/swift-icon', + enabled: isFeatureEnabled('sdk:swift'), }, { title: 'Android Kotlin', @@ -151,6 +158,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from an Android Kotlin app.', icon: '/docs/img/icons/kotlin-icon', + enabled: isFeatureEnabled('sdk:kotlin'), }, { title: 'SvelteKit', @@ -158,6 +166,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SvelteKit app.', icon: '/docs/img/icons/svelte-icon', + enabled: true, }, { title: 'SolidJS', @@ -165,6 +174,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a SolidJS app.', icon: '/docs/img/icons/solidjs-icon', + enabled: true, }, { title: 'Vue', @@ -172,6 +182,7 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Vue app.', icon: '/docs/img/icons/vuejs-icon', + enabled: true, }, { title: 'refine', @@ -179,22 +190,25 @@ hideToc: true description: 'Learn how to create a Supabase project, add some sample data to your database, and query the data from a refine app.', icon: '/docs/img/icons/refine-icon', + enabled: true, }, - ].map((item) => { - return ( - - - {item.description} - - - ) - })} + ] + .filter((item) => item.enabled !== false) + .map((item) => { + return ( + + + {item.description} + + + ) + })} ### Web app demos @@ -208,63 +222,63 @@ hideToc: true description: 'Learn how to build a user management app with Next.js and Supabase Database, Auth, and Storage functionality.', icon: '/docs/img/icons/nextjs-icon', - hasLightIcon: true + hasLightIcon: true, }, { title: 'React', href: '/guides/getting-started/tutorials/with-react', description: 'Learn how to build a user management app with React and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/react-icon' + icon: '/docs/img/icons/react-icon', }, { title: 'Vue 3', href: '/guides/getting-started/tutorials/with-vue-3', description: 'Learn how to build a user management app with Vue 3 and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/vuejs-icon' + icon: '/docs/img/icons/vuejs-icon', }, { title: 'Nuxt 3', href: '/guides/getting-started/tutorials/with-nuxt-3', description: 'Learn how to build a user management app with Nuxt 3 and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/nuxt-icon' + icon: '/docs/img/icons/nuxt-icon', }, { title: 'Angular', href: '/guides/getting-started/tutorials/with-angular', description: 'Learn how to build a user management app with Angular and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/angular-icon' + icon: '/docs/img/icons/angular-icon', }, { title: 'RedwoodJS', href: '/guides/getting-started/tutorials/with-redwoodjs', description: 'Learn how to build a user management app with RedwoodJS and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/redwood-icon' + icon: '/docs/img/icons/redwood-icon', }, { title: 'Svelte', href: '/guides/getting-started/tutorials/with-svelte', description: 'Learn how to build a user management app with Svelte and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/svelte-icon' + icon: '/docs/img/icons/svelte-icon', }, { title: 'SvelteKit', href: '/guides/getting-started/tutorials/with-sveltekit', description: 'Learn how to build a user management app with SvelteKit and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/svelte-icon' + icon: '/docs/img/icons/svelte-icon', }, { title: 'refine', href: '/guides/getting-started/tutorials/with-refine', description: 'Learn how to build a user management app with refine and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/refine-icon' + icon: '/docs/img/icons/refine-icon', } ] .map((item) => { @@ -295,7 +309,8 @@ hideToc: true href: '/guides/getting-started/tutorials/with-flutter', description: 'Learn how to build a user management app with Flutter and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/flutter-icon' + icon: '/docs/img/icons/flutter-icon', + enabled: isFeatureEnabled('sdk:dart') }, { title: 'Expo React Native', @@ -303,44 +318,52 @@ hideToc: true description: 'Learn how to build a user management app with Expo React Native and Supabase Database, Auth, and Storage functionality.', icon: '/docs/img/icons/expo-icon', - hasLightIcon: true + hasLightIcon: true, + enabled: true }, { title: 'Android Kotlin', href: '/guides/getting-started/tutorials/with-kotlin', description: 'Learn how to build a product management app with Android and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/kotlin-icon' + icon: '/docs/img/icons/kotlin-icon', + enabled: isFeatureEnabled('sdk:kotlin') }, { title: 'iOS Swift', href: '/guides/getting-started/tutorials/with-swift', description: 'Learn how to build a user management app with iOS and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/swift-icon' + icon: '/docs/img/icons/swift-icon', + enabled: isFeatureEnabled('sdk:swift') }, { title: 'Ionic React', href: '/guides/getting-started/tutorials/with-ionic-react', description: 'Learn how to build a user management app with Ionic React and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/ionic-icon' + icon: '/docs/img/icons/ionic-icon', + enabled: true }, { title: 'Ionic Vue', href: '/guides/getting-started/tutorials/with-ionic-vue', description: 'Learn how to build a user management app with Ionic Vue and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/ionic-icon' + icon: '/docs/img/icons/ionic-icon', + enabled: true }, { title: 'Ionic Angular', href: '/guides/getting-started/tutorials/with-ionic-angular', description: 'Learn how to build a user management app with Ionic Angular and Supabase Database, Auth, and Storage functionality.', - icon: '/docs/img/icons/ionic-icon' + icon: '/docs/img/icons/ionic-icon', + enabled: true } - ].map((item) => { + ] +.filter((item) => item.enabled !== false) +.map((item) => { return ( <$Partial path="billing/pricing/pricing_pitr.mdx" /> - -For a detailed breakdown of how charges are calculated, refer to [Manage Point-in-Time Recovery usage](/docs/guides/platform/manage-your-usage/point-in-time-recovery). + ## Restore to a new project diff --git a/apps/docs/content/guides/platform/custom-domains.mdx b/apps/docs/content/guides/platform/custom-domains.mdx index e4d4a5e88471f..05b4f37e0133a 100644 --- a/apps/docs/content/guides/platform/custom-domains.mdx +++ b/apps/docs/content/guides/platform/custom-domains.mdx @@ -217,6 +217,6 @@ Use the [`vanity-subdomains delete`](/docs/reference/cli/supabase-vanity-subdoma supabase vanity-subdomains delete --project-ref abcdefghijklmnopqrst --experimental ``` -## Pricing - -For a detailed breakdown of how charges are calculated, refer to [Manage Custom Domain usage](/docs/guides/platform/manage-your-usage/custom-domains). +<$Show if="billing:all"> +<$Partial path="billing/pricing/pricing_custom_domains.mdx" /> + diff --git a/apps/docs/content/guides/platform/ipv4-address.mdx b/apps/docs/content/guides/platform/ipv4-address.mdx index 13c0b01abe768..4e1afd14e9493 100644 --- a/apps/docs/content/guides/platform/ipv4-address.mdx +++ b/apps/docs/content/guides/platform/ipv4-address.mdx @@ -126,6 +126,6 @@ Always uses an IPv4 address postgresql://postgres.ajrbwkcuthywfddihrmflo:[YOUR-PASSWORD]@aws-0-us-east-1.pooler.supabase.com:5432/postgres ``` -## Pricing - -For a detailed breakdown of how charges are calculated, refer to [Manage IPv4 usage](/docs/guides/platform/manage-your-usage/ipv4). +<$Show if="billing:all"> +<$Partial path="billing/pricing/pricing_ipv4.mdx" /> + diff --git a/apps/docs/content/guides/realtime/authorization.mdx b/apps/docs/content/guides/realtime/authorization.mdx index 9fb4f3aa8aac9..1ba5a3d45f354 100644 --- a/apps/docs/content/guides/realtime/authorization.mdx +++ b/apps/docs/content/guides/realtime/authorization.mdx @@ -171,6 +171,7 @@ Then, to join a topic with RLS enabled, instantiate the Channel with the `privat ``` + <$Show if="sdk:dart"> ```dart final channel = supabase.channel( @@ -190,6 +191,8 @@ Then, to join a topic with RLS enabled, instantiate the Channel with the `privat ``` + + <$Show if="sdk:swift"> ```swift let channel = supabase.channel("room-1") { @@ -207,6 +210,8 @@ Then, to join a topic with RLS enabled, instantiate the Channel with the `privat ``` + + <$Show if="sdk:kotlin"> ```kotlin val channel = supabase.channel("room-1") { @@ -220,6 +225,8 @@ Then, to join a topic with RLS enabled, instantiate the Channel with the `privat ``` + + <$Show if="sdk:python"> ```py channel = realtime.channel( @@ -238,6 +245,7 @@ Then, to join a topic with RLS enabled, instantiate the Channel with the `privat ``` + #### Allow a user to send a Broadcast message diff --git a/apps/docs/content/guides/realtime/broadcast.mdx b/apps/docs/content/guides/realtime/broadcast.mdx index 18bce67996743..9379189904806 100644 --- a/apps/docs/content/guides/realtime/broadcast.mdx +++ b/apps/docs/content/guides/realtime/broadcast.mdx @@ -33,6 +33,7 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj ``` + <$Show if="sdk:dart"> ```dart @@ -50,6 +51,8 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj ``` + + <$Show if="sdk:swift"> ```swift @@ -62,6 +65,8 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj ``` + + <$Show if="sdk:kotlin"> ```kotlin @@ -73,6 +78,8 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj ``` + + <$Show if="sdk:python"> ```python @@ -88,6 +95,7 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj ``` + ### Receiving Broadcast messages @@ -129,6 +137,7 @@ You can provide a callback for the `broadcast` channel to receive messages. This ``` + <$Show if="sdk:dart"> {/* prettier-ignore */} @@ -150,6 +159,8 @@ You can provide a callback for the `broadcast` channel to receive messages. This ``` + + <$Show if="sdk:swift"> ```swift @@ -166,6 +177,8 @@ You can provide a callback for the `broadcast` channel to receive messages. This ``` + + <$Show if="sdk:kotlin"> {/* prettier-ignore */} @@ -182,6 +195,8 @@ You can provide a callback for the `broadcast` channel to receive messages. This ``` + + <$Show if="sdk:python"> @@ -205,6 +220,7 @@ You can provide a callback for the `broadcast` channel to receive messages. This ``` + ## Send messages @@ -260,6 +276,8 @@ You can use the Supabase client libraries to send Broadcast messages. +<$Show if="sdk:dart"> + {/* prettier-ignore */} @@ -287,6 +305,8 @@ You can use the Supabase client libraries to send Broadcast messages. ``` + + <$Show if="sdk:swift"> {/* prettier-ignore */} @@ -307,6 +327,8 @@ You can use the Supabase client libraries to send Broadcast messages. ``` + + <$Show if="sdk:kotlin"> ```kotlin val myChannel = supabase.channel("test-channel") { @@ -329,6 +351,9 @@ You can use the Supabase client libraries to send Broadcast messages. ``` + + +<$Show if="sdk:python"> @@ -356,6 +381,7 @@ You can use the Supabase client libraries to send Broadcast messages. ``` + {/* supa-mdx-lint-disable-next-line Rule001HeadingCase */} ### Broadcast from the Database @@ -484,6 +510,7 @@ You can pass configuration options while initializing the Supabase Client. ``` + <$Show if="sdk:dart"> By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `self` parameter to `true`. @@ -512,6 +539,8 @@ You can pass configuration options while initializing the Supabase Client. ``` + + <$Show if="sdk:swift"> By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `receiveOwnBroadcasts` parameter to `true`. @@ -534,6 +563,8 @@ You can pass configuration options while initializing the Supabase Client. ``` + + <$Show if="sdk:kotlin"> By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `receiveOwnBroadcasts` parameter to `true`. @@ -562,6 +593,8 @@ You can pass configuration options while initializing the Supabase Client. ``` + + <$Show if="sdk:python"> @@ -595,6 +628,7 @@ You can pass configuration options while initializing the Supabase Client. ``` + ### Acknowledge messages @@ -636,6 +670,7 @@ You can pass configuration options while initializing the Supabase Client. ``` + <$Show if="sdk:dart"> ```dart @@ -659,6 +694,8 @@ You can pass configuration options while initializing the Supabase Client. ``` + + <$Show if="sdk:swift"> You can confirm that Realtime received your message by setting Broadcast's `acknowledgeBroadcasts` config to `true`. @@ -674,6 +711,8 @@ You can pass configuration options while initializing the Supabase Client. ``` + + <$Show if="sdk:kotlin"> By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `acknowledgeBroadcasts` parameter to `true`. @@ -691,9 +730,12 @@ You can pass configuration options while initializing the Supabase Client. ``` + + <$Show if="sdk:python"> Unsupported in Python yet. + Use this to guarantee that the server has received the message before resolving `channelD.send`'s promise. If the `ack` config is not set to `true` when creating the channel, the promise returned by `channelD.send` will resolve immediately. @@ -736,6 +778,7 @@ You can also send a Broadcast message by making an HTTP request to Realtime serv ``` + <$Show if="sdk:dart"> ```dart // No need to subscribe to channel @@ -751,6 +794,8 @@ You can also send a Broadcast message by making an HTTP request to Realtime serv ``` + + <$Show if="sdk:swift"> ```swift let myChannel = await supabase.channel("room-2") { @@ -763,6 +808,8 @@ You can also send a Broadcast message by making an HTTP request to Realtime serv ``` + + <$Show if="sdk:kotlin"> ```kotlin val myChannel = supabase.channel("room-2") { @@ -779,9 +826,12 @@ You can also send a Broadcast message by making an HTTP request to Realtime serv ``` + + <$Show if="sdk:python"> Unsupported in Python yet. + ## Trigger broadcast messages from your database diff --git a/apps/docs/content/guides/realtime/postgres-changes.mdx b/apps/docs/content/guides/realtime/postgres-changes.mdx index 993b6935fd651..d2b35ac32e6ad 100644 --- a/apps/docs/content/guides/realtime/postgres-changes.mdx +++ b/apps/docs/content/guides/realtime/postgres-changes.mdx @@ -238,6 +238,7 @@ const changes = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -252,6 +253,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -272,6 +275,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -293,6 +298,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -305,6 +312,7 @@ changes = supabase.channel('schema-db-changes').on_postgres_changes( ``` + The channel name can be any string except 'realtime'. @@ -337,6 +345,7 @@ const changes = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -350,6 +359,8 @@ final changes = supabase ``` + +<$Show if="sdk:swift"> Use `InsertAction.self` as type to listen only to database `INSERT`s: @@ -367,6 +378,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> Use `PostgresAction.Insert` as type to listen only to database `INSERT`s: @@ -386,6 +399,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -398,6 +413,7 @@ changes = supabase.channel('schema-db-changes').on_postgres_changes( ``` + The channel name can be any string except 'realtime'. @@ -430,6 +446,7 @@ const changes = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -443,6 +460,8 @@ supabase ``` + +<$Show if="sdk:swift"> Use `UpdateAction.self` as type to listen only to database `UPDATE`s: @@ -460,6 +479,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> Use `PostgresAction.Update` as type to listen only to database `UPDATE`s: @@ -479,6 +500,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -491,6 +514,7 @@ changes = supabase.channel('schema-db-changes').on_postgres_changes( ``` + The channel name can be any string except 'realtime'. @@ -523,6 +547,7 @@ const changes = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -536,6 +561,8 @@ supabase ``` + +<$Show if="sdk:swift"> Use `DeleteAction.self` as type to listen only to database `DELETE`s: @@ -553,6 +580,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> Use `PostgresAction.Delete` as type to listen only to database `DELETE`s: @@ -572,6 +601,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -584,6 +615,7 @@ changes = supabase.channel('schema-db-changes').on_postgres_changes( ``` + The channel name can be any string except 'realtime'. @@ -617,6 +649,7 @@ const changes = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -631,6 +664,8 @@ supabase ``` + +<$Show if="sdk:swfit"> ```swift @@ -651,6 +686,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -670,6 +707,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -683,6 +722,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + The channel name can be any string except 'realtime'. @@ -725,6 +765,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -744,6 +785,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -756,6 +799,8 @@ await myChannel.subscribe() ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -770,6 +815,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -787,6 +834,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + ### Filtering for specific changes @@ -819,6 +867,7 @@ const changes = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -838,6 +887,8 @@ const changes = supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -858,6 +909,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -878,6 +931,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -892,6 +947,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + ## Available filters @@ -928,6 +984,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -947,6 +1004,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -967,6 +1026,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -987,6 +1048,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -1001,6 +1064,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + This filter uses Postgres's `=` filter. @@ -1035,6 +1099,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1054,6 +1119,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -1074,6 +1141,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1095,6 +1164,8 @@ myChannel.join() ``` + +<$Show if="sdk:python"> ```python @@ -1109,6 +1180,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + This filter uses Postgres's `!=` filter. @@ -1143,6 +1215,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1162,6 +1235,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -1182,6 +1257,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1202,6 +1279,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -1216,6 +1295,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + This filter uses Postgres's `<` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type. @@ -1250,6 +1330,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1269,6 +1350,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -1289,6 +1372,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1309,6 +1394,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -1323,6 +1410,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + This filter uses Postgres' `<=` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type. @@ -1357,6 +1445,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1376,6 +1465,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -1396,6 +1487,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1416,6 +1509,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -1430,6 +1525,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + This filter uses Postgres's `>` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type. @@ -1464,6 +1560,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1483,6 +1580,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -1503,6 +1602,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1523,6 +1624,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -1537,6 +1640,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + This filter uses Postgres's `>=` filter, so it works for non-numeric types. Make sure to check the expected behavior of the compared data's type. @@ -1571,6 +1675,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1590,6 +1695,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -1610,6 +1717,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1630,6 +1739,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -1644,6 +1755,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + This filter uses Postgres's `= ANY`. Realtime allows a maximum of 100 values for this filter. @@ -1724,6 +1836,7 @@ const channel = supabase ``` +<$Show if="sdk:dart"> ```dart @@ -1746,6 +1859,8 @@ supabase ``` + +<$Show if="sdk:swift"> ```swift @@ -1768,6 +1883,8 @@ for await change in changes { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -1793,6 +1910,8 @@ myChannel.subscribe() ``` + +<$Show if="sdk:python"> ```python @@ -1809,6 +1928,7 @@ changes = supabase.channel('db-changes').on_postgres_changes( ``` + ### Refreshed tokens @@ -1833,6 +1953,7 @@ supabase.realtime.setAuth('fresh-token') ``` +<$Show if="sdk:dart"> ```dart @@ -1840,6 +1961,8 @@ supabase.realtime.setAuth('fresh-token'); ``` + +<$Show if="sdk:swift"> ```swift @@ -1847,6 +1970,8 @@ await supabase.realtime.setAuth("fresh-token") ``` + +<$Show if="sdk:kotlin"> In Kotlin, you have to update the token manually per channel: @@ -1856,6 +1981,8 @@ myChannel.updateAuth("fresh-token") ``` + +<$Show if="sdk:python"> ```python @@ -1863,6 +1990,7 @@ supabase.realtime.set_auth('fresh-token') ``` + ## Limitations diff --git a/apps/docs/content/guides/realtime/presence.mdx b/apps/docs/content/guides/realtime/presence.mdx index 2c599015f088b..86b474cc665f6 100644 --- a/apps/docs/content/guides/realtime/presence.mdx +++ b/apps/docs/content/guides/realtime/presence.mdx @@ -33,6 +33,7 @@ const supabase = createClient(SUPABASE_URL, SUPABASE_KEY) ``` +<$Show if="sdk:dart"> ```dart @@ -49,6 +50,8 @@ final supabase = Supabase.instance.client; ``` + +<$Show if="sdk:swift"> ```swift @@ -60,6 +63,8 @@ let realtime = supabase.realtime ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -71,6 +76,8 @@ val supabase = createSupabaseClient(supabaseUrl, supabaseKey) { ``` + +<$Show if="sdk:python"> ```python @@ -83,6 +90,7 @@ supabase = create_client(SUPABASE_URL, SUPABASE_KEY) ``` + ### Sync and track state @@ -120,6 +128,7 @@ roomOne ``` +<$Show if="sdk:dart"> ```dart @@ -138,6 +147,8 @@ roomOne.onPresenceSync((_) { ``` + +<$Show if="sdk:swift"> Listen to the presence change stream, emitting a new `PresenceAction` whenever someone joins or leaves: @@ -155,6 +166,8 @@ for await presence in presenceStream { ``` + +<$Show if="sdk:kotlin"> Listen to the presence change flow, emitting new a new `PresenceAction` whenever someone joins or leaves: @@ -173,6 +186,8 @@ roomOne.subscribe() ``` + +<$Show if="sdk:python"> Listen to the `sync`, `join`, and `leave` events triggered whenever any client joins or leaves the channel or changes their slice of state: @@ -188,6 +203,7 @@ room_one ``` + ### Sending state @@ -225,6 +241,7 @@ roomOne.subscribe(async (status) => { ``` +<$Show if="sdk:dart"> ```dart @@ -244,6 +261,8 @@ roomOne.subscribe((status, error) async { ``` + +<$Show if="sdk:swift"> ```swift @@ -269,6 +288,8 @@ await roomOne.track( ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -285,6 +306,8 @@ roomOne.track(userStatus) ``` + +<$Show if="sdk:python"> ```python @@ -305,6 +328,7 @@ room_one.subscribe(on_subscribe) ``` + A client will receive state from any other client that is subscribed to the same topic (in this case `room_01`). It will also automatically trigger its own `sync` and `join` event handlers. @@ -337,6 +361,7 @@ untrackPresence() ``` +<$Show if="sdk:dart"> ```dart @@ -351,6 +376,8 @@ untrackPresence(); ``` + +<$Show if="sdk:swift"> ```swift @@ -358,6 +385,8 @@ await roomOne.untrack() ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -369,6 +398,8 @@ untrackPresence() ``` + +<$Show if="sdk:python"> ```python @@ -376,6 +407,7 @@ room_one.untrack() ``` + ## Presence options @@ -409,6 +441,7 @@ const channelC = supabase.channel('test', { ``` +<$Show if="sdk:dart"> ```dart @@ -419,6 +452,8 @@ final channelC = supabase.channel( ``` + +<$Show if="sdk:swift"> ```swift @@ -428,6 +463,8 @@ let channelC = await supabase.channel("test") { ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -439,6 +476,8 @@ val channelC = supabase.channel("test") { ``` + +<$Show if="sdk:python"> ```python @@ -452,4 +491,5 @@ channel_c = supabase.channel('test', { ``` + diff --git a/apps/docs/content/guides/storage/buckets/creating-buckets.mdx b/apps/docs/content/guides/storage/buckets/creating-buckets.mdx index b466ec48cbf3c..65ad5398ba3b1 100644 --- a/apps/docs/content/guides/storage/buckets/creating-buckets.mdx +++ b/apps/docs/content/guides/storage/buckets/creating-buckets.mdx @@ -52,6 +52,7 @@ values ``` +<$Show if="sdk:dart"> ```dart @@ -67,6 +68,8 @@ void main() async { [Reference.](https://pub.dev/documentation/storage_client/latest/storage_client/SupabaseStorageClient/createBucket.html) + +<$Show if="sdk:swift"> ```swift @@ -79,6 +82,8 @@ try await supabase.storage.createBucket( [Reference.](/docs/reference/swift/storage-createbucket) + +<$Show if="sdk:python"> ```python @@ -91,6 +96,7 @@ supabase.storage.create_bucket( [Reference.](/docs/reference/python/storage-createbucket) + ## Restricting uploads diff --git a/apps/docs/content/guides/storage/quickstart.mdx b/apps/docs/content/guides/storage/quickstart.mdx index ee1f9e06f8c3f..2ea0e5b8ff83f 100644 --- a/apps/docs/content/guides/storage/quickstart.mdx +++ b/apps/docs/content/guides/storage/quickstart.mdx @@ -72,6 +72,7 @@ const { data, error } = await supabase.storage.createBucket('avatars') [Reference.](/docs/reference/javascript/storage-createbucket) +<$Show if="sdk:dart"> ```dart @@ -87,6 +88,8 @@ void main() async { [Reference.](https://pub.dev/documentation/storage_client/latest/storage_client/SupabaseStorageClient/createBucket.html) + +<$Show if="sdk:swift"> ```swift @@ -96,6 +99,8 @@ try await supabase.storage.createBucket("avatars") [Reference.](/docs/reference/swift/storage-createbucket) + +<$Show if="sdk:python"> ```python @@ -105,6 +110,7 @@ response = supabase.storage.create_bucket('avatars') [Reference.](/docs/reference/python/storage-createbucket) + ## Upload a file @@ -138,6 +144,7 @@ const { data, error } = await supabase.storage [Reference.](/docs/reference/javascript/storage-from-upload) +<$Show if="sdk:dart"> ```dart @@ -157,6 +164,7 @@ void main() async { [Reference.](https://pub.dev/documentation/storage_client/latest/storage_client/SupabaseStorageClient/createBucket.html) + ## Download a file @@ -189,6 +197,7 @@ const { data, error } = await supabase.storage.from('avatars').download('public/ [Reference.](/docs/reference/javascript/storage-from-download) +<$Show if="sdk:dart"> ```dart @@ -205,6 +214,8 @@ void main() async { [Reference.](/docs/reference/dart/storage-from-download) + +<$Show if="sdk:swift"> ```swift @@ -214,6 +225,8 @@ let response = try await supabase.storage.from("avatars").download(path: "public [Reference.](/docs/reference/python/storage-from-download) + +<$Show if="sdk:python"> ```python @@ -223,6 +236,7 @@ response = supabase.storage.from_('avatars').download('public/avatar1.png') [Reference.](/docs/reference/python/storage-from-download) + ## Add security rules diff --git a/apps/docs/content/guides/storage/serving/image-transformations.mdx b/apps/docs/content/guides/storage/serving/image-transformations.mdx index 952d6a79b11b0..6d79231daad15 100644 --- a/apps/docs/content/guides/storage/serving/image-transformations.mdx +++ b/apps/docs/content/guides/storage/serving/image-transformations.mdx @@ -42,6 +42,7 @@ supabase.storage.from('bucket').getPublicUrl('image.jpg', { ``` +<$Show if="sdk:dart"> ```dart @@ -55,6 +56,8 @@ final url = supabase.storage.from('bucket').getPublicUrl( ``` + +<$Show if="sdk:swift"> ```swift @@ -66,6 +69,8 @@ let url = try await supabase.storage.from("bucket") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -75,6 +80,8 @@ val url = supabase.storage.from("bucket").publicRenderUrl("image.jpg") { ``` + +<$Show if="sdk:python"> ```python @@ -90,6 +97,7 @@ url = supabase.storage.from_('avatars').get_public_url( ``` + An example URL could look like this: @@ -125,6 +133,7 @@ supabase.storage.from('bucket').createSignedUrl('image.jpg', 60000, { ``` +<$Show if="sdk:dart"> ```dart @@ -139,6 +148,8 @@ final url = await supabase.storage.from('bucket').createSignedUrl( ``` + +<$Show if="sdk:swift"> ```swift @@ -154,6 +165,8 @@ let url = try await supabase.storage.from("bucket") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -163,6 +176,7 @@ val url = supabase.storage.from("bucket").createSignedUrl("image.jpg", 60.second ``` + The transformation options are embedded into the token attached to the URL — they cannot be changed once signed. @@ -194,6 +208,7 @@ supabase.storage.from('bucket').download('image.jpg', { ``` +<$Show if="sdk:dart"> ```dart @@ -207,6 +222,8 @@ final data = await supabase.storage.from('bucket').download( ``` + +<$Show if="sdk:swift"> ```swift @@ -221,6 +238,8 @@ let data = try await supabase.storage.from("bucket") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -240,6 +259,8 @@ supabase.storage.from("bucket").downloadAuthenticatedTo("image.jpg", file) { ``` + +<$Show if="sdk:python"> ```python @@ -253,6 +274,7 @@ response = supabase.storage.from_('bucket').download( ``` + ## Automatic image optimization (WebP) @@ -295,6 +317,7 @@ await supabase.storage.from('bucket').download('image.jpeg', { ``` +<$Show if="sdk:dart"> ```dart @@ -309,6 +332,8 @@ final data = await supabase.storage.from('bucket').download( ``` + +<$Show if="sdk:swift"> ```swift @@ -324,6 +349,8 @@ let data = try await supabase.storage.from("bucket") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -345,6 +372,8 @@ supabase.storage.from("bucket").downloadAuthenticatedTo("image.jpg", file) { ``` + +<$Show if="sdk:python"> ```python @@ -361,6 +390,7 @@ response = supabase.storage.from_('bucket').download( ``` + ## Next.js loader @@ -430,6 +460,7 @@ supabase.storage.from('bucket').download('image.jpg', { ``` +<$Show if="sdk:dart"> ```dart @@ -442,6 +473,8 @@ final data = await supabase.storage.from('bucket').download( ``` + +<$Show if="sdk:swift"> ```swift @@ -455,6 +488,8 @@ let data = try await supabase.storage.from("bucket") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -474,6 +509,8 @@ supabase.storage["bucket"].downloadAuthenticatedTo("image.jpg", file) { ``` + +<$Show if="sdk:python"> ```python @@ -488,6 +525,7 @@ response = supabase.storage.from_('bucket').download( ``` + ### Resizing @@ -532,6 +570,7 @@ supabase.storage.from('bucket').download('image.jpg', { ``` +<$Show if="sdk:dart"> ```dart @@ -546,6 +585,8 @@ final data = supabase.storage.from('bucket').download( ``` + +<$Show if="sdk:swift"> ```swift @@ -561,6 +602,8 @@ let data = try await supabase.storage.from("bucket") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -582,6 +625,8 @@ supabase.storage.from("bucket").downloadAuthenticatedTo("image.jpg", file) { ``` + +<$Show if="sdk:python"> ```python @@ -598,6 +643,7 @@ response = supabase.storage.from_('bucket').download( ``` + ### Limits @@ -621,11 +667,9 @@ response = supabase.storage.from_('bucket').download( | BMP | `bmp` | ☑️ | ☑️ | | TIFF | `tiff` | ☑️ | ☑️ | -## Pricing - +<$Show if="billing:all"> <$Partial path="billing/pricing/pricing_storage_image_transformations.mdx" /> - -For a detailed breakdown of how charges are calculated, refer to [Manage Storage Image Transformations usage](/docs/guides/platform/manage-your-usage/storage-image-transformations). + ## Self hosting diff --git a/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx b/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx index 99fec0b097934..2a1db32286c8b 100644 --- a/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx +++ b/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx @@ -191,6 +191,8 @@ Instead of `https://project-id.supabase.co` use `https://project-id.storage.supa +<$Show if="sdk:kotlin"> + Kotlin supports resumable uploads natively for all targets: @@ -222,6 +224,9 @@ Instead of `https://project-id.supabase.co` use `https://project-id.storage.supa ``` + + +<$Show if="sdk:python"> @@ -269,6 +274,7 @@ Instead of `https://project-id.supabase.co` use `https://project-id.storage.supa ``` + ### Upload URL diff --git a/apps/docs/content/guides/storage/uploads/standard-uploads.mdx b/apps/docs/content/guides/storage/uploads/standard-uploads.mdx index fdde4e31fb78d..2e7a2562a90ab 100644 --- a/apps/docs/content/guides/storage/uploads/standard-uploads.mdx +++ b/apps/docs/content/guides/storage/uploads/standard-uploads.mdx @@ -48,6 +48,7 @@ async function uploadFile(file) { ``` +<$Show if="sdk:dart"> ```dart @@ -58,6 +59,8 @@ Future uploadFile(File file) async { ``` + +<$Show if="sdk:swift"> ```swift @@ -70,6 +73,8 @@ try await supabase.storage.from("bucket_name").upload(path: "file_path", file: f ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -80,6 +85,8 @@ supabase.storage.from("bucket_name").upload("file_path", file) ``` + +<$Show if="sdk:python"> ```python @@ -87,6 +94,7 @@ response = supabase.storage.from_('bucket_name').upload('file_path', file) ``` + ## Overwriting files @@ -117,6 +125,7 @@ await supabase.storage.from('bucket_name').upload('file_path', file, { ``` +<$Show if="sdk:dart"> ```dart @@ -128,6 +137,8 @@ await supabase.storage.from('bucket_name').upload( ``` + +<$Show if="sdk:swift"> ```swift @@ -147,6 +158,8 @@ try await supabase.storage.from("bucket_name") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -156,6 +169,8 @@ supabase.storage.from("bucket_name").upload("file_path", bytes) { ``` + +<$Show if="sdk:python"> ```python @@ -165,6 +180,7 @@ response = supabase.storage.from_('bucket_name').upload('file_path', file, { ``` + We do advise against overwriting files when possible, as our Content Delivery Network will take sometime to propagate the changes to all the edge nodes leading to stale content. @@ -197,6 +213,7 @@ await supabase.storage.from('bucket_name').upload('file_path', file, { ``` +<$Show if="sdk:dart"> ```dart @@ -208,6 +225,8 @@ await supabase.storage.from('bucket_name').upload( ``` + +<$Show if="sdk:swift"> ```swift @@ -227,6 +246,8 @@ try await supabase.storage.from("bucket_name") ``` + +<$Show if="sdk:kotlin"> ```kotlin @@ -236,6 +257,8 @@ supabase.storage.from("bucket_name").upload("file_path", bytes) { ``` + +<$Show if="sdk:python"> ```python @@ -245,6 +268,7 @@ response = supabase.storage.from_('bucket_name').upload('file_path', file, { ``` + ## Concurrency diff --git a/apps/docs/features/directives/Partial.ts b/apps/docs/features/directives/Partial.ts index eec3b109ebf4f..888071f2a70f5 100644 --- a/apps/docs/features/directives/Partial.ts +++ b/apps/docs/features/directives/Partial.ts @@ -65,6 +65,7 @@ function toFilePath(node: MdxJsxFlowElement) { if (typeof path !== 'string' || !isMdFile(path)) { throw new Error('Invalid $Partial path: path must end with .mdx or .md') } + const filePath = join(PARTIALS_DIRECTORY, path) if (!filePath.startsWith(PARTIALS_DIRECTORY)) { throw new Error(`Invalid $Partial path: Path must be inside ${PARTIALS_DIRECTORY}`) diff --git a/apps/docs/features/directives/Show.test.ts b/apps/docs/features/directives/Show.test.ts new file mode 100644 index 0000000000000..cbe3024a11472 --- /dev/null +++ b/apps/docs/features/directives/Show.test.ts @@ -0,0 +1,199 @@ +import { mdxToMarkdown } from 'mdast-util-mdx' +import { toMarkdown } from 'mdast-util-to-markdown' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +import { type Feature } from 'common' +import { showRemark } from './Show' +import { fromDocsMarkdown } from './utils.server' + +// Mock the isFeatureEnabled function from common package +vi.mock('common/enabled-features', () => ({ + isFeatureEnabled: vi.fn(), +})) + +const { isFeatureEnabled } = await import('common/enabled-features') + +describe('$Show', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should keep children and remove $Show wrapper when feature is enabled', async () => { + vi.mocked(isFeatureEnabled).mockReturnValue(true) + + const markdown = ` +# Test content + +<$Show if="test-feature"> +This content should be visible when feature is enabled. + +## A nested heading + +Some more content. + + +Content after the show block. +`.trim() + + const mdast = fromDocsMarkdown(markdown) + const transformed = showRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + const expected = ` +# Test content + +This content should be visible when feature is enabled. + +## A nested heading + +Some more content. + +Content after the show block. +`.trimStart() + + expect(output).toEqual(expected) + expect(isFeatureEnabled).toHaveBeenCalledWith('test-feature') + }) + + it('should remove entire $Show block and children when feature is disabled', async () => { + vi.mocked(isFeatureEnabled).mockReturnValue(false) + + const markdown = ` +# Test content + +<$Show if="disabled-feature"> +This content should NOT be visible when feature is disabled. + +## This heading should also be removed + +Some more content that should be hidden. + + +Content after the show block should remain. +`.trim() + + const mdast = fromDocsMarkdown(markdown) + const transformed = showRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + const expected = ` +# Test content + +Content after the show block should remain. +`.trimStart() + + expect(output).toEqual(expected) + expect(isFeatureEnabled).toHaveBeenCalledWith('disabled-feature') + }) + + it('should handle multiple $Show blocks with different feature flags', async () => { + vi.mocked(isFeatureEnabled).mockImplementation((feature) => { + if (feature === ('enabled-feature' as Feature)) return true + return false + }) + + const markdown = ` +# Test content + +<$Show if="enabled-feature"> +This should be visible. + + +<$Show if="disabled-feature"> +This should be hidden. + + +<$Show if="another-disabled-feature"> +This should also be hidden. + + +Final content. +`.trim() + + const mdast = fromDocsMarkdown(markdown) + const transformed = showRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + const expected = ` +# Test content + +This should be visible. + +Final content. +`.trimStart() + + expect(output).toEqual(expected) + expect(isFeatureEnabled).toHaveBeenCalledWith('enabled-feature') + expect(isFeatureEnabled).toHaveBeenCalledWith('disabled-feature') + expect(isFeatureEnabled).toHaveBeenCalledWith('another-disabled-feature') + }) + + it('should handle nested $Show blocks correctly', async () => { + vi.mocked(isFeatureEnabled).mockImplementation((feature) => { + if (feature === ('outer-feature' as Feature)) return true + return false + }) + + const markdown = ` +# Test content + +<$Show if="outer-feature"> +Outer content visible. + +<$Show if="inner-feature"> +Inner content should be hidden. + + +More outer content. + +`.trim() + + const mdast = fromDocsMarkdown(markdown) + const transformed = showRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + const expected = ` +# Test content + +Outer content visible. + +More outer content. +`.trimStart() + + expect(output).toEqual(expected) + expect(isFeatureEnabled).toHaveBeenCalledWith('outer-feature') + expect(isFeatureEnabled).toHaveBeenCalledWith('inner-feature') + }) + + it('should throw error when "if" attribute is missing', () => { + const markdown = ` +<$Show> +Content without if attribute. + +`.trim() + + const mdast = fromDocsMarkdown(markdown) + + expect(() => { + showRemark()(mdast) + }).toThrow('$Show directive requires a string value for the "if" attribute') + }) + + it('should throw error when "if" attribute is not a string', () => { + const markdown = ` +<$Show if={true}> +Content with non-string if attribute. + +`.trim() + + const mdast = fromDocsMarkdown(markdown) + + expect(() => { + showRemark()(mdast) + }).toThrow('$Show directive requires a string value for the "if" attribute') + }) +}) diff --git a/apps/docs/features/directives/Show.ts b/apps/docs/features/directives/Show.ts new file mode 100644 index 0000000000000..94401222466aa --- /dev/null +++ b/apps/docs/features/directives/Show.ts @@ -0,0 +1,72 @@ +/** + * The Show directive supports conditional rendering of content based on feature flags. + * It uses the isFeatureEnabled function from the common package to determine whether + * the content should be displayed. + * + * If the feature is enabled, the <$Show> tag is stripped but its children remain. + * If the feature is disabled, both the <$Show> tag and its children are removed. + * + * ## Examples + * + * ### Basic usage + * + * ```mdx + * <$Show if="feature-name"> + * This content will only show if the feature is enabled. + * + * ``` + */ + +import { type Root } from 'mdast' +import type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx' +import { type Parent } from 'unist' +import { visitParents } from 'unist-util-visit-parents' + +import { isFeatureEnabled, type Feature } from 'common/enabled-features' +import { getAttributeValue } from './utils.server' + +export function showRemark() { + return function transform(tree: Root) { + const nodesToProcess = [] as Array<{ + node: MdxJsxFlowElement + parent: Parent + shouldShow: boolean + }> + + // Collect all $Show nodes and determine their visibility + visitParents(tree, 'mdxJsxFlowElement', (node: MdxJsxFlowElement, ancestors) => { + if (node.name !== '$Show') return + + const parent = ancestors[ancestors.length - 1] + const featureName = getAttributeValue(node, 'if') + + if (typeof featureName !== 'string') { + throw new Error('$Show directive requires a string value for the "if" attribute') + } + + const shouldShow = isFeatureEnabled(featureName as Feature) + + nodesToProcess.push({ + node, + parent, + shouldShow, + }) + }) + + // Process nodes in reverse order to avoid index shifting issues + for (let i = nodesToProcess.length - 1; i >= 0; i--) { + const { node, parent, shouldShow } = nodesToProcess[i] + const nodeIndex = parent.children.indexOf(node) + + if (shouldShow) { + // Feature is enabled: remove the $Show wrapper but keep children + parent.children.splice(nodeIndex, 1, ...node.children) + } else { + // Feature is disabled: remove the entire $Show element and its children + parent.children.splice(nodeIndex, 1) + } + } + + return tree + } +} diff --git a/apps/docs/features/directives/utils.ts b/apps/docs/features/directives/utils.ts index 0138a0b9e66cc..225b54bfb0146 100644 --- a/apps/docs/features/directives/utils.ts +++ b/apps/docs/features/directives/utils.ts @@ -10,6 +10,7 @@ import { codeSampleRemark } from './CodeSample' import { codeTabsRemark } from './CodeTabs' import { fromDocsMarkdown } from './utils.server' import { partialsRemark } from './Partial' +import { showRemark } from './Show' type Transformer = (ast: Root) => Root | Promise @@ -27,6 +28,7 @@ export async function preprocessMdx(mdx: string, transformers: Transformer[]) export function preprocessMdxWithDefaults(mdx: string) { return preprocessMdx(mdx, [ + showRemark(), remarkMkDocsAdmonition(), remarkPyMdownTabs(), partialsRemark(), diff --git a/apps/docs/features/docs/MdxBase.tsx b/apps/docs/features/docs/MdxBase.tsx index 8e77bf230e497..672a49b04ab06 100644 --- a/apps/docs/features/docs/MdxBase.tsx +++ b/apps/docs/features/docs/MdxBase.tsx @@ -4,6 +4,7 @@ import rehypeKatex from 'rehype-katex' import remarkGfm from 'remark-gfm' import remarkMath from 'remark-math' +import { isFeatureEnabled } from 'common' import { preprocessMdxWithDefaults } from '~/features/directives/utils' import { components } from '~/features/docs/MdxBase.shared' import { SerializeOptions } from '~/types/next-mdx-remote-serialize' @@ -39,6 +40,7 @@ const MDXRemoteBase = async ({ } = mdxOptions const finalOptions = { + scope: { isFeatureEnabled }, ...mdxOptions, ...otherOptions, mdxOptions: { diff --git a/packages/common/enabled-features/enabled-features.json b/packages/common/enabled-features/enabled-features.json index 41de346f04d7f..58068090300d3 100644 --- a/packages/common/enabled-features/enabled-features.json +++ b/packages/common/enabled-features/enabled-features.json @@ -27,6 +27,8 @@ "database:replication": true, "database:roles": true, + "docs:self-hosting": true, + "integrations:vercel": true, "integrations:show_stripe_wrapper": true, @@ -53,5 +55,11 @@ "project_settings:legacy_jwt_keys": true, "project_settings:log_drains": true, - "reports:all": true + "reports:all": true, + + "sdk:csharp": true, + "sdk:dart": true, + "sdk:kotlin": true, + "sdk:python": true, + "sdk:swift": true } diff --git a/packages/common/enabled-features/enabled-features.schema.json b/packages/common/enabled-features/enabled-features.schema.json index 0e979da780c84..ca38b65741655 100644 --- a/packages/common/enabled-features/enabled-features.schema.json +++ b/packages/common/enabled-features/enabled-features.schema.json @@ -95,6 +95,11 @@ "description": "Enable the database roles page" }, + "docs:self-hosting": { + "type": "boolean", + "description": "Enable documentation for self-hosting" + }, + "integrations:vercel": { "type": "boolean", "description": "Enable the vercel integration section in the organization and project settings pages" @@ -182,6 +187,27 @@ "reports:all": { "type": "boolean", "description": "Enable the project reports page" + }, + + "sdk:csharp": { + "type": "boolean", + "description": "Enable the C# SDK" + }, + "sdk:dart": { + "type": "boolean", + "description": "Enable the Dart/Flutter SDK" + }, + "sdk:kotlin": { + "type": "boolean", + "description": "Enable the Kotlin SDK" + }, + "sdk:python": { + "type": "boolean", + "description": "Enable the Python SDK" + }, + "sdk:swift": { + "type": "boolean", + "description": "Enable the Swift SDK" } }, "required": [ @@ -206,6 +232,7 @@ "billing:all", "database:replication", "database:roles", + "docs:self-hosting", "integrations:vercel", "integrations:show_stripe_wrapper", "profile:show_email", @@ -225,7 +252,12 @@ "project_settings:log_drains", "project_connection:javascript_example", "project_connection:dart_example", - "reports:all" + "reports:all", + "sdk:csharp", + "sdk:dart", + "sdk:kotlin", + "sdk:python", + "sdk:swift" ], "additionalProperties": false } diff --git a/packages/common/enabled-features/index.ts b/packages/common/enabled-features/index.ts index ec5987eb6dd5c..8dba6caf4648d 100644 --- a/packages/common/enabled-features/index.ts +++ b/packages/common/enabled-features/index.ts @@ -1,5 +1,5 @@ import type { components } from 'api-types' -import enabledFeaturesRaw from './enabled-features.json' +import enabledFeaturesRaw from './enabled-features.json' with { type: 'json' } const enabledFeaturesStaticObj = enabledFeaturesRaw as Omit diff --git a/packages/common/package.json b/packages/common/package.json index 804804f11f06b..5f780e1cf12b2 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,7 @@ { "name": "common", "version": "0.0.0", + "type": "module", "main": "./index.tsx", "types": "./index.tsx", "license": "MIT", From 541c9f90ccfedcdad1613bf896a415cc4106e496 Mon Sep 17 00:00:00 2001 From: "kemal.earth" <606977+kemaldotearth@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:58:24 +0100 Subject: [PATCH 5/7] fix(studio): query performance empty columns (#38437) fix: query performance empty columns The role and total time columns were not rendering the results that were there. Added a check to make sure they render. --- .../QueryPerformance/QueryPerformanceGrid.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx index d6f5d5a0b9d51..87bf77ba7883b 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx @@ -90,6 +90,22 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance ) } + if (col.id === 'rolname') { + return ( +
    +

    {value || 'n/a'}

    +
    + ) + } + + if (col.id === 'prop_total_time') { + return ( +
    +

    {value || 'n/a'}

    +
    + ) + } + const isTime = col.name.includes('time') const formattedValue = !!value && typeof value === 'number' && !isNaN(value) && isFinite(value) From e4cc65f0ac8a6d123216ddffde91d777916c5c99 Mon Sep 17 00:00:00 2001 From: Cuong Do Date: Thu, 4 Sep 2025 11:05:39 -0400 Subject: [PATCH 6/7] Add myself to humans.txt (#38435) --- apps/docs/public/humans.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt index 922a7d2565206..b50412670444b 100644 --- a/apps/docs/public/humans.txt +++ b/apps/docs/public/humans.txt @@ -31,6 +31,7 @@ Chris Martin Chris Stockton Chris Ward Craig Cannon +Cuong Do Danny White Darren Cunningham Dave Wilson From 11ebbb7a7cbe1e924f41bc25ffe0b1a7f2f8f6bf Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:47:47 -0400 Subject: [PATCH 7/7] fix: embedding upload error (#38439) Markdown processing for uploads now depends on the 'common' package (for feature flag querying). Added it to the GitHub Workflow to make sure it is accessible during that step. (Actually added the whole packages directory since it seems likely that we might need other stuff from this in the future.) --- .github/workflows/search.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/search.yml b/.github/workflows/search.yml index 7baa26f498144..c12c3ddd3e28e 100644 --- a/.github/workflows/search.yml +++ b/.github/workflows/search.yml @@ -46,6 +46,7 @@ jobs: apps/docs apps/www/.env.local.example examples + packages supabase - uses: pnpm/action-setup@v4