ronantakizawa/codereview-bench huggingface dataset:
Repository: supabase/supabase
File: packages/ui-patterns/src/McpUrlBuilder/types.ts (TypeScript)

Code before:
  | ClaudeCodeMcpConfig
  | ClaudeDesktopMcpConfig
  | CursorMcpConfig
  | FactoryMcpConfig
  | GooseMcpConfig
  | McpClientBaseConfig
  | OtherMcpConfig
  | VSCodeMcpConfig
  | WindsurfMcpConfig

// Type guards
export function isVSCodeMcpConfig(config: McpClientConfig): config is VSCodeMcpConfig {
  return 'servers' in config && 'supabase' in config.servers
}

export function isGooseMcpConfig(config: McpClientConfig): config is GooseMcpConfig {
  return 'extensions' in config && 'supabase' in config.extensions
}

export function isMcpServersConfig(
  config: McpClientConfig
): config is McpClientBaseConfig | ClaudeCodeMcpConfig | FactoryMcpConfig {
  return 'mcpServers' in config && 'supabase' in config.mcpServers
}

// Helper to extract MCP URL from any config type
export function getMcpUrl(config: McpClientConfig): string {
  if (isVSCodeMcpConfig(config)) {
    return config.servers.supabase.url
  }
  if (isGooseMcpConfig(config)) {
    return config.extensions.supabase.uri
  }
  if (isMcpServersConfig(config)) {
    return config.mcpServers.supabase.url
  }
  throw new Error('Invalid MCP config type')
}


Review comment (security):
Note: Was not able to authenticate myself through oauth flow when configuring the MCP for streamable http on Windsurf but it worked using `mcp-remote`. It's weird because according to their [docs](https://docs.windsurf.com/windsurf/cascade/mcp#adding-a-new-mcp-plugin), they support both stdio and http transport

Code after:
      type: 'http'
      url: string
    }
  }
}

export interface CodexMcpConfig {
  mcp_servers: {
    supabase: {
      url: string
    }
  }
}

// Union of all possible config types
export type McpClientConfig =
  | ClaudeCodeMcpConfig
  | ClaudeDesktopMcpConfig
  | CodexMcpConfig
  | CursorMcpConfig
  | FactoryMcpConfig
  | GooseMcpConfig
  | McpClientBaseConfig
  | OtherMcpConfig
  | VSCodeMcpConfig
  | WindsurfMcpConfig

// Type guards
export function isVSCodeMcpConfig(config: McpClientConfig): config is VSCodeMcpConfig {
  return 'servers' in config && 'supabase' in config.servers
}

export function isGooseMcpConfig(config: McpClientConfig): config is GooseMcpConfig {
  return 'extensions' in config && 'supabase' in config.extensions
}

export function isCodexMcpConfig(config: McpClientConfig): config is CodexMcpConfig {
  return 'mcp_servers' in config && 'supabase' in config.mcp_servers
}

export function isMcpServersConfig(
  config: McpClientConfig
): config is McpClientBaseConfig | ClaudeCodeMcpConfig | FactoryMcpConfig {
  return 'mcpServers' in config && 'supabase' in config.mcpServers
}

// Helper to extract MCP URL from any config type
export function getMcpUrl(config: McpClientConfig): string {
  if (isVSCodeMcpConfig(config)) {
    return config.servers.supabase.url
  }
Repository: supabase/supabase
File: packages/shared-data/pricing.ts (TypeScript)

Code before:
      },
      {
        key: 'auth.customSMTPServer',
        title: 'Custom SMTP server',
        plans: {
          free: true,
          pro: true,
          team: true,
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.removeSupabaseBranding',
        title: 'Remove Supabase branding from emails',
        plans: {
          free: false,
          pro: true,
          team: true,
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.auditLogs',
        title: 'Auth Audit logs',
        plans: {
          free: '1 hour',
          pro: '7 days',
          team: '28 days',
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.basicMFA',
        title: 'Basic Multi-Factor Auth',
        plans: {
          free: true,
          pro: true,
          team: true,
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.advancedMFAPhone',
        title: 'Advanced Multi-Factor Auth - Phone',
        plans: {
          free: false,
          pro: ['$75 per month for first project', 'then $10 per month per additional projects'],

Review comment (security):
```suggestion
        title: 'Auth Audit Logs',
```

Code after:
      },
      {
        key: 'auth.customSMTPServer',
        title: 'Custom SMTP server',
        plans: {
          free: true,
          pro: true,
          team: true,
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.removeSupabaseBranding',
        title: 'Remove Supabase branding from emails',
        plans: {
          free: false,
          pro: true,
          team: true,
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.auditLogs',
        title: 'Auth Audit Logs',
        plans: {
          free: '1 hour',
          pro: '7 days',
          team: '28 days',
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.basicMFA',
        title: 'Basic Multi-Factor Auth',
        plans: {
          free: true,
          pro: true,
          team: true,
          enterprise: true,
        },
        usage_based: false,
      },
      {
        key: 'auth.advancedMFAPhone',
        title: 'Advanced Multi-Factor Auth - Phone',
        plans: {
          free: false,
          pro: ['$75 per month for first project', 'then $10 per month per additional projects'],
Repository: supabase/supabase
File: apps/studio/components/layouts/ProjectLayout/LayoutHeader/FeedbackDropdown/FeedbackDropdown.utils.ts (TypeScript)

Code before:
    .from('feedback-attachments')
    .upload(filename, blob, options)

  if (uploadError || !file) {
    console.error('Failed to upload screenshot attachment:', uploadError)
    return undefined
  }

  const signedUrls = await generateAttachmentURLs({
    bucket: 'feedback-attachments',
    filenames: [file.path],
  })
  return signedUrls[0]
}

/**
 * Client-side heuristic to quickly detect obvious support requests
 * This provides immediate feedback before the AI classification completes
 */
export function isLikelySupportRequest(text: string): boolean {
  if (!text || text.trim().length === 0) return false

  const lowerText = text.toLowerCase()

  // Common support request patterns
  const supportPatterns = [
    // Help requests
    /i need help with/i,
    /i'm having trouble/i,
    /i'm having issues/i,
    /i'm having problems/i,
    /can you help/i,
    /could you help/i,
    /please help/i,
    /need help/i,
    /how do i/i,
    /how can i/i,
    /how to/i,
    /why isn't/i,
    /why doesn't/i,
    /why won't/i,
    /why can't/i,

    // Problem indicators
    /it's not working/i,
    /it doesn't work/i,
    /it won't work/i,
    /it can't work/i,
    /isn't working/i,
    /doesn't work/i,
    /won't work/i,

Review comment (suggestion):
rekcon its better to remove nouns from the support patterns here

e.g "i'm having trouble" vs "having trouble"

would capture more instances i think - otherwise there'll be too many cases to (attempt to) catch, given this is a best guess sort of approach

Code after:
    .from('feedback-attachments')
    .upload(filename, blob, options)

  if (uploadError || !file) {
    console.error('Failed to upload screenshot attachment:', uploadError)
    return undefined
  }

  const signedUrls = await generateAttachmentURLs({
    bucket: 'feedback-attachments',
    filenames: [file.path],
  })
  return signedUrls[0]
}

/**
 * Client-side heuristic to quickly detect obvious support requests
 * This provides immediate feedback before the AI classification completes
 */
export function isLikelySupportRequest(text: string): boolean {
  if (!text || text.trim().length === 0) return false

  const lowerText = text.toLowerCase()

  // Common support request patterns
  const supportPatterns = [
    // Help requests
    /need help/i,
    /having trouble/i,
    /having issues/i,
    /having problems/i,
    /can you help/i,
    /could you help/i,
    /please help/i,
    /need help/i,
    /how do/i,
    /how can/i,
    /how to/i,
    /why isn't/i,
    /why doesn't/i,
    /why won't/i,
    /why can't/i,

    // Problem indicators
    /it's not working/i,
    /it doesn't work/i,
    /it won't work/i,
    /it can't work/i,
    /isn't working/i,
    /doesn't work/i,
    /won't work/i,
Repository: supabase/supabase
File: apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx (TypeScript)

Code before:
      selectedTable === undefined ||
      project === undefined ||
      row === undefined ||
      !isTableLike(selectedTable)
    )
      return
    if (selectedTable.primary_keys.length === 0) {
      return toast('Unable to load value as table has no primary keys')
    }

    const pkMatch = selectedTable.primary_keys.reduce((a, b) => {
      return { ...a, [b.name]: (row as any)[b.name] }
    }, {})

    getCellValue(
      {
        table: { schema: selectedTable.schema, name: selectedTable.name },
        column: column,
        pkMatch,
        projectRef: project?.ref,
        connectionString: project?.connectionString,
      },
      {
        onSuccess: (data) => {
          setJsonStr(JSON.stringify(data))
        },
      }
    )
  }

  useEffect(() => {
    if (visible) {
      const temp = prettifyJSON(jsonString)
      setJsonStr(temp)
    }
  }, [visible])

  // reset the mutation when the panel closes. Fixes an issue where the value is truncated if you close and reopen the
  // panel again
  const onClose = useCallback(() => {
    reset()
    closePanel()
  }, [reset])

  return (
    <SidePanel
      size="large"
      header={
        <div className="flex items-center justify-between">
          {view === 'edit' ? (
            <p>

Review comment (suggestion):
The JSONstr undefined error is mostly likely coming from this. Data is undefined and setting that state, erroring out when referencing .tostring 

Code after:
      selectedTable === undefined ||
      project === undefined ||
      row === undefined ||
      !isTableLike(selectedTable)
    )
      return
    if (selectedTable.primary_keys.length === 0) {
      return toast('Unable to load value as table has no primary keys')
    }

    const pkMatch = selectedTable.primary_keys.reduce((a, b) => {
      return { ...a, [b.name]: (row as any)[b.name] }
    }, {})

    getCellValue(
      {
        table: { schema: selectedTable.schema, name: selectedTable.name },
        column: column,
        pkMatch,
        projectRef: project?.ref,
        connectionString: project?.connectionString,
      },
      {
        onSuccess: (data: unknown | undefined) => {
          if (data === undefined) return
          setJsonStr(JSON.stringify(data))
        },
      }
    )
  }

  useEffect(() => {
    if (visible) {
      const temp = prettifyJSON(jsonString)
      setJsonStr(temp)
    }
  }, [visible])

  // reset the mutation when the panel closes. Fixes an issue where the value is truncated if you close and reopen the
  // panel again
  const onClose = useCallback(() => {
    reset()
    closePanel()
  }, [reset])

  return (
    <SidePanel
      size="large"
      header={
        <div className="flex items-center justify-between">
          {view === 'edit' ? (
Repository: supabase/supabase
File: apps/studio/data/database-indexes/indexes-query.ts (TypeScript)

Code before:
import { getIndexesSQL } from '@supabase/pg-meta/src/sql/studio/get-indexes'
import { useQuery } from '@tanstack/react-query'

import { UseCustomQueryOptions } from 'types'
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
import { databaseIndexesKeys } from './keys'

type GetIndexesArgs = {
  schema?: string
}

export type DatabaseIndex = {
  name: string
  schema: string
  table: string
  definition: string
  columns: string // Comma-separated strings
}

export type IndexesVariables = GetIndexesArgs & {
  projectRef?: string
  connectionString?: string | null
}

export async function getIndexes(
  { schema, projectRef, connectionString }: IndexesVariables,
  signal?: AbortSignal
) {
  if (!schema) {
    throw new Error('schema is required')
  }

  const sql = getIndexesSQL({ schema })

  const { result } = await executeSql(
    { projectRef, connectionString, sql, queryKey: ['indexes', schema] },
    signal
  )

  return result as DatabaseIndex[]
}

export type IndexesData = Awaited<ReturnType<typeof getIndexes>>
export type IndexesError = ExecuteSqlError

export const useIndexesQuery = <TData = IndexesData>(
  { projectRef, connectionString, schema }: IndexesVariables,

Review comment (suggestion):
Actually @avallete and I agreed that we should keep Studio-specific queries in the Studio codebase, and only put general purpose queries in pg-meta ([ref](https://supabase.slack.com/archives/C085L4EFUGH/p1762284188642459))

Code after:
import { useQuery } from '@tanstack/react-query'

import { getIndexesSQL } from 'data/sql/queries/get-indexes'
import { UseCustomQueryOptions } from 'types'
import { executeSql, ExecuteSqlError } from '../sql/execute-sql-query'
import { databaseIndexesKeys } from './keys'

type GetIndexesArgs = {
  schema?: string
}

export type DatabaseIndex = {
  name: string
  schema: string
  table: string
  definition: string
  columns: string // Comma-separated strings
}

export type IndexesVariables = GetIndexesArgs & {
  projectRef?: string
  connectionString?: string | null
}

export async function getIndexes(
  { schema, projectRef, connectionString }: IndexesVariables,
  signal?: AbortSignal
) {
  if (!schema) {
    throw new Error('schema is required')
  }

  const sql = getIndexesSQL({ schema })

  const { result } = await executeSql(
    { projectRef, connectionString, sql, queryKey: ['indexes', schema] },
    signal
  )

  return result as DatabaseIndex[]
}

export type IndexesData = Awaited<ReturnType<typeof getIndexes>>
export type IndexesError = ExecuteSqlError

export const useIndexesQuery = <TData = IndexesData>(
  { projectRef, connectionString, schema }: IndexesVariables,
Repository: supabase/supabase
File: apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx (TypeScript)

Code before:
            {isErrorCount
              ? 'Failed to retrieve total number of invoices'
              : `Showing ${offset + 1} to ${offset + invoices.length} out of ${count} invoices`}
          </p>
          <div className="flex items-center gap-x-2" aria-label="Pagination">
            <Button
              icon={<ChevronLeft />}
              aria-label={`Go to page ${page - 1}`}
              type="default"
              size="tiny"
              disabled={page === 1}
              onClick={async () => setPage(page - 1)}
            />
            <Button
              icon={<ChevronRight />}
              aria-label={`Go to page ${page + 1}`}
              type="default"
              size="tiny"
              disabled={page * PAGE_LIMIT >= (count ?? 0)}
              onClick={async () => setPage(page + 1)}
            />
          </div>
        </CardFooter>
      )}
    </Card>
  )
}


Review comment (suggestion):
Table elements like `tfoot` and `caption` should only ever container inline elements. This is outside of the scope of the table, hence the `CardFooter`.

Code after:
          <p className="text-foreground-muted text-sm">
            {isErrorCount
              ? 'Failed to retrieve total number of invoices'
              : typeof count === 'number'
                ? `Showing ${offset + 1} to ${offset + invoices.length} out of ${count} invoices`
                : `Showing ${offset + 1} to ${offset + invoices.length} invoices`}
          </p>
          <div className="flex items-center gap-x-2" aria-label="Pagination">
            <Button
              icon={<ChevronLeft />}
              aria-label="Previous page"
              type="default"
              size="tiny"
              disabled={page === 1}
              onClick={async () => setPage(page - 1)}
            />
            <Button
              icon={<ChevronRight />}
              aria-label="Next page"
              type="default"
              size="tiny"
              disabled={page * PAGE_LIMIT >= (count ?? 0)}
              onClick={async () => setPage(page + 1)}
            />
          </div>
        </CardFooter>
      )}
    </Card>
  )
}
Repository: supabase/supabase
File: apps/studio/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider.tsx (TypeScript)

Code before:
          action: 'sidebar_opened',
          properties: {
            sidebar: activeSidebar.id as (typeof SIDEBAR_KEYS)[keyof typeof SIDEBAR_KEYS],
          },
          groups: {
            project: project?.ref ?? 'Unknown',
            organization: org?.slug ?? 'Unknown',
          },
        })
        setSidebarLocalStorage(activeSidebar.id)
      } else {
        setSidebarLocalStorage('')
        setSidebarUrlParam(null)
      }
    }
  }, [activeSidebar])

  // Handle toggling of sidebars on page init
  // Prioritize URL params first, then local storage
  useEffect(() => {
    if (router.isReady && isLoadedLocalStorage) {
      if (
        typeof sidebarURLParam === 'string' &&
        Object.values(SIDEBAR_KEYS).includes(
          sidebarURLParam as (typeof SIDEBAR_KEYS)[keyof typeof SIDEBAR_KEYS]
        )
      ) {
        openSidebar(sidebarURLParam)
      } else if (
        !!sidebarLocalStorage &&
        Object.values(SIDEBAR_KEYS).includes(
          sidebarLocalStorage as (typeof SIDEBAR_KEYS)[keyof typeof SIDEBAR_KEYS]
        )
      ) {
        openSidebar(sidebarLocalStorage)
      }
    }
  }, [router.isReady, isLoadedLocalStorage])

  return <>{children}</>
}


Review comment (bug):
It might be good to wrap `sidebarURLParam` and `sidebarLocalStorage` in `useLatest()` so we can potentially avoid some stale state bugs

Code after:
    if (!!project) {
      if (activeSidebar) {
        // add event tracking
        sendEvent({
          action: 'sidebar_opened',
          properties: {
            sidebar: activeSidebar.id as (typeof SIDEBAR_KEYS)[keyof typeof SIDEBAR_KEYS],
          },
          groups: {
            project: project?.ref ?? 'Unknown',
            organization: org?.slug ?? 'Unknown',
          },
        })
        setSidebarLocalStorage(activeSidebar.id)
      } else {
        setSidebarLocalStorage('')
        setSidebarUrlParam(null)
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeSidebar])

  // Handle toggling of sidebars on page init
  // Prioritize URL params first, then local storage
  useEffect(() => {
    if (router.isReady && isLoadedLocalStorage) {
      if (
        typeof sidebarURLParamRef.current === 'string' &&
        Object.values(SIDEBAR_KEYS).includes(
          sidebarURLParamRef.current as (typeof SIDEBAR_KEYS)[keyof typeof SIDEBAR_KEYS]
        )
      ) {
        console.log('Open sidebar based on URL')
        openSidebar(sidebarURLParamRef.current)
      } else if (
        !!sidebarLocalStorageRef.current &&
        Object.values(SIDEBAR_KEYS).includes(
          sidebarLocalStorageRef.current as (typeof SIDEBAR_KEYS)[keyof typeof SIDEBAR_KEYS]
        )
      ) {
        openSidebar(sidebarLocalStorageRef.current)
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [router.isReady, isLoadedLocalStorage])

  return <>{children}</>
}
Repository: supabase/supabase
File: apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts (TypeScript)

Code before:
        {
          name: 'Ionic Angular',
          url: '/guides/getting-started/tutorials/with-ionic-angular' as `/${string}`,
        },
        {
          name: 'Swift',
          url: '/guides/getting-started/tutorials/with-swift' as `/${string}`,
          enabled: sdkSwiftEnabled,
        },
      ],
    },
    {
      name: 'AI Tools',
      url: undefined,
      items: [
        {
          name: 'Prompts',
          url: '/guides/getting-started/ai-prompts' as `/${string}`,
        },
        {
          name: 'Model context protocol (MCP)',
          url: '/guides/getting-started/mcp' as `/${string}`,
        },
      ],
    },
  ],
}

export const cli = {
  title: 'CLI',
  items: [
    { name: 'Overview', url: '/guides/cli' },
    { name: 'Managing Environments', url: '/guides/cli/managing-environments' },
    {
      name: 'Using environment variables in config.toml',
      url: '/guides/cli/using-environment-variables-in-config',
    },
  ],
}

export const NativeMobileLoginItems = [
  {
    name: 'Apple',
    icon: '/docs/img/icons/apple-icon',
    url: '/guides/auth/social-login/auth-apple?platform=react-native',
  },
  {
    name: 'Google',
    icon: '/docs/img/icons/google-icon',
    url: '/guides/auth/social-login/auth-google?platform=react-native',
  },

Review comment (suggestion):
The meaning of `byom` is not obvious to me as an outsider. Should we be bit more specific?
```suggestion
          url: '/guides/getting-started/byo-mcp' as `/${string}`,
```

Code after:
        {
          name: 'Ionic Angular',
          url: '/guides/getting-started/tutorials/with-ionic-angular' as `/${string}`,
        },
        {
          name: 'Swift',
          url: '/guides/getting-started/tutorials/with-swift' as `/${string}`,
          enabled: sdkSwiftEnabled,
        },
      ],
    },
    {
      name: 'AI Tools',
      url: undefined,
      items: [
        {
          name: 'Prompts',
          url: '/guides/getting-started/ai-prompts' as `/${string}`,
        },
        {
          name: 'Supabase MCP server',
          url: '/guides/getting-started/mcp' as `/${string}`,
        },
        {
          name: 'Deploy MCP servers',
          url: '/guides/getting-started/byom' as `/${string}`,
        },
      ],
    },
  ],
}

export const cli = {
  title: 'CLI',
  items: [
    { name: 'Overview', url: '/guides/cli' },
    { name: 'Managing Environments', url: '/guides/cli/managing-environments' },
    {
      name: 'Using environment variables in config.toml',
      url: '/guides/cli/using-environment-variables-in-config',
    },
  ],
}

export const NativeMobileLoginItems = [
  {
    name: 'Apple',
    icon: '/docs/img/icons/apple-icon',
    url: '/guides/auth/social-login/auth-apple?platform=react-native',
  },
  {