shadimage
supabase
blockplatform-kit-nextjssource: ok · hit

Platform Kit for Nextjs and Supabase

Platform Kit for Nextjs and Supabase

npx shadcn add "https://supabase.com/ui/r/platform-kit-nextjs.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · medium
forms@hookform/resolversreact-hook-form
commandcmdk
chartsrecharts
toastssonner

Registry dependencies

alertbadgebuttoncardchartcommanddialogdrawerformhover-cardinputlabelpopoverselectskeletonswitchtabletoggletoggle-grouptooltip

Files32 · source cached 10/8/2026, 5:56:46 PM

registry/default/platform/platform-kit-nextjs/app/api/ai/sql/route.ts
import { NextResponse } from 'next/server'
import OpenAI from 'openai'
import createClient from 'openapi-fetch'

import type { paths } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api-schema'
import { listTablesSql } from '@/registry/default/platform/platform-kit-nextjs/lib/pg-meta'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

const client = createClient<paths>({
  baseUrl: 'https://api.supabase.com',
  headers: {
    Authorization: `Bearer ${process.env.SUPABASE_MANAGEMENT_API_TOKEN}`,
  },
})

// Function to get database schema
async function getDbSchema(projectRef: string) {
  const token = process.env.SUPABASE_MANAGEMENT_API_TOKEN
  if (!token) {
    throw new Error('Supabase Management API token is not configured.')
  }

  const sql = listTablesSql()

  const { data, error } = await client.POST('/v1/projects/{ref}/database/query', {
    params: {
      path: {
        ref: projectRef,
      },
    },
    body: {
      query: sql,
      read_only: true,
    },
  })

  if (error) {
    throw error
  }

  return data as any
}

function formatSchemaForPrompt(schema: any) {
  let schemaString = ''
  if (schema && Array.isArray(schema)) {
    schema.forEach((table: any) => {
      const columnInfo = table.columns.map((c: any) => `${c.name} (${c.data_type})`)
      schemaString += `Table "${table.name}" has columns: ${columnInfo.join(', ')}.\n`
    })
  }
  return schemaString
}

export async function POST(request: Request) {
  try {
    const { prompt, projectRef } = await request.json()

    if (!prompt) {
      return NextResponse.json({ message: 'Prompt is required.' }, { status: 400 })
    }
    if (!projectRef) {
      return NextResponse.json({ message: 'projectRef is required.' }, { status: 400 })
    }

    // Implement your permission check here (e.g. check if the user is a member of the project)
    // In this example, everyone can access all projects
    const userHasPermissionForProject = Boolean(projectRef)

    if (!userHasPermissionForProject) {
      return NextResponse.json(
        { message: 'You do not have permission to access this project.' },
        { status: 403 }
      )
    }

    // 1. Get database schema
    const schema = await getDbSchema(projectRef)
    const formattedSchema = formatSchemaForPrompt(schema)

    // 2. Create a prompt for OpenAI
    const systemPrompt = `You are an expert SQL assistant. Given the following database schema, write a SQL query that answers the user's question. Return only the SQL query, do not include any explanations or markdown.\n\nSchema:\n${formattedSchema}`

    // 3. Call OpenAI to generate SQL using responses.create (plain text output)
    const response = await openai.responses.create({
      model: 'gpt-4.1',
      instructions: systemPrompt, // Use systemPrompt as instructions
      input: prompt, // User's question
    })

    const sql = response.output_text

    if (!sql) {
      return NextResponse.json(
        { message: 'Could not generate SQL from the prompt.' },
        { status: 500 }
      )
    }

    // 4. Return the generated SQL
    return NextResponse.json({ sql })
  } catch (error: any) {
    console.error('AI SQL generation error:', error)
    const errorMessage = error.message || 'An unexpected error occurred.'
    const status = error.response?.status || 500
    return NextResponse.json({ message: errorMessage }, { status })
  }
}
registry/default/platform/platform-kit-nextjs/app/api/supabase-proxy/[...path]/route.ts
import { NextResponse } from 'next/server'

async function forwardToSupabaseAPI(request: Request, method: string, params: { path: string[] }) {
  // eslint-disable-next-line turbo/no-undeclared-env-vars
  if (!process.env.SUPABASE_MANAGEMENT_API_TOKEN) {
    console.error('Supabase Management API token is not configured.')
    return NextResponse.json({ message: 'Server configuration error.' }, { status: 500 })
  }

  const { path } = params
  const apiPath = path.join('/')

  const url = new URL(request.url)
  url.protocol = 'https'
  url.hostname = 'api.supabase.com'
  url.port = '443'
  url.pathname = apiPath

  const projectRef = path[2]

  // Implement your permission check here (e.g. check if the user is a member of the project)
  // In this example, everyone can access all projects
  const userHasPermissionForProject = Boolean(projectRef)

  if (!userHasPermissionForProject) {
    return NextResponse.json(
      { message: 'You do not have permission to access this project.' },
      { status: 403 }
    )
  }

  try {
    const forwardHeaders: HeadersInit = {
      // eslint-disable-next-line turbo/no-undeclared-env-vars
      Authorization: `Bearer ${process.env.SUPABASE_MANAGEMENT_API_TOKEN}`,
    }

    // Copy relevant headers from the original request
    const contentType = request.headers.get('content-type')
    if (contentType) {
      forwardHeaders['Content-Type'] = contentType
    }

    const fetchOptions: RequestInit = {
      method,
      headers: forwardHeaders,
    }

    // Include body for methods that support it
    if (method !== 'GET' && method !== 'HEAD') {
      try {
        const body = await request.text()
        if (body) {
          fetchOptions.body = body
        }
      } catch (error) {
        // Handle cases where body is not readable
        console.warn('Could not read request body:', error)
      }
    }

    const response = await fetch(url, fetchOptions)

    // Get response body
    const responseText = await response.text()
    let responseData

    try {
      responseData = responseText ? JSON.parse(responseText) : null
    } catch {
      responseData = responseText
    }

    // Return the response with the same status
    return NextResponse.json(responseData, { status: response.status })
  } catch (error: any) {
    console.error('Supabase API proxy error:', error)
    const errorMessage = error.message || 'An unexpected error occurred.'
    return NextResponse.json({ message: errorMessage }, { status: 500 })
  }
}

export async function GET(request: Request, { params }: { params: Promise<{ path: string[] }> }) {
  const resolvedParams = await params
  return forwardToSupabaseAPI(request, 'GET', resolvedParams)
}

export async function HEAD(request: Request, { params }: { params: Promise<{ path: string[] }> }) {
  const resolvedParams = await params
  return forwardToSupabaseAPI(request, 'HEAD', resolvedParams)
}

export async function POST(request: Request, { params }: { params: Promise<{ path: string[] }> }) {
  const resolvedParams = await params
  return forwardToSupabaseAPI(request, 'POST', resolvedParams)
}

export async function PUT(request: Request, { params }: { params: Promise<{ path: string[] }> }) {
  const resolvedParams = await params
  return forwardToSupabaseAPI(request, 'PUT', resolvedParams)
}

export async function DELETE(
  request: Request,
  { params }: { params: Promise<{ path: string[] }> }
) {
  const resolvedParams = await params
  return forwardToSupabaseAPI(request, 'DELETE', resolvedParams)
}

export async function PATCH(request: Request, { params }: { params: Promise<{ path: string[] }> }) {
  const resolvedParams = await params
  return forwardToSupabaseAPI(request, 'PATCH', resolvedParams)
}
registry/default/platform/platform-kit-nextjs/components/dynamic-form.tsx
'use client'

import { zodResolver } from '@hookform/resolvers/zod'
import { useEffect, useRef } from 'react'
import { useForm } from 'react-hook-form'
import type { z, ZodTypeAny } from 'zod'

import { Button } from '@/registry/default/components/ui/button'
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/registry/default/components/ui/form'
import { Input } from '@/registry/default/components/ui/input'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/registry/default/components/ui/select'
import { Switch } from '@/registry/default/components/ui/switch'

interface DynamicFormProps<T extends z.ZodRawShape = z.ZodRawShape> {
  schema: z.ZodObject<T>
  onSubmit: (data: z.infer<z.ZodObject<T>>) => void
  isLoading?: boolean
  initialValues?: Partial<z.infer<z.ZodObject<T>>>
  labels?: Record<string, string | { label: string; options?: Record<string, string> }>
  columnInfo?: Record<string, { data_type: string; is_nullable: boolean }>
}

const getZodDef = (schema: ZodTypeAny): Record<string, any> =>
  (schema as any)._def || (schema as any).def

const unwrapZodType = (fieldSchema: ZodTypeAny): ZodTypeAny => {
  if (
    !fieldSchema ||
    typeof getZodDef(fieldSchema) !== 'object' ||
    getZodDef(fieldSchema) === null
  ) {
    throw new Error(
      `unwrapZodType received an invalid Zod schema object. Check the console for the problematic schema/key.`
    )
  }
  let currentSchema = fieldSchema

  // Support both old (typeName) and new (type) Zod formats
  const getTypeName = (def: Record<string, any>) => def.typeName || def.type

  while (
    getTypeName(getZodDef(currentSchema)) === 'ZodOptional' ||
    getTypeName(getZodDef(currentSchema)) === 'optional' ||
    getTypeName(getZodDef(currentSchema)) === 'ZodDefault' ||
    getTypeName(getZodDef(currentSchema)) === 'default' ||
    getTypeName(getZodDef(currentSchema)) === 'ZodNullable' ||
    getTypeName(getZodDef(currentSchema)) === 'nullable' ||
    getTypeName(getZodDef(currentSchema)) === 'ZodEffects' ||
    getTypeName(getZodDef(currentSchema)) === 'effects'
  ) {
    const typeName = getTypeName(getZodDef(currentSchema))
    if (typeName === 'ZodEffects' || typeName === 'effects') {
      // For ZodEffects, get the schema inside the effect
      currentSchema = getZodDef(currentSchema).schema
    } else {
      currentSchema = getZodDef(currentSchema).innerType
    }
  }
  return currentSchema
}

export function DynamicForm<T extends z.ZodRawShape = z.ZodRawShape>({
  schema,
  onSubmit,
  isLoading = false,
  initialValues,
  labels,
  columnInfo,
}: DynamicFormProps<T>) {
  const isInitializingRef = useRef(true)

  const defaultValues = Object.keys(schema.shape).reduce(
    (acc, key) => {
      const originalFieldSchema = schema.shape[key]
      if (typeof originalFieldSchema === 'undefined') {
        throw new Error(
          `Schema error: schema.shape['${key}'] is undefined. Check schema definition.`
        )
      }

      // Support both old (typeName) and new (type) Zod formats
      const getTypeName = (def: Record<string, any>) => def.typeName || def.type

      if (
        getTypeName(getZodDef(originalFieldSchema)) === 'ZodDefault' ||
        getTypeName(getZodDef(originalFieldSchema)) === 'default'
      ) {
        acc[key] = getZodDef(originalFieldSchema).defaultValue()
        return acc
      }

      const baseType = unwrapZodType(originalFieldSchema)

      switch (getTypeName(getZodDef(baseType))) {
        case 'ZodString':
        case 'string':
          acc[key] = ''
          break
        case 'ZodBoolean':
        case 'boolean':
          acc[key] = false // Default optional booleans to false
          break
        case 'ZodEnum':
        case 'enum':
          // For enums, use the first enum value as default
          const enumValues = getZodDef(baseType).values || []
          acc[key] = enumValues.length > 0 ? enumValues[0] : ''
          break
        case 'ZodNumber':
        case 'number':
          acc[key] = 0 // Default optional numbers to 0
          break
        case 'ZodArray':
        case 'array':
          acc[key] = [] // Default arrays to empty array
          break
        default:
          acc[key] = undefined // For other types, or if truly no default makes sense
          break
      }
      return acc
    },
    {} as Record<string, any>
  )

  const form = useForm<z.infer<typeof schema>>({
    resolver: zodResolver(schema),
    defaultValues: defaultValues as any,
  })

  useEffect(() => {
    if (initialValues) {
      isInitializingRef.current = true
      const schemaKeys = Object.keys(schema.shape)
      const processedInitialValues = schemaKeys.reduce(
        (acc, key) => {
          const fieldDefFromSchema = schema.shape[key]
          if (typeof fieldDefFromSchema === 'undefined') {
            throw new Error(`Schema error in useEffect: schema.shape['${key}'] is undefined.`)
          }
          const value = initialValues.hasOwnProperty(key) ? initialValues[key] : undefined
          const baseFieldType = unwrapZodType(fieldDefFromSchema)

          // Support both old (typeName) and new (type) Zod formats
          const getTypeName = (def: Record<string, any>) => def.typeName || def.type

          const fieldTypeName = getTypeName(getZodDef(baseFieldType))
          if (fieldTypeName === 'ZodBoolean' || fieldTypeName === 'boolean') {
            acc[key] = !!value
          } else if (fieldTypeName === 'ZodString' || fieldTypeName === 'string') {
            acc[key] = value === null || value === undefined ? '' : String(value)
          } else if (fieldTypeName === 'ZodEnum' || fieldTypeName === 'enum') {
            const enumValues = getZodDef(baseFieldType).values || []
            acc[key] =
              value === null || value === undefined || !enumValues.includes(value)
                ? enumValues[0] || ''
                : String(value)
          } else if (fieldTypeName === 'ZodNumber' || fieldTypeName === 'number') {
            const num = Number(value)
            acc[key] = isNaN(num) ? 0 : num
          } else if (fieldTypeName === 'ZodArray' || fieldTypeName === 'array') {
            // For arrays, the value should already be an array from the database
            // Handle null values properly
            if (Array.isArray(value)) {
              acc[key] = value
            } else if (value === null || value === undefined) {
              acc[key] = []
            } else if (typeof value === 'string' && value.startsWith('{') && value.endsWith('}')) {
              // Parse PostgreSQL array format like {ONE,TWO} into JavaScript array
              try {
                const innerContent = value.slice(1, -1) // Remove { and }
                if (innerContent.trim() === '') {
                  acc[key] = []
                } else {
                  // Split by comma and trim whitespace
                  acc[key] = innerContent.split(',').map((item: string) => item.trim())
                }
              } catch {
                // If parsing fails, default to empty array
                acc[key] = []
              }
            } else {
              acc[key] = []
            }
          } else {
            acc[key] = value
          }
          return acc
        },
        {} as Record<string, any>
      )
      form.reset(processedInitialValues as any)
      setTimeout(() => {
        isInitializingRef.current = false
      }, 0)
    } else {
      isInitializingRef.current = false
    }
  }, [initialValues, form, schema])

  const renderField = (fieldName: string, fieldSchema: ZodTypeAny) => {
    const baseType = unwrapZodType(fieldSchema)
    // Support both old (typeName) and new (type) Zod formats
    const getTypeName = (def: Record<string, any>) => def.typeName || def.type
    const typeName = getTypeName(getZodDef(baseType))
    const description = fieldSchema.description

    return (
      <FormField
        key={fieldName}
        control={form.control}
        name={fieldName as any}
        render={({ field }) => {
          const labelConfig = labels?.[fieldName]
          const label =
            typeof labelConfig === 'string' ? labelConfig : labelConfig?.label || fieldName
          const typeDisplay = columnInfo?.[fieldName]?.data_type || ''
          switch (typeName) {
            case 'ZodString':
            case 'string':
              return (
                <FormItem className="py-6 border-b">
                  <div className="w-full flex flex-col lg:flex-row lg:items-center justify-between w-full gap-4 lg:gap-8">
                    <div className="flex-1 pr-4">
                      <FormLabel>{label}</FormLabel>
                      <div className="text-sm text-muted-foreground mt-1">{typeDisplay}</div>
                      {description && <FormDescription>{description}</FormDescription>}
                      <FormMessage />
                    </div>
                    <div className="flex-1 lg:max-w-1/2">
                      <FormControl>
                        <Input
                          placeholder={`Enter your ${fieldName}`}
                          {...field}
                          value={String(field.value || '')}
                        />
                      </FormControl>
                    </div>
                  </div>
                </FormItem>
              )
            case 'ZodNumber':
            case 'number':
              return (
                <FormItem className="py-6 border-b">
                  <div className="w-full flex flex-col lg:flex-row lg:items-center justify-between w-full gap-4 lg:gap-8">
                    <div className="flex-1 pr-4">
                      <FormLabel>{label}</FormLabel>
                      <div className="text-sm text-muted-foreground">{typeDisplay}</div>
                      {description && <FormDescription>{description}</FormDescription>}
                      <FormMessage />
                    </div>
                    <div className="flex-1 lg:max-w-1/2">
                      <FormControl>
                        <Input
                          type="number"
                          placeholder={`Enter value for ${fieldName}`}
                          {...field}
                          value={String(field.value ?? '')}
                          onChange={(e) => {
                            const value = e.target.value
                            const num = parseInt(value, 10)
                            field.onChange(isNaN(num) ? undefined : num)
                          }}
                        />
                      </FormControl>
                    </div>
                  </div>
                </FormItem>
              )
            case 'ZodBoolean':
            case 'boolean':
              return (
                <FormItem className="py-6 border-b flex flex-row items-center justify-between gap-8">
                  <div>
                    <FormLabel>{label}</FormLabel>
                    <div className="text-sm text-muted-foreground">{typeDisplay}</div>
                    {description && <FormDescription>{description}</FormDescription>}
                    <FormMessage />
                  </div>
                  <FormControl>
                    <Switch checked={!!field.value} onCheckedChange={field.onChange} />
                  </FormControl>
                </FormItem>
              )
            case 'ZodEnum':
            case 'enum':
              const options = getZodDef(baseType).values
              const optionLabels = typeof labelConfig === 'object' ? labelConfig.options : undefined
              return (
                <FormItem className="py-6 border-b">
                  <div className="w-full flex flex-col lg:flex-row lg:items-center justify-between w-full gap-4 lg:gap-8">
                    <div className="flex-1 pr-4">
                      <FormLabel>{label}</FormLabel>
                      <div className="text-sm text-muted-foreground">{typeDisplay}</div>
                      {description && <FormDescription>{description}</FormDescription>}
                      <FormMessage />
                    </div>
                    <div className="flex-1 lg:max-w-1/2">
                      <Select
                        onValueChange={(value) => {
                          if (!isInitializingRef.current) {
                            field.onChange(value)
                          }
                        }}
                        value={String(field.value || '')}
                      >
                        <FormControl>
                          <SelectTrigger>
                            <SelectValue placeholder={`Select a ${fieldName}`} />
                          </SelectTrigger>
                        </FormControl>
                        <SelectContent>
                          {options.map((option: string) => (
                            <SelectItem key={option} value={option}>
                              {optionLabels?.[option] || option}
                            </SelectItem>
                          ))}
                        </SelectContent>
                      </Select>
                    </div>
                  </div>
                </FormItem>
              )
            case 'ZodArray':
            case 'array':
              return (
                <FormItem className="py-6 border-b">
                  <div className="flex flex-row items-center justify-between w-full gap-8">
                    <div className="flex-1 pr-4">
                      <FormLabel>{label}</FormLabel>
                      <div className="text-sm text-muted-foreground">
                        Enter as JSON array: [&quot;item1&quot;, &quot;item2&quot;]
                        {columnInfo?.[fieldName]?.is_nullable && ' (leave empty for null)'}
                      </div>

                      {description && <FormDescription>{description}</FormDescription>}
                      <FormMessage />
                    </div>
                    <div className="flex-1">
                      <FormControl>
                        <Input
                          placeholder={
                            columnInfo?.[fieldName]?.is_nullable
                              ? `[&quot;item1&quot;, &quot;item2&quot;] or leave empty for null`
                              : `[&quot;item1&quot;, &quot;item2&quot;]`
                          }
                          {...field}
                          value={
                            field.value === null || field.value === undefined
                              ? ''
                              : Array.isArray(field.value)
                                ? JSON.stringify(field.value)
                                : String(field.value || '')
                          }
                          onChange={(e) => {
                            const value = e.target.value
                            if (value.trim() === '') {
                              // Empty string means null (for nullable) or empty array (for non-nullable)
                              field.onChange(null)
                            } else {
                              try {
                                const parsed = JSON.parse(value)
                                if (Array.isArray(parsed)) {
                                  field.onChange(parsed)
                                } else {
                                  // If it's not an array, treat as invalid input
                                  field.onChange(value)
                                }
                              } catch {
                                // Invalid JSON, keep the string value to show error
                                field.onChange(value)
                              }
                            }
                          }}
                        />
                      </FormControl>
                    </div>
                  </div>
                </FormItem>
              )
            default:
              return <></>
          }
        }}
      />
    )
  }

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)}>
        {Object.keys(schema.shape).map((fieldName) =>
          renderField(fieldName, schema.shape[fieldName])
        )}
        <div className="pt-6">
          <Button type="submit" disabled={isLoading}>
            {isLoading ? 'Saving...' : 'Save'}
          </Button>
        </div>
      </form>
    </Form>
  )
}
registry/default/platform/platform-kit-nextjs/components/logo-supabase.tsx
export function LogoSupabase({ className, size = 24 }: { className?: string; size?: number }) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      fill="currentColor"
      viewBox="0 0 109 113"
      className={className}
      style={{ width: size, height: size }}
    >
      <path
        fill="url(#supabase_svg__a)"
        d="M63.708 110.284c-2.86 3.601-8.658 1.628-8.727-2.97l-1.007-67.251h45.22c8.19 0 12.758 9.46 7.665 15.874z"
      ></path>
      <path
        fill="url(#supabase_svg__b)"
        fillOpacity="0.2"
        d="M63.708 110.284c-2.86 3.601-8.658 1.628-8.727-2.97l-1.007-67.251h45.22c8.19 0 12.758 9.46 7.665 15.874z"
      ></path>
      <path
        fill="#3ECF8E"
        d="M45.317 2.071c2.86-3.601 8.657-1.628 8.726 2.97l.442 67.251H9.83c-8.19 0-12.759-9.46-7.665-15.875z"
      ></path>
      <defs>
        <linearGradient
          id="supabase_svg__a"
          x1="53.974"
          x2="94.163"
          y1="54.974"
          y2="71.829"
          gradientUnits="userSpaceOnUse"
        >
          <stop stopColor="#249361"></stop>
          <stop offset="1" stopColor="#3ECF8E"></stop>
        </linearGradient>
        <linearGradient
          id="supabase_svg__b"
          x1="36.156"
          x2="54.484"
          y1="30.578"
          y2="65.081"
          gradientUnits="userSpaceOnUse"
        >
          <stop></stop>
          <stop offset="1" stopOpacity="0"></stop>
        </linearGradient>
      </defs>
    </svg>
  )
}
registry/default/platform/platform-kit-nextjs/components/results-table.tsx
'use client'

import { cn } from '@/lib/utils'
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/registry/default/components/ui/table'

interface ResultsTableProps {
  data: any[]
  onRowClick?: (row: any) => void
}

export function ResultsTable({ data, onRowClick }: ResultsTableProps) {
  if (!data || data.length === 0) {
    return <p className="p-4 text-center">The query returned no results.</p>
  }

  const headers = Object.keys(data[0])

  return (
    <div className="overflow-auto">
      <Table>
        <TableHeader>
          <TableRow className="hover:bg-transparent">
            {headers.map((header) => (
              <TableHead className="first:pl-6 lg:first:pl-8 last:pr-6 lg:last:pr-8" key={header}>
                {header}
              </TableHead>
            ))}
          </TableRow>
        </TableHeader>
        <TableBody>
          {data.map((row, rowIndex) => (
            <TableRow
              key={rowIndex}
              onClick={() => onRowClick?.(row)}
              className={cn(onRowClick && 'cursor-pointer hover:bg-muted/50 group')}
            >
              {headers.map((header) => (
                <TableCell
                  className="first:pl-6 lg:first:pl-8 last:pr-6 lg:last:pr-8 text-xs text-muted-foreground group-hover:text-foreground min-w-32"
                  key={`${rowIndex}-${header}`}
                >
                  <div className="text-xs font-mono w-fit max-w-96 truncate">
                    {JSON.stringify(row[header]).replace(/^"|"$/g, '')}
                  </div>
                </TableCell>
              ))}
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/components/sql-editor.tsx
'use client'

import { Editor } from '@monaco-editor/react'
import {
  AlertTriangle,
  ArrowUp,
  BarChart as BarChartIcon,
  FileText,
  Loader2,
  Wand,
} from 'lucide-react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from 'recharts'

import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import { Button } from '@/registry/default/components/ui/button'
import {
  ChartConfig,
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from '@/registry/default/components/ui/chart'
import { Input } from '@/registry/default/components/ui/input'
import { Label } from '@/registry/default/components/ui/label'
import { Popover, PopoverContent, PopoverTrigger } from '@/registry/default/components/ui/popover'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/registry/default/components/ui/select'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import { Switch } from '@/registry/default/components/ui/switch'
import { ToggleGroup, ToggleGroupItem } from '@/registry/default/components/ui/toggle-group'
import { ResultsTable } from '@/registry/default/platform/platform-kit-nextjs/components/results-table'
import { useRunQuery } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-run-query'

interface SqlEditorProps {
  projectRef: string
  initialSql?: string
  queryKey?: any
  label?: string
  onResults?: (data: any[] | undefined) => void
  onRowClick?: (row: any, queryKey?: any) => void
  hideSql?: boolean
  readOnly?: boolean
  runAutomatically?: boolean
  refetch?: number
  initialNaturalLanguageMode?: boolean
  hideChartOption?: boolean
}

export function SqlEditor({
  projectRef,
  initialSql,
  queryKey,
  onResults,
  onRowClick,
  label = 'Query your data',
  hideSql = false,
  readOnly = false,
  runAutomatically = false,
  refetch,
  initialNaturalLanguageMode = false,
  hideChartOption = false,
}: SqlEditorProps) {
  const [sql, setSql] = useState(initialSql || '')
  const [isSqlVisible, setIsSqlVisible] = useState(!hideSql)
  const [isNaturalLanguageMode, setIsNaturalLanguageMode] = useState(
    process.env.NEXT_PUBLIC_ENABLE_AI_QUERIES === 'true' && initialNaturalLanguageMode
  )
  const [naturalLanguageQuery, setNaturalLanguageQuery] = useState('')
  const { mutate: runQuery, data, isPending, error } = useRunQuery()
  const [isGeneratingSql, setIsGeneratingSql] = useState(false)
  const [aiError, setAiError] = useState<string | null>(null)
  const [isChartVisible, setIsChartVisible] = useState(false)
  const [xAxisColumn, setXAxisColumn] = useState<string | null>(null)
  const [yAxisColumn, setYAxisColumn] = useState<string | null>(null)

  const columns = useMemo(() => {
    if (!data || data.length === 0) return []
    return Object.keys(data[0])
  }, [data])

  useEffect(() => {
    if (initialSql) {
      setSql(initialSql)
    }
  }, [initialSql])

  const handleRunNaturalLanguageQuery = async () => {
    if (!naturalLanguageQuery) return

    setIsGeneratingSql(true)
    setAiError(null)
    try {
      const response = await fetch('/api/ai/sql', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          prompt: naturalLanguageQuery,
          projectRef,
        }),
      })

      if (!response.ok) {
        const errorData = await response.json()
        throw new Error(errorData.message || 'Failed to generate SQL')
      }

      const { sql: generatedSql } = await response.json()
      setSql(generatedSql)
      runQuery({ projectRef, query: generatedSql, readOnly: true })
    } catch (error: any) {
      console.error(error)
      setAiError(error.message)
    } finally {
      setIsGeneratingSql(false)
    }
  }

  const handleRunQuery = useCallback(() => {
    if (sql) {
      runQuery({ projectRef, query: sql, readOnly: true })
    }
  }, [sql, projectRef, runQuery])

  useEffect(() => {
    setIsSqlVisible(!hideSql)
  }, [hideSql])

  useEffect(() => {
    if (runAutomatically && initialSql) {
      runQuery({ projectRef, query: initialSql, readOnly: true })
    }
  }, [runAutomatically, initialSql, projectRef, runQuery])

  useEffect(() => {
    if (refetch && refetch > 0) {
      handleRunQuery()
    }
  }, [refetch, handleRunQuery])

  useEffect(() => {
    if (onResults) {
      onResults(data)
    }
  }, [data, onResults])

  useEffect(() => {
    const noResults = !data || (Array.isArray(data) && data.length === 0)
    if (noResults && !isSqlVisible && !isNaturalLanguageMode && !readOnly && !isPending) {
      setIsSqlVisible(true)
    }
  }, [data, isSqlVisible, isNaturalLanguageMode])

  const serverErrorMessage = (error as any)?.response?.data?.message || ''
  const isReadOnlyError =
    serverErrorMessage.includes('permission denied') || serverErrorMessage.includes('42501')
  const customReadOnlyError = "You can't directly alter your database schema, use chat instead"

  // Build the toggle-group selection based on current UI state
  const toggleValues = useMemo(() => {
    const values: string[] = []
    if (isNaturalLanguageMode) values.push('chat')
    if (isSqlVisible) values.push('sql')
    if (!hideChartOption && isChartVisible) values.push('chart')
    return values
  }, [isNaturalLanguageMode, isSqlVisible, isChartVisible, hideChartOption])

  const handleToggleGroupChange = (values: string[]) => {
    setIsNaturalLanguageMode(values.includes('chat'))
    setIsSqlVisible(values.includes('sql'))
    if (!hideChartOption) {
      setIsChartVisible(values.includes('chart'))
    }
  }

  return (
    <div>
      <div className="px-6 pt-4 lg:px-8 lg:pt-8">
        <div className="flex items-center gap-4 mb-4    ">
          <h2 className="font-semibold flex-1">{label}</h2>
          <ToggleGroup
            type="multiple"
            size="sm"
            value={toggleValues}
            onValueChange={handleToggleGroupChange}
            className="gap-1"
          >
            {process.env.NEXT_PUBLIC_ENABLE_AI_QUERIES === 'true' && (
              <ToggleGroupItem value="chat" aria-label="Chat">
                <Wand className="h-4 w-4" />
              </ToggleGroupItem>
            )}
            <ToggleGroupItem value="sql" aria-label="SQL">
              <FileText className="h-4 w-4" />
            </ToggleGroupItem>
            {!hideChartOption && (
              <Popover>
                <PopoverTrigger asChild>
                  <ToggleGroupItem value="chart" aria-label="Chart">
                    <BarChartIcon className="h-4 w-4" />
                  </ToggleGroupItem>
                </PopoverTrigger>
                <PopoverContent className="w-80">
                  <div className="grid gap-4">
                    <div className="grid gap-2">
                      <div className="flex items-center space-x-2">
                        <div className="flex-1">
                          <Label className="flex-1 mb-2 block">Show Chart</Label>
                          <p className="text-xs text-muted-foreground">
                            Visualize your data with a chart.
                          </p>
                        </div>
                        <Switch
                          id="show-chart"
                          size="sm"
                          className="mb-0"
                          checked={isChartVisible}
                          onCheckedChange={setIsChartVisible}
                        />
                      </div>
                      {isChartVisible && (
                        <div className="mt-2">
                          <div className="grid grid-cols-3 items-center gap-4 mb-2">
                            <Label htmlFor="x-axis">X-Axis</Label>
                            <Select
                              onValueChange={setXAxisColumn}
                              defaultValue={xAxisColumn || undefined}
                            >
                              <SelectTrigger className="col-span-2 h-8">
                                <SelectValue placeholder="Select column" />
                              </SelectTrigger>
                              <SelectContent>
                                {columns.map((col) => (
                                  <SelectItem key={col} value={col}>
                                    {col}
                                  </SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                          <div className="grid grid-cols-3 items-center gap-4 mb-2">
                            <Label htmlFor="y-axis">Y-Axis</Label>
                            <Select
                              onValueChange={setYAxisColumn}
                              defaultValue={yAxisColumn || undefined}
                            >
                              <SelectTrigger className="col-span-2 h-8">
                                <SelectValue placeholder="Select column" />
                              </SelectTrigger>
                              <SelectContent>
                                {columns.map((col) => (
                                  <SelectItem key={col} value={col}>
                                    {col}
                                  </SelectItem>
                                ))}
                              </SelectContent>
                            </Select>
                          </div>
                        </div>
                      )}
                    </div>
                  </div>
                </PopoverContent>
              </Popover>
            )}
          </ToggleGroup>
        </div>
        <div>
          {isNaturalLanguageMode && (
            <div className="relative mb-4">
              <Wand
                strokeWidth={1.5}
                size={16}
                className="absolute left-4 top-1/2 -translate-y-1/2"
              />
              <Input
                placeholder="e.g. Show me all users who signed up in the last 7 days"
                value={naturalLanguageQuery}
                onChange={(e) => setNaturalLanguageQuery(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter' && !e.shiftKey) {
                    e.preventDefault()
                    handleRunNaturalLanguageQuery()
                  }
                }}
                className="w-full px-10"
              />
              <Button
                onClick={handleRunNaturalLanguageQuery}
                disabled={isGeneratingSql || isPending}
                className="h-7 w-7 rounded-full p-0 shrink-0 absolute right-1 top-1/2 -translate-y-1/2"
              >
                {isGeneratingSql ? (
                  <Loader2 size={16} className="animate-spin" />
                ) : isPending ? (
                  <Loader2 size={16} className="animate-spin" />
                ) : (
                  <ArrowUp size={16} />
                )}
              </Button>
            </div>
          )}
          {isSqlVisible && (
            <div className="border-t border-b bg-muted overflow-hidden -mx-6 lg:-mx-8 mt-4 relative">
              <Editor
                height="200px"
                language="sql"
                value={sql}
                onChange={(value) => setSql(value || '')}
                theme="vs-dark"
                className="bg-transparent"
                options={{
                  minimap: { enabled: false },
                  fontSize: 14,
                  readOnly,
                  padding: {
                    top: 24,
                    bottom: 24,
                  },
                }}
              />
              <Button
                size="sm"
                onClick={handleRunQuery}
                disabled={isPending}
                className="absolute bottom-4 right-4"
              >
                {isPending ? 'Running...' : 'Run Query'}
              </Button>
            </div>
          )}
        </div>
      </div>
      {isPending && (
        <div className="space-y-2 p-4 px-6 lg:px-8">
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
        </div>
      )}
      {aiError && (
        <div className="px-6 lg:px-8 py-4">
          <Alert variant="destructive">
            <AlertTriangle className="h-4 w-4" />
            <AlertTitle>Error generating SQL</AlertTitle>
            <AlertDescription>{aiError}</AlertDescription>
          </Alert>
        </div>
      )}
      {error && (
        <div className="px-6 lg:px-8 py-4">
          <Alert variant="destructive">
            <AlertTriangle className="h-4 w-4" />
            <AlertTitle>Query Error</AlertTitle>
            <AlertDescription>
              {isReadOnlyError
                ? customReadOnlyError
                : serverErrorMessage || (error as Error)?.message || 'An unexpected error occurred'}
            </AlertDescription>
          </Alert>
        </div>
      )}

      {!hideChartOption && data && isChartVisible && xAxisColumn && yAxisColumn && (
        <div className="px-8 mt-8 mb-4">
          <QueryResultChart data={data} xAxis={xAxisColumn} yAxis={yAxisColumn} />
        </div>
      )}

      {data && (
        <div>
          <ResultsTable data={data} onRowClick={(row) => onRowClick?.(row, queryKey)} />
        </div>
      )}
    </div>
  )
}

function QueryResultChart({ data, xAxis, yAxis }: { data: any[]; xAxis: string; yAxis: string }) {
  const chartConfig = {
    [yAxis]: {
      label: yAxis,
      color: 'var(--chart-1)',
    },
  } satisfies ChartConfig

  return (
    <ChartContainer config={chartConfig} className="aspect-auto h-[250px] w-full">
      <BarChart
        accessibilityLayer
        data={data}
        margin={{
          left: -24,
          right: 12,
        }}
      >
        <CartesianGrid vertical={false} />
        <XAxis dataKey={xAxis} tickLine={false} axisLine={false} tickMargin={8} minTickGap={32} />
        <YAxis
          dataKey={yAxis}
          tickLine={false}
          axisLine={false}
          tickMargin={8}
          tickCount={5}
          allowDecimals={false}
        />
        <ChartTooltip content={<ChartTooltipContent className="w-[150px]" indicator="dot" />} />
        <Bar dataKey={yAxis} fill={`var(--color-${yAxis})`} />
      </BarChart>
    </ChartContainer>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/auth.tsx
'use client'

import { AlertTriangle, ChevronRight, Mail, Phone, User } from 'lucide-react'
import { useCallback, useMemo } from 'react'
import { z } from 'zod'

import {
  authEmailProviderSchema,
  authFieldLabels,
  authGeneralSettingsSchema,
  authGoogleProviderSchema,
  authPhoneProviderSchema,
  type AuthGeneralSettingsSchema,
} from '../../lib/schemas/auth'
import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import { Button } from '@/registry/default/components/ui/button'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import { DynamicForm } from '@/registry/default/platform/platform-kit-nextjs/components/dynamic-form'
import { useSheetNavigation } from '@/registry/default/platform/platform-kit-nextjs/contexts/SheetNavigationContext'
import {
  useGetAuthConfig,
  useUpdateAuthConfig,
} from '@/registry/default/platform/platform-kit-nextjs/hooks/use-auth'

function ProviderSettingsView({
  projectRef,
  schema,
  title,
  initialValues: allInitialValues,
  onSuccess,
}: {
  projectRef: string
  schema: z.ZodObject<any> | z.ZodEffects<z.ZodObject<any>>
  title: string
  initialValues: any
  onSuccess: () => void
}) {
  const { mutate: updateAuthConfig, isPending: isUpdatingConfig } = useUpdateAuthConfig()

  const actualSchema = 'shape' in schema ? schema : (schema._def.schema as z.ZodObject<any>)

  const handleUpdateAuthConfig = (formData: z.infer<typeof actualSchema>) => {
    const payload = Object.fromEntries(
      Object.entries(formData).filter(([_, value]) => value !== undefined)
    )

    if (Object.keys(payload).length === 0) {
      alert('No changes to submit. Please modify a field to update.')
      return
    }

    updateAuthConfig(
      {
        projectRef,
        payload,
      },
      {
        onSuccess,
      }
    )
  }

  const formInitialValues = useMemo(() => {
    if (!allInitialValues) {
      return undefined
    }
    const schemaKeys = Object.keys(actualSchema.shape)
    const result = schemaKeys.reduce(
      (acc, key) => {
        if (Object.prototype.hasOwnProperty.call(allInitialValues, key)) {
          acc[key] = allInitialValues[key]
        }
        return acc
      },
      {} as Record<string, any>
    )

    return result
  }, [allInitialValues, actualSchema])

  return (
    <div className="w-full max-w-3xl mx-auto p-6 pt-4 lg:p-12 lg:pt-12">
      <h2 className="lg:text-xl font-semibold mb-2 lg:mb-4">{title}</h2>
      <DynamicForm
        schema={actualSchema}
        onSubmit={handleUpdateAuthConfig}
        isLoading={isUpdatingConfig}
        initialValues={formInitialValues}
        labels={authFieldLabels}
      />
    </div>
  )
}

export function AuthManager({ projectRef }: { projectRef: string }) {
  const {
    data: authConfigData,
    isLoading: isLoadingConfig,
    error: errorLoadingConfig,
  } = useGetAuthConfig(projectRef)

  const { mutate: updateAuthConfig, isPending: isUpdatingConfig } = useUpdateAuthConfig()
  const { push, pop } = useSheetNavigation()

  const handleUpdateGeneralSettings = (formData: AuthGeneralSettingsSchema) => {
    const payload = Object.fromEntries(
      Object.entries(formData).filter(([_, value]) => value !== undefined)
    )

    if (Object.keys(payload).length === 0) {
      alert('No changes to submit. Please modify a field to update.')
      return
    }

    updateAuthConfig({
      projectRef,
      payload,
    })
  }

  const providers: {
    name: string
    icon: React.ReactNode
    description: string
    schema: z.ZodObject<any> | z.ZodEffects<z.ZodObject<any>>
  }[] = [
    {
      icon: <Mail className="h-4 w-4 text-muted-foreground" />,
      name: 'Email',
      description: 'Sign in with email and password',
      schema: authEmailProviderSchema,
    },
    {
      icon: <Phone className="h-4 w-4 text-muted-foreground" />,
      name: 'Phone',
      description: 'Sign in with phone number',
      schema: authPhoneProviderSchema,
    },
    {
      icon: <User className="h-4 w-4 text-muted-foreground" />,
      name: 'Google',
      description: 'Sign in with Google',
      schema: authGoogleProviderSchema,
    },
  ]

  const handleProviderClick = useCallback(
    (provider: { name: string; schema: z.ZodObject<any> | z.ZodEffects<z.ZodObject<any>> }) => {
      push({
        title: `${provider.name} Provider Settings`,
        component: (
          <ProviderSettingsView
            projectRef={projectRef}
            schema={provider.schema}
            title={`${provider.name} Provider Settings`}
            initialValues={authConfigData}
            onSuccess={() => pop()}
          />
        ),
      })
    },
    [projectRef, authConfigData, push, pop]
  )

  const formInitialValues = useMemo(() => {
    if (!authConfigData) {
      return undefined
    }
    const schemaKeys = Object.keys(authGeneralSettingsSchema.shape)
    return schemaKeys.reduce(
      (acc, key) => {
        if (Object.prototype.hasOwnProperty.call(authConfigData, key)) {
          acc[key] = authConfigData[key as keyof typeof authConfigData]
        }
        return acc
      },
      {} as Record<string, any>
    )
  }, [authConfigData])

  if (errorLoadingConfig) {
    return (
      <div className="mx-6 lg:mx-8 mt-8">
        <Alert variant="destructive">
          <AlertTriangle className="h-4 w-4" />
          <AlertTitle>Error loading auth settings</AlertTitle>
          <AlertDescription>
            There was a problem loading your authentication configuration.
          </AlertDescription>
        </Alert>
      </div>
    )
  }

  if (isLoadingConfig)
    return (
      <div className="w-full max-w-3xl mx-auto p-12 space-y-2">
        <Skeleton className="h-12 w-full mb-8" />
        <Skeleton className="h-12 w-full" />
        <Skeleton className="h-12 w-full" />
      </div>
    )

  return (
    <div className="w-full max-w-3xl mx-auto p-6 pt-4 lg:p-12 lg:pt-12">
      <div className="mb-12">
        <h2 className="text-base lg:text-xl font-semibold">General Settings</h2>
        <p className="text-sm lg:text-base text-muted-foreground mt-1 mb-2">
          Allow people to sign up to your app
        </p>
        <DynamicForm
          schema={authGeneralSettingsSchema}
          onSubmit={handleUpdateGeneralSettings}
          isLoading={isUpdatingConfig}
          initialValues={formInitialValues}
          labels={authFieldLabels}
        />
      </div>
      <div>
        <h2 className="font-semibold lg:text-lg">Sign in methods</h2>
        <p className="text-muted-foreground mb-6 mt-1 text-sm lg:text-base">
          Configure how people sign in and up to your app.
        </p>
        <div className="border rounded-md overflow-hidden bg-background">
          {providers.map((provider) => (
            <Button
              variant="ghost"
              size="lg"
              onClick={() => handleProviderClick(provider)}
              key={provider.name}
              className="rounded-none justify-start flex-row text-left h-auto px-8 py-4 w-full gap-4 border-b last:border-b-0"
            >
              {provider.icon}
              <div className="flex-1">
                <h3 className="font-bold">{provider.name}</h3>
                <p className="text-muted-foreground text-sm">{provider.description}</p>
              </div>
              <ChevronRight className="h-4 w-4 text-muted-foreground" />
            </Button>
          ))}
        </div>
      </div>
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/database.tsx
'use client'

import { AlertTriangle, Table, Wand } from 'lucide-react'
import { useCallback, useMemo, useState } from 'react'
import { toast } from 'sonner'
import { z, type ZodTypeAny } from 'zod'

import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import { Button } from '@/registry/default/components/ui/button'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import { DynamicForm } from '@/registry/default/platform/platform-kit-nextjs/components/dynamic-form'
import { SqlEditor } from '@/registry/default/platform/platform-kit-nextjs/components/sql-editor'
import { useSheetNavigation } from '@/registry/default/platform/platform-kit-nextjs/contexts/SheetNavigationContext'
import { useRunQuery } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-run-query'
import { useListTables } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-tables'

// Helper to generate a Zod schema from the table's column definitions
function generateZodSchema(table: any): z.ZodObject<any, any, any> {
  if (!table || !table.columns) {
    return z.object({})
  }

  const shape: Record<string, ZodTypeAny> = {}
  for (const column of table.columns) {
    // We can only edit updatable columns that are not generated
    if (!column.is_updatable || column.is_generated) continue

    let fieldSchema: ZodTypeAny
    const dataType = column.data_type.toLowerCase()

    if (dataType.includes('array')) {
      fieldSchema = z.array(z.any())
    } else if (dataType.includes('int') || dataType.includes('numeric')) {
      fieldSchema = z.number()
    } else if (dataType.includes('bool')) {
      fieldSchema = z.boolean()
    } else if (
      dataType === 'user-defined' &&
      column.enums &&
      Array.isArray(column.enums) &&
      column.enums.length > 0
    ) {
      // Handle enum types
      fieldSchema = z.enum(column.enums as [string, ...string[]])
    } else {
      // Default to string for text, varchar, timestamp, uuid, etc.
      fieldSchema = z.string()
    }

    if (column.is_nullable) {
      fieldSchema = fieldSchema.nullish()
    }

    shape[column.name] = fieldSchema
  }
  return z.object(shape)
}

const getPrimaryKeys = (table: any): string[] => {
  if (!table || !table.primary_keys) {
    return []
  }
  return table.primary_keys.map((pk: any) => pk.name)
}

function EditRowView({
  projectRef,
  table,
  row,
  onSuccess,
}: {
  projectRef: string
  table: any
  row: any
  onSuccess: () => void
}) {
  const { mutate: runUpdateQuery, isPending: isUpdatePending } = useRunQuery()
  const formSchema = useMemo(() => generateZodSchema(table), [table])

  const columnInfo = useMemo(() => {
    if (!table || !table.columns) return {}

    const info: Record<string, any> = {}
    for (const column of table.columns) {
      // Only include updatable columns that are not generated
      if (!column.is_updatable || column.is_generated) continue

      const dataType = column.data_type.toLowerCase()
      const displayType =
        dataType === 'user-defined' &&
        column.enums &&
        Array.isArray(column.enums) &&
        column.enums.length > 0
          ? 'enum'
          : dataType

      info[column.name] = {
        data_type: displayType,
        is_nullable: column.is_nullable,
      }
    }
    return info
  }, [table])

  const handleFormSubmit = useCallback(
    (formData: any) => {
      const pks = getPrimaryKeys(table)
      if (pks.length === 0) {
        toast.error('Cannot update row. This table does not have a primary key.')
        return
      }

      const setClauses = Object.entries(formData)
        .map(([key, value]) => {
          if (JSON.stringify(row[key]) === JSON.stringify(value)) return null

          // Find the column type to determine formatting
          const column = table.columns.find((col: any) => col.name === key)
          const dataType = column?.data_type?.toLowerCase() || ''
          const isNullable = column?.is_nullable || false

          let formattedValue

          // Handle empty/null values
          if (
            value === null ||
            value === undefined ||
            (typeof value === 'string' && value.trim() === '')
          ) {
            if (isNullable) {
              formattedValue = 'NULL'
            } else if (dataType.includes('array')) {
              // Non-nullable array, set to empty array
              const jsonObj = JSON.stringify({ [key]: [] })
              formattedValue = `(select ${key} from json_populate_record(null::public."${
                table.name
              }", '${jsonObj.replace(/'/g, "''")}'))`
            } else {
              // Non-nullable text field, set to empty string
              formattedValue = "''"
            }
          } else if (Array.isArray(value) && value.length === 0) {
            // Explicitly empty array (different from null)
            const jsonObj = JSON.stringify({ [key]: [] })
            formattedValue = `(select ${key} from json_populate_record(null::public."${
              table.name
            }", '${jsonObj.replace(/'/g, "''")}'))`
          } else if (dataType.includes('array')) {
            // Array type with actual values - use json_populate_record syntax
            const jsonObj = JSON.stringify({ [key]: value })
            formattedValue = `(select ${key} from json_populate_record(null::public."${
              table.name
            }", '${jsonObj.replace(/'/g, "''")}'))`
          } else if (dataType === 'user-defined' && column?.enums) {
            // Handle enum values - treat as strings with proper escaping
            formattedValue = `'${String(value).replace(/'/g, "''")}'`
          } else if (typeof value === 'string') {
            formattedValue = `'${value.replace(/'/g, "''")}'`
          } else {
            formattedValue = value
          }

          return `"${key}" = ${formattedValue}`
        })
        .filter(Boolean)
        .join(', ')

      if (!setClauses) {
        toast.error('No changes to save')
        onSuccess()
        return
      }

      const whereClauses = pks
        .map((pk) => {
          const value = row[pk]
          const formattedValue =
            typeof value === 'string' ? `'${value.replace(/'/g, "''")}'` : value
          return `"${pk}" = ${formattedValue}`
        })
        .join(' AND ')

      const updateSql = `UPDATE public."${table.name}" SET ${setClauses} WHERE ${whereClauses};`

      runUpdateQuery({ projectRef, query: updateSql, readOnly: false }, { onSuccess })
    },
    [projectRef, table, row, runUpdateQuery, onSuccess]
  )

  return (
    <div className="px-6 pt-4 pb-10 lg:px-12 lg:pt-10">
      <h2 className="text-base lg:text-xl font-semibold mb-4">Editing row in {table.name}</h2>
      <DynamicForm
        schema={formSchema}
        initialValues={row}
        onSubmit={handleFormSubmit}
        isLoading={isUpdatePending}
        columnInfo={columnInfo}
      />
    </div>
  )
}

function TableRecordsView({ projectRef, table }: { projectRef: string; table: any }) {
  const { push, pop } = useSheetNavigation()
  const [refetchCounter, setRefetchCounter] = useState(0)

  const handleRowClick = useCallback(
    (row: any) => {
      push({
        title: `Editing row`,
        component: (
          <EditRowView
            projectRef={projectRef}
            table={table}
            row={row}
            onSuccess={() => {
              pop()
              setRefetchCounter((c) => c + 1)
            }}
          />
        ),
      })
    },
    [push, pop, projectRef, table]
  )

  return (
    <SqlEditor
      hideChartOption={true}
      projectRef={projectRef}
      label={`View records in ${table.name}`}
      initialSql={`SELECT * FROM public."${table.name}" LIMIT 100;`}
      queryKey={table.id}
      onRowClick={handleRowClick}
      hideSql={true}
      runAutomatically={true}
      refetch={refetchCounter}
      readOnly
    />
  )
}

export function DatabaseManager({ projectRef }: { projectRef: string }) {
  const { push } = useSheetNavigation()
  const { data: tables, isLoading, isError } = useListTables(projectRef, ['public'])

  const handleTableClick = useCallback(
    (table: any) => {
      push({
        title: table.name,
        component: <TableRecordsView projectRef={projectRef} table={table} />,
      })
    },
    [push, projectRef]
  )

  const handleNaturalLanguageQueryClick = useCallback(() => {
    push({
      title: 'Talk to your database',
      component: (
        <SqlEditor projectRef={projectRef} initialNaturalLanguageMode={true} hideSql={true} />
      ),
    })
  }, [push, projectRef])

  return (
    <div className="p-6 pt-4 lg:p-8 lg:pt-8">
      <div className="flex items-center justify-between mb-6 gap-6">
        <div className="flex-1">
          <h1 className="text-base lg:text-xl font-semibold">Database</h1>
          <p className="hidden lg:block text-sm lg:text-base text-muted-foreground mt-1">
            View and manage the data stored in your app.
          </p>
        </div>
        <Button
          variant="outline"
          key="talk-to-db"
          className="flex-row justify-between"
          onClick={handleNaturalLanguageQueryClick}
        >
          <Wand strokeWidth={1.5} size={16} />
          Query your database
        </Button>
      </div>

      {isError && (
        <Alert variant="destructive" className="mt-8">
          <AlertTriangle className="h-4 w-4" />
          <AlertTitle>Error loading tables</AlertTitle>
          <AlertDescription>There was a problem loading your database tables.</AlertDescription>
        </Alert>
      )}

      {isLoading && (
        <div className="mt-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          <Skeleton className="h-12 w-full" />
          <Skeleton className="h-12 w-full" />
          <Skeleton className="h-12 w-full" />
          <Skeleton className="h-12 w-full" />
        </div>
      )}

      {tables && tables.length > 0 ? (
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
          {tables.map((table: any) => (
            <Button
              variant="outline"
              key={table.id}
              size="lg"
              className="flex-row justify-between text-left"
              onClick={() => handleTableClick(table)}
            >
              <Table className="h-4 w-4 text-muted-foreground" />
              <h2 className="text-sm font-medium font-mono truncate flex-1">{table.name}</h2>
              <div className="text-sm text-muted-foreground font-mono shrink-0">
                {table.live_rows_estimate} rows
              </div>
            </Button>
          ))}
        </div>
      ) : !isLoading && (!tables || tables.length === 0) ? (
        <Alert className="mt-8">
          <Table className="h-4 w-4" />
          <AlertTitle>No database tables</AlertTitle>
          <AlertDescription>Create tables to store and organize your data.</AlertDescription>
        </Alert>
      ) : null}
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/index.tsx
'use client'

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import {
  ChevronLeft,
  ChevronRight,
  Database,
  ExternalLink,
  HardDrive,
  KeyRound,
  Lightbulb,
  ScrollText,
  Shield,
  Users,
} from 'lucide-react'
import Link from 'next/link'
import { ReactNode, useMemo, useState } from 'react'

import { Button } from '@/registry/default/components/ui/button'
import {
  Dialog,
  DialogContent,
  DialogTitle,
  DialogTrigger,
} from '@/registry/default/components/ui/dialog'
import {
  Drawer,
  DrawerContent,
  DrawerHeader,
  DrawerTitle,
} from '@/registry/default/components/ui/drawer'
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from '@/registry/default/components/ui/hover-card'
import { LogoSupabase } from '@/registry/default/platform/platform-kit-nextjs/components/logo-supabase'
import { AuthManager } from '@/registry/default/platform/platform-kit-nextjs/components/supabase-manager/auth'
import { DatabaseManager } from '@/registry/default/platform/platform-kit-nextjs/components/supabase-manager/database'
import { LogsManager } from '@/registry/default/platform/platform-kit-nextjs/components/supabase-manager/logs'
import { SecretsManager } from '@/registry/default/platform/platform-kit-nextjs/components/supabase-manager/secrets'
import { StorageManager } from '@/registry/default/platform/platform-kit-nextjs/components/supabase-manager/storage'
import { SuggestionsManager } from '@/registry/default/platform/platform-kit-nextjs/components/supabase-manager/suggestions'
import { UsersManager } from '@/registry/default/platform/platform-kit-nextjs/components/supabase-manager/users'
import {
  SheetNavigationProvider,
  useSheetNavigation,
} from '@/registry/default/platform/platform-kit-nextjs/contexts/SheetNavigationContext'

const queryClient = new QueryClient()

function DialogView({ projectRef, isMobile }: { projectRef: string; isMobile?: boolean }) {
  const { stack, push, popTo, reset } = useSheetNavigation()

  const handleTopLevelNavigation = (title: string, component: ReactNode) => {
    if (stack.length === 1 && stack[0].title === title) {
      return
    }
    reset()
    push({ title, component })
  }

  const currentView = stack[stack.length - 1]
  const activeManager = stack.length > 0 ? stack[0].title : null

  const navigationItems = useMemo(
    () => [
      {
        title: 'Database',
        icon: Database,
        component: <DatabaseManager projectRef={projectRef} />,
      },
      {
        title: 'Storage',
        icon: HardDrive,
        component: <StorageManager projectRef={projectRef} />,
      },
      {
        title: 'Auth',
        icon: Shield,
        component: <AuthManager projectRef={projectRef} />,
      },
      {
        title: 'Users',
        icon: Users,
        component: <UsersManager projectRef={projectRef} />,
      },
      {
        title: 'Secrets',
        icon: KeyRound,
        component: <SecretsManager projectRef={projectRef} />,
      },
      {
        title: 'Logs',
        icon: ScrollText,
        component: <LogsManager projectRef={projectRef} />,
      },
      {
        title: 'Suggestions',
        icon: Lightbulb,
        component: <SuggestionsManager projectRef={projectRef} />,
      },
    ],
    [projectRef]
  )

  if (isMobile) {
    return (
      <div className="flex flex-col h-full overflow-hidden">
        {/* Content Area */}
        <div className="flex flex-col overflow-hidden h-full">
          <div className="grow overflow-y-auto">
            {currentView ? (
              currentView.component
            ) : (
              <div className="flex items-center justify-center h-full">
                <p className="text-muted-foreground">
                  Select a manager from the bottom navigation to get started.
                </p>
              </div>
            )}
          </div>
        </div>

        {/* Bottom Navigation Bar */}
        <div className="border-t bg-background">
          <div className="overflow-x-auto">
            <div className="flex gap-1 p-2 min-w-max">
              {navigationItems.map((item) => {
                const Icon = item.icon
                return (
                  <Button
                    key={item.title}
                    variant={activeManager === item.title ? 'secondary' : 'ghost'}
                    className="flex-col h-16 w-20 min-w-16 text-xs gap-1 px-2"
                    onClick={() => handleTopLevelNavigation(item.title, item.component)}
                  >
                    <Icon className="h-5 w-5" />
                    <span className="text-[10px] leading-tight text-center">
                      {item.title === 'Auth' ? 'Auth' : item.title}
                    </span>
                  </Button>
                )
              })}
            </div>
          </div>
        </div>
      </div>
    )
  }

  return (
    <div className="grid grid-cols-[240px_1fr] h-full overflow-hidden">
      {/* Sidebar */}
      <div className="flex flex-col border-r px-3 py-6 pb-3">
        <div className="px-4 mb-4">
          <h2 className="text-muted-foreground font-semibold text-sm">Manage your back-end</h2>
        </div>
        <div className="grow space-y-0.5">
          {navigationItems.map((item) => {
            const Icon = item.icon
            return (
              <Button
                key={item.title}
                variant={activeManager === item.title ? 'secondary' : 'ghost'}
                className="w-full justify-start text-sm"
                onClick={() => handleTopLevelNavigation(item.title, item.component)}
              >
                <Icon className="mr-2 text-muted-foreground" />
                {item.title === 'Auth' ? 'Authentication' : item.title}
              </Button>
            )
          })}
        </div>
        <footer className="p-0 text-sm text-muted-foreground flex items-center gap-3 -m-3 border-t">
          <HoverCard>
            <HoverCardTrigger asChild>
              <Link
                href={`https://supabase.com/dashboard/project/${projectRef}`}
                target="_blank"
                className="flex items-center px-4 w-full rounded-none text-sm py-4 h-auto justify-start gap-3 text-sm text-left hover:bg-accent"
              >
                <LogoSupabase size={16} />
                <span className="flex-1">Open in Supabase</span>
                <ExternalLink className="ml-2 h-4 w-4 text-muted-foreground/50" />
              </Link>
            </HoverCardTrigger>
            <HoverCardContent
              sideOffset={8}
              align="start"
              side="top"
              className="text-sm bg-muted/50 w-[216px]"
            >
              <h4 className="font-semibold mb-1">About Supabase</h4>
              <p className="text-muted-foreground">
                Access powerful back-end tools for database, auth, storage, and logs directly in
                Supabase.
              </p>
            </HoverCardContent>
          </HoverCard>
        </footer>
      </div>

      {/* Content Area */}
      <div className="flex flex-col overflow-hidden h-full">
        {/* Header with breadcrumbs */}
        <div className="flex items-center h-12 shrink-0 px-4 relative border-b">
          {stack.length > 1 && (
            <Button
              variant="outline"
              size="icon"
              className="h-8 w-8 relative z-10"
              onClick={() => popTo(stack.length - 2)}
            >
              <ChevronLeft className="h-4 w-4" />
              <span className="sr-only">Back</span>
            </Button>
          )}
          {/* Breadcrumbs */}
          <div className="ml-4 flex items-center gap-1.5 text-sm text-muted-foreground relative z-10">
            {stack.map((item: { title: string }, index: number) => (
              <div key={`${item.title}-${index}`} className="flex items-center gap-1.5">
                {index > 0 && <ChevronRight className="h-3 w-3" />}
                {index === stack.length - 1 ? (
                  <span className="font-semibold text-foreground">{item.title}</span>
                ) : (
                  <button
                    type="button"
                    tabIndex={0}
                    onClick={() => popTo(index)}
                    className="hover:underline"
                  >
                    {item.title}
                  </button>
                )}
              </div>
            ))}
          </div>
        </div>

        <div className="grow overflow-y-auto">
          {currentView ? (
            currentView.component
          ) : (
            <div className="flex items-center justify-center h-full">
              <p className="text-muted-foreground">
                Select a manager from the sidebar to get started.
              </p>
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

export default function SupabaseManagerDialog({
  projectRef,
  open,
  onOpenChange,
  isMobile,
}: {
  projectRef: string
  open: boolean
  onOpenChange: (open: boolean) => void
  isMobile: boolean
}) {
  const content = (
    <SheetNavigationProvider
      onStackEmpty={() => {}}
      initialStack={[
        {
          title: 'Database',
          component: <DatabaseManager projectRef={projectRef} />,
        },
      ]}
    >
      <DialogView projectRef={projectRef} isMobile={isMobile} />
    </SheetNavigationProvider>
  )

  if (!isMobile) {
    return (
      <QueryClientProvider client={queryClient}>
        <Dialog open={open} onOpenChange={onOpenChange}>
          <DialogContent className="w-full h-[80vh] max-h-[700px] sm:max-w-[calc(100%-2rem)] w-[1180px] p-0 overflow-hidden sm:rounded-lg">
            <DialogTitle className="sr-only">Manage your back-end</DialogTitle>
            {content}
          </DialogContent>
        </Dialog>
      </QueryClientProvider>
    )
  }

  return (
    <QueryClientProvider client={queryClient}>
      <Drawer open={open} onOpenChange={onOpenChange}>
        <DrawerContent className="h-[90vh] p-0 overflow-hidden">
          <DrawerHeader className="sr-only">
            <DrawerTitle>Manage your back-end</DrawerTitle>
          </DrawerHeader>
          {content}
        </DrawerContent>
      </Drawer>
    </QueryClientProvider>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/logs.tsx
'use client'

import { Check, ChevronsUpDown, Logs, Terminal } from 'lucide-react'
import { useMemo, useState } from 'react'

import { genDefaultQuery, LogsTableName } from '../../lib/logs'
import { cn } from '@/lib/utils'
import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import { Button } from '@/registry/default/components/ui/button'
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
} from '@/registry/default/components/ui/command'
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from '@/registry/default/components/ui/hover-card'
import { Popover, PopoverContent, PopoverTrigger } from '@/registry/default/components/ui/popover'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/registry/default/components/ui/table'
import { useGetLogs } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-logs'

// Define log types with names and descriptions
const logTypes = [
  {
    value: LogsTableName.FN_EDGE,
    label: 'Function Edge Logs',
    description: 'Edge function execution logs with request and response metadata',
  },
  {
    value: LogsTableName.AUTH,
    label: 'Authentication Logs',
    description: 'User authentication events and security logs',
  },
  {
    value: LogsTableName.POSTGRES,
    label: 'PostgreSQL Logs',
    description: 'Database queries, errors, and performance metrics',
  },
  {
    value: LogsTableName.REALTIME,
    label: 'Realtime Logs',
    description: 'WebSocket connections and realtime subscriptions',
  },
  {
    value: LogsTableName.STORAGE,
    label: 'Storage Logs',
    description: 'File uploads, downloads, and storage operations',
  },
  {
    value: LogsTableName.PG_CRON,
    label: 'Cron Job Logs',
    description: 'Scheduled job executions and cron task logs',
  },
  {
    value: LogsTableName.EDGE,
    label: 'Edge Logs',
    description: 'HTTP requests and responses from the data API',
  },

  {
    value: LogsTableName.FUNCTIONS,
    label: 'Function Logs',
    description: 'Serverless function execution logs and events',
  },
  {
    value: LogsTableName.POSTGREST,
    label: 'PostgREST Logs',
    description: 'API requests to your database through PostgREST',
  },
  {
    value: LogsTableName.SUPAVISOR,
    label: 'Supavisor Logs',
    description: 'Connection pooling and database proxy logs',
  },
  {
    value: LogsTableName.PGBOUNCER,
    label: 'PgBouncer Logs',
    description: 'Legacy connection pooling logs',
  },
  {
    value: LogsTableName.PG_UPGRADE,
    label: 'PostgreSQL Upgrade Logs',
    description: 'Database upgrade processes and migration logs',
  },
]

export function LogsManager({ projectRef }: { projectRef: string }) {
  const [activeTab, setActiveTab] = useState<LogsTableName>(LogsTableName.FN_EDGE)
  const [open, setOpen] = useState(false)

  const sql = useMemo(() => genDefaultQuery(activeTab), [activeTab])

  const { data: logs, isLoading, error } = useGetLogs(projectRef, { sql })

  const selectedLogType = logTypes.find((type) => type.value === activeTab)

  return (
    <>
      <div className="p-6 pt-4 lg:p-8 lg:pt-8 border-b bg-background z-10 sticky top-0 flex items-center justify-between mb-6">
        <div className="flex-1">
          <h1 className="text-base lg:text-xl font-semibold">Logs</h1>
          <p className="hidden lg:block text-sm lg:text-base text-muted-foreground mt-1">
            Debug errors and track activity in your app
          </p>
        </div>
        <Popover open={open} onOpenChange={setOpen}>
          <PopoverTrigger asChild>
            <Button
              variant="outline"
              role="combobox"
              aria-expanded={open}
              className="w-64 justify-between"
            >
              {selectedLogType ? selectedLogType.label : 'Select log type...'}
              <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
            </Button>
          </PopoverTrigger>
          <PopoverContent className="w-80 p-0" align="end">
            <Command>
              <CommandInput placeholder="Search log types..." className="h-9" />
              <CommandList
                className="max-h-60 overflow-y-auto"
                onWheel={(e) => {
                  e.stopPropagation()
                }}
                style={{ overscrollBehavior: 'contain' }}
              >
                <CommandEmpty>No log type found.</CommandEmpty>
                <CommandGroup>
                  {logTypes.map((logType) => (
                    <CommandItem
                      key={logType.value}
                      value={logType.value}
                      onSelect={(currentValue) => {
                        setActiveTab(currentValue as LogsTableName)
                        setOpen(false)
                      }}
                      className="flex items-center gap-2 p-3"
                    >
                      <div className="flex-1">
                        <div className="text-xs mb-1 font-medium leading-none">{logType.label}</div>
                        <div className="text-xs text-muted-foreground leading-snug">
                          {logType.description}
                        </div>
                      </div>
                      <Check
                        className={cn(
                          'h-4 w-4 mt-0.5',
                          activeTab === logType.value ? 'opacity-100' : 'opacity-0'
                        )}
                      />
                    </CommandItem>
                  ))}
                </CommandGroup>
              </CommandList>
            </Command>
          </PopoverContent>
        </Popover>
      </div>

      {isLoading && (
        <div className="space-y-2 mx-8 mt-8">
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
        </div>
      )}
      {(error || (logs && logs.error)) && (
        <div className="mx-6 lg:mx-8 mt-8">
          <Alert variant="destructive">
            <Terminal className="h-4 w-4" />
            <AlertTitle>Error fetching logs</AlertTitle>
            <AlertDescription>
              {(error as any)?.message ||
                (typeof logs?.error === 'object' && logs.error?.message) ||
                'An unexpected error occurred. Please try again.'}
            </AlertDescription>
          </Alert>
        </div>
      )}
      {logs && logs.result && logs.result.length > 0 && (
        <div className="mt-4 overflow-auto w-full">
          <Table>
            <TableHeader>
              <TableRow className="hover:bg-transparent">
                {Object.keys(logs.result[0] as object).map((key, idx, arr) => (
                  <TableHead
                    key={key}
                    className={
                      (idx === 0 ? 'first:pl-6 lg:first:pl-8 ' : '') +
                      (idx === arr.length - 1 ? 'last:pr-6 lg:last:pr-8 ' : '')
                    }
                  >
                    {key.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase())}
                  </TableHead>
                ))}
              </TableRow>
            </TableHeader>
            <TableBody>
              {(logs.result as any[]).map((log, index) => (
                <TableRow key={log.id || index} className="group hover:bg-muted/50 relative">
                  {Object.keys(logs.result?.[0] ?? []).map((key, idx, arr) => {
                    const value = log[key]
                    const formattedValue = (() => {
                      if (key === 'timestamp' && typeof value === 'number') {
                        return new Date(value / 1000).toLocaleString()
                      }
                      if (value === null) {
                        return 'NULL'
                      }
                      return typeof value === 'object'
                        ? JSON.stringify(value, null, 2)
                        : String(value)
                    })()

                    return (
                      <TableCell
                        key={key}
                        className="first:pl-6 lg:first:pl-8 last:pr-6 lg:last:pr-8 text-xs text-muted-foreground group-hover:text-foreground min-w-32"
                      >
                        <HoverCard>
                          <HoverCardTrigger asChild>
                            <div className="text-xs font-mono w-fit max-w-96 truncate cursor-default">
                              {formattedValue}
                            </div>
                          </HoverCardTrigger>
                          <HoverCardContent className="w-96 max-h-96 overflow-auto p-3">
                            <pre className="text-xs font-mono whitespace-pre-wrap wrap-break-word">
                              {formattedValue}
                            </pre>
                          </HoverCardContent>
                        </HoverCard>
                      </TableCell>
                    )
                  })}
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </div>
      )}
      {logs && logs.result && logs.result.length === 0 && (
        <div className="mt-8 mx-8">
          <Alert>
            <Logs className="h-4 w-4" />
            <AlertTitle>No logs found</AlertTitle>
            <AlertDescription>
              Logs will appear here when your application generates activity.
            </AlertDescription>
          </Alert>
        </div>
      )}
    </>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/secrets.tsx
'use client'

import { zodResolver } from '@hookform/resolvers/zod'
import { AlertTriangle, Key, Minus, PlusIcon } from 'lucide-react'
import { useFieldArray, useForm } from 'react-hook-form'
import { z } from 'zod'

import { secretsSchema } from '../../lib/schemas/secrets'
import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import { Button } from '@/registry/default/components/ui/button'
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from '@/registry/default/components/ui/form'
import { Input } from '@/registry/default/components/ui/input'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import {
  useCreateSecrets,
  useDeleteSecrets,
  useGetSecrets,
} from '@/registry/default/platform/platform-kit-nextjs/hooks/use-secrets'

export function SecretsManager({ projectRef }: { projectRef: string }) {
  const { data: secrets, isLoading, error } = useGetSecrets(projectRef)
  const { mutate: createSecrets, isPending: isCreating } = useCreateSecrets()
  const { mutate: deleteSecrets, isPending: isDeleting } = useDeleteSecrets()
  const form = useForm<z.infer<typeof secretsSchema>>({
    resolver: zodResolver(secretsSchema),
    defaultValues: {
      secrets: [{ name: '', value: '' }],
    },
  })

  const { fields, append, remove } = useFieldArray({
    control: form.control,
    name: 'secrets',
  })

  const handleCreateSecrets = (formData: z.infer<typeof secretsSchema>) => {
    createSecrets(
      {
        projectRef,
        secrets: formData.secrets,
      },
      {
        onSuccess: () => {
          form.reset({ secrets: [{ name: '', value: '' }] })
        },
      }
    )
  }

  const handleDeleteSecret = (secretName: string) => {
    if (window.confirm(`Are you sure you want to delete the secret "${secretName}"?`)) {
      deleteSecrets({
        projectRef,
        secretNames: [secretName],
      })
    }
  }

  if (isLoading) {
    return (
      <div className="w-full max-w-4xl mx-auto p-12 space-y-4">
        <Skeleton className="h-48 w-full" />
        <Skeleton className="h-16 w-full" />
        <Skeleton className="h-16 w-full" />
      </div>
    )
  }

  if (error) {
    return (
      <div className="mx-6 lg:mx-8 mt-8">
        <Alert variant="destructive">
          <AlertTriangle className="h-4 w-4" />
          <AlertTitle>Error loading secrets</AlertTitle>
          <AlertDescription>
            There was a problem loading your secrets configuration.
          </AlertDescription>
        </Alert>
      </div>
    )
  }

  return (
    <div className="w-full max-w-3xl mx-auto p-6 pt-4 lg:p-12 lg:pt-12">
      <div className="mb-16">
        <h2 className="text-base lg:text-xl font-semibold mb-1">Add New Secret</h2>
        <p className="text-muted-foreground mb-6 text-sm lg:text-base">
          Add sensitive information like API keys that your app needs to work securely.
        </p>
        <Form {...form}>
          <form onSubmit={form.handleSubmit(handleCreateSecrets)} className="space-y-2">
            {fields.map((field, index) => (
              <div key={field.id} className="flex items-start space-x-2">
                <FormField
                  control={form.control}
                  name={`secrets.${index}.name`}
                  render={({ field }) => (
                    <FormItem className="flex-1">
                      {index === 0 && <FormLabel className="mb-4">Name</FormLabel>}
                      <FormControl>
                        <Input placeholder="SECRET_NAME" {...field} />
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  )}
                />
                <FormField
                  control={form.control}
                  name={`secrets.${index}.value`}
                  render={({ field }) => (
                    <FormItem className="flex-1">
                      {index === 0 && <FormLabel className="mb-4">Value</FormLabel>}
                      <FormControl>
                        <Input type="password" placeholder="••••••••" {...field} />
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  )}
                />
                <div className="w-8">
                  {index === 0 && <FormLabel className="opacity-0 mb-4">Remove</FormLabel>}
                  <Button
                    type="button"
                    variant="outline"
                    size="icon"
                    onClick={() => remove(index)}
                    disabled={fields.length <= 1}
                  >
                    <Minus className="h-4 w-4" />
                  </Button>
                </div>
              </div>
            ))}
            <div className="flex items-center space-x-2">
              <Button
                type="button"
                variant="outline"
                size="sm"
                onClick={() => append({ name: '', value: '' })}
              >
                <PlusIcon className="mr-2 h-4 w-4" />
                Add Secret
              </Button>
            </div>

            <Button type="submit" disabled={isCreating} className="mt-6">
              {isCreating ? 'Creating...' : 'Create Secrets'}
            </Button>
          </form>
        </Form>
      </div>

      <div>
        <h2 className="font-semibold mb-4 lg:text-lg">Existing secrets</h2>
        {secrets && secrets.length > 0 ? (
          secrets.map((secret) => (
            <div
              key={secret.name}
              className="flex items-center justify-between py-4 border-b last:border-b-0"
            >
              <div>
                <p className="font-mono text-sm tracking-wider">{secret.name}</p>
                {secret.updated_at && (
                  <p className="text-xs text-muted-foreground mt-1">
                    Last updated: {new Date(secret.updated_at).toLocaleString()}
                  </p>
                )}
              </div>
              <Button
                variant="outline"
                size="sm"
                onClick={() => handleDeleteSecret(secret.name)}
                disabled={isDeleting}
              >
                Delete
              </Button>
            </div>
          ))
        ) : (
          <Alert>
            <Key className="h-4 w-4" />
            <AlertTitle>No secrets found</AlertTitle>
            <AlertDescription>No secrets found for this project.</AlertDescription>
          </Alert>
        )}
      </div>
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/storage.tsx
'use client'

import { AlertTriangle, Folder } from 'lucide-react'

import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import { Badge } from '@/registry/default/components/ui/badge'
import { Button } from '@/registry/default/components/ui/button'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/registry/default/components/ui/tooltip'
import { useGetBuckets } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-storage'

export function StorageManager({ projectRef }: { projectRef: string }) {
  const { data: buckets, isLoading, isError } = useGetBuckets(projectRef)

  return (
    <div className="p-6 pt-4 lg:p-8 lg:pt-8">
      <h1 className="text-base lg:text-xl font-semibold">Storage</h1>
      <p className="hidden lg:block text-sm lg:text-base text-muted-foreground mt-1">
        View and manage the files stored in your app.
      </p>

      {isLoading && (
        <div className="mt-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          <Skeleton className="h-12 w-full" />
          <Skeleton className="h-12 w-full" />
          <Skeleton className="h-12 w-full" />
          <Skeleton className="h-12 w-full" />
        </div>
      )}
      {isError && (
        <Alert variant="destructive" className="mt-8">
          <AlertTriangle className="h-4 w-4" />
          <AlertTitle>Error loading buckets</AlertTitle>
          <AlertDescription>There was a problem loading your storage buckets.</AlertDescription>
        </Alert>
      )}

      {buckets && buckets.length > 0 ? (
        <div className="mt-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          {buckets.map((bucket: any) => (
            <Tooltip key={bucket.id}>
              <TooltipTrigger asChild>
                <Button
                  key={bucket.id}
                  variant="outline"
                  className="flex-row justify-start text-left h-auto p-4 gap-4"
                >
                  <Folder className="h-4 w-4 text-muted-foreground" />
                  <div className="flex-1">
                    <h2 className="font-semibold mb-1">{bucket.name}</h2>

                    <p className="text-xs text-muted-foreground">
                      Updated {new Date(bucket.updated_at).toLocaleDateString()}
                    </p>
                  </div>
                  <Badge variant={bucket.public ? 'default' : 'secondary'}>
                    {bucket.public ? 'Public' : 'Private'}
                  </Badge>
                </Button>
              </TooltipTrigger>
              <TooltipContent>
                <p>Viewing files is coming soon</p>
              </TooltipContent>
            </Tooltip>
          ))}
        </div>
      ) : buckets && buckets.length === 0 ? (
        <Alert className="mt-8">
          <Folder className="h-4 w-4" />
          <AlertTitle>No storage buckets</AlertTitle>
          <AlertDescription>
            A bucket is a container used to store and protect files in your app.
          </AlertDescription>
        </Alert>
      ) : null}
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/suggestions.tsx
'use client'

import { Terminal } from 'lucide-react'
import { useMemo } from 'react'
import ReactMarkdown from 'react-markdown'

import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import { Badge } from '@/registry/default/components/ui/badge'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import { useGetSuggestions } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-suggestions'

export function SuggestionsManager({ projectRef }: { projectRef: string }) {
  const { data: suggestions, isLoading, error } = useGetSuggestions(projectRef)

  const sortedSuggestions = useMemo(() => {
    if (!suggestions) return []
    const levelOrder = { ERROR: 1, WARN: 2, INFO: 3 }
    return [...suggestions].sort((a: any, b: any) => {
      const levelA = levelOrder[a.level as keyof typeof levelOrder] || 99
      const levelB = levelOrder[b.level as keyof typeof levelOrder] || 99
      return levelA - levelB
    })
  }, [suggestions])

  const getBadgeVariant = (level: 'ERROR' | 'WARN' | 'INFO') => {
    switch (level) {
      case 'ERROR':
        return 'destructive'
      case 'WARN':
        return 'secondary'
      default:
        return 'outline'
    }
  }

  return (
    <div className="p-6 pt-4 lg:p-12 lg:pt-12 max-w-3xl mx-auto">
      <h2 className="text-base lg:text-xl font-semibold mb-1">Suggestions</h2>
      <p className="text-muted-foreground mb-4 lg:mb-8 text-sm lg:text-base">
        Improve your project&apos;s security and performance.
      </p>
      {isLoading && (
        <div className="space-y-2">
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
          <Skeleton className="h-6 w-full" />
        </div>
      )}
      {error && (
        <Alert variant="destructive" className="mt-8">
          <Terminal className="h-4 w-4" />
          <AlertTitle>Error fetching suggestions</AlertTitle>
          <AlertDescription>
            {(error as any)?.message || 'An unexpected error occurred. Please try again.'}
          </AlertDescription>
        </Alert>
      )}
      {suggestions && (
        <div>
          {sortedSuggestions.length > 0 ? (
            <div>
              {sortedSuggestions.map((suggestion: any) => (
                <div
                  key={suggestion.cache_key}
                  className="py-4 border-b last:border-b-0 group relative"
                >
                  <div className="flex-1">
                    <div className="flex justify-start items-start gap-4">
                      <h4 className="font-semibold text-sm">{suggestion.title}</h4>
                      <div className="flex items-center gap-1">
                        <Badge variant={getBadgeVariant(suggestion.level)} className="shrink-0">
                          {suggestion.level}
                        </Badge>
                        {suggestion.type && (
                          <Badge
                            variant={suggestion.type === 'security' ? 'destructive' : 'secondary'}
                            className="shrink-0"
                          >
                            {suggestion.type.charAt(0).toUpperCase() + suggestion.type.slice(1)}
                          </Badge>
                        )}
                      </div>
                    </div>
                    <div className="text-sm text-muted-foreground mt-2 prose prose-sm max-w-none">
                      <ReactMarkdown
                        components={{
                          code({ inline, children, ...props }: any) {
                            return inline ? (
                              <code className="bg-muted px-1 rounded-sm" {...props}>
                                {children}
                              </code>
                            ) : (
                              <pre className="bg-muted p-2 rounded-sm overflow-x-auto" {...props}>
                                <code>{children}</code>
                              </pre>
                            )
                          },
                        }}
                      >
                        {suggestion.detail}
                      </ReactMarkdown>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          ) : (
            <Alert>
              <Terminal className="h-4 w-4" />
              <AlertTitle>No suggestions found</AlertTitle>
              <AlertDescription>
                Your project looks good! No suggestions at this time.
              </AlertDescription>
            </Alert>
          )}
        </div>
      )}
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/components/supabase-manager/users.tsx
'use client'

import { useState } from 'react'

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/registry/default/components/ui/select'
import { SqlEditor } from '@/registry/default/platform/platform-kit-nextjs/components/sql-editor'
import { UsersGrowthChart } from '@/registry/default/platform/platform-kit-nextjs/components/users-growth-chart'

export function UsersManager({ projectRef }: { projectRef: string }) {
  const [timeRange, setTimeRange] = useState(90)
  const defaultSql = `SELECT * FROM auth.users ORDER BY created_at DESC LIMIT 100;`

  return (
    <div className="pb-8">
      <div className="flex items-center justify-between p-6 pt-4 lg:p-8 lg:pt-8">
        <div className="flex-1">
          <h1 className="text-base lg:text-xl font-semibold">Users</h1>
          <p className="hidden lg:block text-sm lg:text-base text-muted-foreground mt-1">
            View user signups over time
          </p>
        </div>
        <Select value={String(timeRange)} onValueChange={(value) => setTimeRange(Number(value))}>
          <SelectTrigger className="w-[160px] rounded-lg sm:ml-auto" aria-label="Select a value">
            <SelectValue placeholder="Select time range" />
          </SelectTrigger>
          <SelectContent className="rounded-xl">
            <SelectItem value="90" className="rounded-lg">
              Last 90 days
            </SelectItem>
            <SelectItem value="30" className="rounded-lg">
              Last 30 days
            </SelectItem>
            <SelectItem value="7" className="rounded-lg">
              Last 7 days
            </SelectItem>
          </SelectContent>
        </Select>
      </div>
      <div className="px-8">
        <UsersGrowthChart projectRef={projectRef} timeRange={timeRange} />
      </div>
      <SqlEditor
        hideChartOption
        projectRef={projectRef}
        initialSql={defaultSql}
        initialNaturalLanguageMode={true}
        label="Recent users"
        hideSql={true}
        readOnly={true}
        runAutomatically={true}
      />
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/components/users-growth-chart.tsx
'use client'

import { AlertTriangle } from 'lucide-react'
import * as React from 'react'
import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from 'recharts'

import { Alert, AlertDescription, AlertTitle } from '@/registry/default/components/ui/alert'
import {
  ChartConfig,
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from '@/registry/default/components/ui/chart'
import { Skeleton } from '@/registry/default/components/ui/skeleton'
import { useGetUserCountsByDay } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-user-counts'

const chartConfig = {
  users: {
    label: 'New Users',
    color: 'var(--chart-1)',
  },
} satisfies ChartConfig

export function UsersGrowthChart({
  projectRef,
  timeRange,
}: {
  projectRef: string
  timeRange: number
}) {
  const { data: chartData, isLoading, isError } = useGetUserCountsByDay(projectRef, timeRange)

  return (
    <div>
      {isLoading && <Skeleton className="h-[250px] w-full" />}
      {isError && (
        <Alert variant="destructive">
          <AlertTriangle className="h-4 w-4" />
          <AlertTitle>Error loading chart data</AlertTitle>
          <AlertDescription>There was a problem loading your chart data.</AlertDescription>
        </Alert>
      )}
      {chartData && !isLoading && (
        <ChartContainer config={chartConfig} className="aspect-auto h-[250px] w-full">
          <BarChart
            accessibilityLayer
            data={chartData}
            margin={{
              left: -24,
              right: 12,
            }}
          >
            <CartesianGrid vertical={false} />
            <XAxis
              dataKey="date"
              tickLine={false}
              axisLine={false}
              tickMargin={8}
              minTickGap={32}
              tickFormatter={(value) => {
                const date = new Date(value)
                return date.toLocaleDateString('en-US', {
                  month: 'short',
                  day: 'numeric',
                })
              }}
            />
            <YAxis
              tickLine={false}
              axisLine={false}
              tickMargin={8}
              tickCount={5}
              allowDecimals={false}
            />
            <ChartTooltip
              content={
                <ChartTooltipContent
                  className="w-[150px]"
                  labelFormatter={(value) => {
                    return new Date(value).toLocaleDateString('en-US', {
                      month: 'short',
                      day: 'numeric',
                      year: 'numeric',
                    })
                  }}
                  indicator="dot"
                />
              }
            />
            <Bar dataKey="users" fill="var(--color-users)" />
          </BarChart>
        </ChartContainer>
      )}
    </div>
  )
}
registry/default/platform/platform-kit-nextjs/hooks/use-auth.ts
'use client'

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { type AxiosError } from 'axios'
import { toast } from 'sonner'

import { client } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api'
import type { components } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api-schema'

const getAuthConfig = async (projectRef: string) => {
  const { data, error } = await client.GET('/v1/projects/{ref}/config/auth', {
    params: {
      path: { ref: projectRef },
    },
  })
  if (error) {
    throw error
  }

  return data
}

export const useGetAuthConfig = (projectRef: string) => {
  return useQuery({
    queryKey: ['auth-config', projectRef],
    queryFn: () => getAuthConfig(projectRef),
    enabled: !!projectRef,
    retry: false,
  })
}

// UPDATE Auth Config
const updateAuthConfig = async ({
  projectRef,
  payload,
}: {
  projectRef: string
  payload: components['schemas']['UpdateAuthConfigBody']
}) => {
  const { data, error } = await client.PATCH('/v1/projects/{ref}/config/auth', {
    params: {
      path: {
        ref: projectRef,
      },
    },
    body: payload,
  })
  if (error) {
    throw error
  }

  return data
}

export const useUpdateAuthConfig = () => {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: updateAuthConfig,
    onSuccess: (data, variables) => {
      toast.success(`Auth config updated.`)
      queryClient.invalidateQueries({
        queryKey: ['auth-config', variables.projectRef],
      })
    },
    onError: (error: AxiosError<{ message: string }>) => {
      toast.error(error.response?.data?.message || 'There was a problem with your request.')
    },
  })
}
registry/default/platform/platform-kit-nextjs/hooks/use-logs.ts
'use client'

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

import { client } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api'

// GET Logs
const getLogs = async ({
  projectRef,
  iso_timestamp_start,
  iso_timestamp_end,
  sql,
}: {
  projectRef: string
  iso_timestamp_start?: string
  iso_timestamp_end?: string
  sql?: string
}) => {
  const { data, error } = await client.GET('/v1/projects/{ref}/analytics/endpoints/logs.all', {
    params: {
      path: {
        ref: projectRef,
      },
      query: {
        iso_timestamp_start,
        iso_timestamp_end,
        sql,
      },
    },
  })
  if (error) {
    throw error
  }

  return data
}

export const useGetLogs = (
  projectRef: string,
  params: {
    iso_timestamp_start?: string
    iso_timestamp_end?: string
    sql?: string
  } = {}
) => {
  const queryKey = ['logs', projectRef, params.sql]

  return useQuery({
    queryKey: queryKey,
    queryFn: () => {
      const now = new Date()
      const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000)

      const queryParams = {
        sql: params.sql,
        iso_timestamp_start: params.iso_timestamp_start ?? oneHourAgo.toISOString(),
        iso_timestamp_end: params.iso_timestamp_end ?? now.toISOString(),
      }
      return getLogs({ projectRef, ...queryParams })
    },
    enabled: !!projectRef,
    retry: false,
  })
}
registry/default/platform/platform-kit-nextjs/hooks/use-run-query.ts
'use client'

import { useMutation } from '@tanstack/react-query'
import { type AxiosError } from 'axios'
import { toast } from 'sonner'

import { client } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api'

// RUN SQL Query
export const runQuery = async ({
  projectRef,
  query,
  readOnly,
}: {
  projectRef: string
  query: string
  readOnly?: boolean
}) => {
  const { data, error } = await client.POST('/v1/projects/{ref}/database/query', {
    params: {
      path: {
        ref: projectRef,
      },
    },
    body: {
      query,
      read_only: readOnly,
    },
  })

  if (error) {
    throw error
  }

  return data as any
}

export const useRunQuery = () => {
  return useMutation({
    mutationFn: runQuery,
    onError: (error: AxiosError<{ message: string }>) => {
      toast.error(error.response?.data?.message || 'There was a problem with your query.')
    },
  })
}
registry/default/platform/platform-kit-nextjs/hooks/use-secrets.ts
'use client'

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { type AxiosError } from 'axios'
import { toast } from 'sonner'

import { client } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api'
import type { components } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api-schema'

// GET Secrets
const getSecrets = async (projectRef: string) => {
  const { data, error } = await client.GET('/v1/projects/{ref}/secrets', {
    params: {
      path: {
        ref: projectRef,
      },
    },
  })
  if (error) {
    throw error
  }

  return data
}

export const useGetSecrets = (projectRef: string) => {
  return useQuery({
    queryKey: ['secrets', projectRef],
    queryFn: () => getSecrets(projectRef),
    enabled: !!projectRef,
    retry: false,
  })
}

// CREATE Secrets
const createSecrets = async ({
  projectRef,
  secrets,
}: {
  projectRef: string
  secrets: components['schemas']['CreateSecretBody']
}) => {
  const { data, error } = await client.POST('/v1/projects/{ref}/secrets', {
    params: {
      path: {
        ref: projectRef,
      },
    },
    body: secrets,
  })
  if (error) {
    throw error
  }

  return data
}

export const useCreateSecrets = () => {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: createSecrets,
    onSuccess: (data, variables) => {
      toast.success(`Secrets created successfully.`)
      queryClient.refetchQueries({
        queryKey: ['secrets', variables.projectRef],
      })
    },
    onError: (error: AxiosError<{ message: string }>) => {
      toast.error(error.response?.data?.message || 'There was a problem with your request.')
    },
  })
}

// DELETE Secrets
const deleteSecrets = async ({
  projectRef,
  secretNames,
}: {
  projectRef: string
  secretNames: string[]
}) => {
  const { data, error } = await client.DELETE('/v1/projects/{ref}/secrets', {
    params: {
      path: {
        ref: projectRef,
      },
    },
    body: secretNames,
  })
  if (error) {
    throw error
  }

  return data
}

export const useDeleteSecrets = () => {
  const queryClient = useQueryClient()
  return useMutation({
    mutationFn: deleteSecrets,
    onSuccess: (data, variables) => {
      toast.success(`Secrets deleted successfully.`)
      queryClient.invalidateQueries({
        queryKey: ['secrets', variables.projectRef],
      })
    },
    onError: (error: AxiosError<{ message: string }>) => {
      toast.error(error.response?.data?.message || 'There was a problem with your request.')
    },
  })
}
registry/default/platform/platform-kit-nextjs/hooks/use-storage.ts
'use client'

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

import { client } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api'

// GET Buckets
const getBuckets = async (projectRef: string) => {
  const { data, error } = await client.GET('/v1/projects/{ref}/storage/buckets', {
    params: {
      path: {
        ref: projectRef,
      },
    },
  })
  if (error) {
    throw error
  }

  return data
}

export const useGetBuckets = (projectRef: string) => {
  return useQuery({
    queryKey: ['buckets', projectRef],
    queryFn: () => getBuckets(projectRef),
    enabled: !!projectRef,
    retry: false,
  })
}

// LIST Objects
const listObjects = async ({ projectRef, bucketId }: { projectRef: string; bucketId: string }) => {
  const { data, error } = await client.POST(
    // TODO
    // @ts-expect-error this endpoint is not yet implemented
    '/v1/projects/{ref}/storage/buckets/{bucketId}/objects/list',
    {
      params: {
        path: {
          ref: projectRef,
          bucketId,
        },
      },
      body: {
        path: '',
        options: { limit: 100, offset: 0 },
      },
    }
  )
  if (error) {
    throw error
  }

  return data as any
}

export const useListObjects = (projectRef: string, bucketId: string) => {
  return useQuery({
    queryKey: ['objects', projectRef, bucketId],
    queryFn: () => listObjects({ projectRef, bucketId }),
    enabled: !!projectRef && !!bucketId,
  })
}
registry/default/platform/platform-kit-nextjs/hooks/use-suggestions.ts
'use client'

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

import { client } from '@/registry/default/platform/platform-kit-nextjs/lib/management-api'

// GET Suggestions
const getSuggestions = async (projectRef: string) => {
  const [
    { data: performanceData, error: performanceError },
    { data: securityData, error: securityError },
  ] = await Promise.all([
    client.GET('/v1/projects/{ref}/advisors/performance', {
      params: {
        path: {
          ref: projectRef,
        },
      },
    }),
    client.GET('/v1/projects/{ref}/advisors/security', {
      params: {
        path: {
          ref: projectRef,
        },
      },
    }),
  ])
  if (performanceError) {
    throw performanceError
  }
  if (securityError) {
    throw securityError
  }

  // Add type to each suggestion
  const performanceLints = (performanceData?.lints || []).map((lint) => ({
    ...lint,
    type: 'performance' as const,
  }))
  const securityLints = (securityData?.lints || []).map((lint) => ({
    ...lint,
    type: 'security' as const,
  }))
  return [...performanceLints, ...securityLints]
}

export const useGetSuggestions = (projectRef: string) => {
  return useQuery({
    queryKey: ['suggestions', projectRef],
    queryFn: () => getSuggestions(projectRef),
    enabled: !!projectRef,
    retry: false,
  })
}
registry/default/platform/platform-kit-nextjs/hooks/use-tables.ts
'use client'

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

import { runQuery } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-run-query'
import { listTablesSql } from '@/registry/default/platform/platform-kit-nextjs/lib/pg-meta'

// LIST Tables
const listTables = ({ projectRef, schemas }: { projectRef: string; schemas?: string[] }) => {
  const sql = listTablesSql(schemas)
  return runQuery({
    projectRef,
    query: sql,
    readOnly: true,
  })
}

export const useListTables = (projectRef: string, schemas?: string[]) => {
  return useQuery({
    queryKey: ['tables', projectRef, schemas],
    queryFn: () => listTables({ projectRef, schemas }),
    enabled: !!projectRef,
  })
}
registry/default/platform/platform-kit-nextjs/hooks/use-user-counts.ts
'use client'

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

import { runQuery } from '@/registry/default/platform/platform-kit-nextjs/hooks/use-run-query'

// GET User Counts by day
const getUserCountsByDay = ({ projectRef, days }: { projectRef: string; days: number }) => {
  const sql = `
    WITH days_series AS (
      SELECT generate_series(
        date_trunc('day', now() - interval '${Number(days) - 1} days'),
        date_trunc('day', now()),
        '1 day'::interval
      )::date AS date
    )
    SELECT
      d.date,
      COALESCE(u.users, 0)::int as users
    FROM
      days_series d
    LEFT JOIN (
      SELECT
        date_trunc('day', created_at AT TIME ZONE 'UTC')::date as date,
        count(id) as users
      FROM
        auth.users
      GROUP BY 1
    ) u ON d.date = u.date
    ORDER BY
      d.date ASC;
  `

  return runQuery({
    projectRef,
    query: sql,
    readOnly: true,
  })
}

export const useGetUserCountsByDay = (projectRef: string, days: number) => {
  return useQuery({
    queryKey: ['user-counts', projectRef, days],
    queryFn: () => getUserCountsByDay({ projectRef, days }),
    enabled: !!projectRef,
    retry: false,
  })
}
registry/default/platform/platform-kit-nextjs/lib/logs.ts
export enum LogsTableName {
  FN_EDGE = 'function_edge_logs',
  AUTH = 'auth_logs',
  POSTGRES = 'postgres_logs',
  REALTIME = 'realtime_logs',
  STORAGE = 'storage_logs',
  PG_CRON = 'pg_cron_logs',
  EDGE = 'edge_logs',
  FUNCTIONS = 'function_logs',
  POSTGREST = 'postgrest_logs',
  SUPAVISOR = 'supavisor_logs',
  PGBOUNCER = 'pgbouncer_logs',
  PG_UPGRADE = 'pg_upgrade_logs',
}

const genCrossJoinUnnests = (table: LogsTableName) => {
  switch (table) {
    case LogsTableName.EDGE:
      return `cross join unnest(metadata) as m
  cross join unnest(m.request) as request
  cross join unnest(m.response) as response`
    case LogsTableName.POSTGRES:
      return `cross join unnest(metadata) as m
  cross join unnest(m.parsed) as parsed`
    case LogsTableName.FUNCTIONS:
      return `cross join unnest(metadata) as metadata`
    case LogsTableName.AUTH:
      return `cross join unnest(metadata) as metadata`
    case LogsTableName.FN_EDGE:
      return `cross join unnest(metadata) as m
  cross join unnest(m.response) as response
  cross join unnest(m.request) as request`
    case LogsTableName.SUPAVISOR:
      return `cross join unnest(metadata) as m`
    default:
      return ''
  }
}

export const genDefaultQuery = (table: LogsTableName, limit: number = 100) => {
  const joins = genCrossJoinUnnests(table)
  const orderBy = 'order by timestamp desc'

  switch (table) {
    case LogsTableName.EDGE:
      return `select id, ${table}.timestamp, event_message, request.method, request.path, request.search, response.status_code
from ${table}
${joins}
${orderBy}
limit ${limit}`

    case LogsTableName.POSTGRES:
      return `select ${table}.timestamp, id, event_message, parsed.error_severity, parsed.detail, parsed.hint
from ${table}
${joins}
${orderBy}
limit ${limit}`

    case LogsTableName.FUNCTIONS:
      return `select id, ${table}.timestamp, event_message, metadata.event_type, metadata.function_id, metadata.level
from ${table}
${joins}
${orderBy}
limit ${limit}`

    case LogsTableName.AUTH:
      return `select id, ${table}.timestamp, event_message, metadata.level, metadata.status, metadata.path, metadata.msg as msg, metadata.error
from ${table}
${joins}
${orderBy}
limit ${limit}`

    case LogsTableName.FN_EDGE:
      return `select id, ${table}.timestamp, event_message, response.status_code, request.method, m.function_id, m.execution_time_ms, m.deployment_id, m.version
from ${table}
${joins}
${orderBy}
limit ${limit}`

    case LogsTableName.SUPAVISOR:
      return `select id, ${table}.timestamp, event_message
from ${table}
${joins}
${orderBy}
limit ${limit}`

    case LogsTableName.PG_UPGRADE:
      return `select id, ${table}.timestamp, event_message
from ${table}
${joins}
${orderBy}
limit ${limit}`

    case LogsTableName.PG_CRON:
      return `select postgres_logs.timestamp, id, event_message, parsed.error_severity, parsed.query
from postgres_logs
cross join unnest(metadata) as m
cross join unnest(m.parsed) as parsed
where (parsed.application_name = 'pg_cron' OR event_message LIKE '%cron job%')
${orderBy}
limit ${limit}`

    default:
      return `select id, ${table}.timestamp, event_message
from ${table}
${joins}
${orderBy}
limit ${limit}`
  }
}
registry/default/platform/platform-kit-nextjs/lib/management-api-schema.d.ts
/**
 * This file was auto-generated by openapi-typescript.
 * Do not make direct changes to the file.
 */

export interface paths {
  '/v1/branches/{branch_id_or_ref}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Get database branch config
     * @description Fetches configurations of the specified database branch
     */
    get: operations['v1-get-a-branch-config']
    put?: never
    post?: never
    /**
     * Delete a database branch
     * @description Deletes the specified database branch
     */
    delete: operations['v1-delete-a-branch']
    options?: never
    head?: never
    /**
     * Update database branch config
     * @description Updates the configuration of the specified database branch
     */
    patch: operations['v1-update-a-branch-config']
    trace?: never
  }
  '/v1/branches/{branch_id_or_ref}/push': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /**
     * Pushes a database branch
     * @description Pushes the specified database branch
     */
    post: operations['v1-push-a-branch']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/branches/{branch_id_or_ref}/merge': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /**
     * Merges a database branch
     * @description Merges the specified database branch
     */
    post: operations['v1-merge-a-branch']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/branches/{branch_id_or_ref}/reset': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /**
     * Resets a database branch
     * @description Resets the specified database branch
     */
    post: operations['v1-reset-a-branch']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/branches/{branch_id_or_ref}/diff': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * [Beta] Diffs a database branch
     * @description Diffs the specified database branch
     */
    get: operations['v1-diff-a-branch']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * List all projects
     * @description Returns a list of all projects you've previously created.
     */
    get: operations['v1-list-all-projects']
    put?: never
    /** Create a project */
    post: operations['v1-create-a-project']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/organizations': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * List all organizations
     * @description Returns a list of organizations that you currently belong to.
     */
    get: operations['v1-list-all-organizations']
    put?: never
    /** Create an organization */
    post: operations['v1-create-an-organization']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/oauth/authorize': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Authorize user through oauth */
    get: operations['v1-authorize-user']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/oauth/token': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Exchange auth code for user's access and refresh token */
    post: operations['v1-exchange-oauth-token']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/oauth/revoke': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Revoke oauth app authorization and it's corresponding tokens */
    post: operations['v1-revoke-token']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/oauth/authorize/project-claim': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Authorize user through oauth and claim a project
     * @description Initiates the OAuth authorization flow for the specified provider. After successful authentication, the user can claim ownership of the specified project.
     */
    get: operations['v1-oauth-authorize-project-claim']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/snippets': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Lists SQL snippets for the logged in user */
    get: operations['v1-list-all-snippets']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/snippets/{id}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets a specific SQL snippet */
    get: operations['v1-get-a-snippet']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/api-keys': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Get project api keys */
    get: operations['v1-get-project-api-keys']
    put?: never
    /** Creates a new API key for the project */
    post: operations['v1-create-project-api-key']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/api-keys/legacy': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Check whether JWT based legacy (anon, service_role) API keys are enabled. This API endpoint will be removed in the future, check for HTTP 404 Not Found. */
    get: operations['v1-get-project-legacy-api-keys']
    /** Disable or re-enable JWT based legacy (anon, service_role) API keys. This API endpoint will be removed in the future, check for HTTP 404 Not Found. */
    put: operations['v1-update-project-legacy-api-keys']
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/api-keys/{id}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Get API key */
    get: operations['v1-get-project-api-key']
    put?: never
    post?: never
    /** Deletes an API key for the project */
    delete: operations['v1-delete-project-api-key']
    options?: never
    head?: never
    /** Updates an API key for the project */
    patch: operations['v1-update-project-api-key']
    trace?: never
  }
  '/v1/projects/{ref}/branches': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * List all database branches
     * @description Returns all database branches of the specified project.
     */
    get: operations['v1-list-all-branches']
    put?: never
    /**
     * Create a database branch
     * @description Creates a database branch from the specified project.
     */
    post: operations['v1-create-a-branch']
    /**
     * Disables preview branching
     * @description Disables preview branching for the specified project
     */
    delete: operations['v1-disable-preview-branching']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/custom-hostname': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Gets project's custom hostname config */
    get: operations['v1-get-hostname-config']
    put?: never
    post?: never
    /** [Beta] Deletes a project's custom hostname configuration */
    delete: operations['v1-Delete hostname config']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/custom-hostname/initialize': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Updates project's custom hostname configuration */
    post: operations['v1-update-hostname-config']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/custom-hostname/reverify': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Attempts to verify the DNS configuration for project's custom hostname configuration */
    post: operations['v1-verify-dns-config']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/custom-hostname/activate': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Activates a custom hostname for a project. */
    post: operations['v1-activate-custom-hostname']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/network-bans/retrieve': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Gets project's network bans */
    post: operations['v1-list-all-network-bans']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/network-bans/retrieve/enriched': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Gets project's network bans with additional information about which databases they affect */
    post: operations['v1-list-all-network-bans-enriched']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/network-bans': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    post?: never
    /** [Beta] Remove network bans. */
    delete: operations['v1-delete-network-bans']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/network-restrictions': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Gets project's network restrictions */
    get: operations['v1-get-network-restrictions']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/network-restrictions/apply': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Updates project's network restrictions */
    post: operations['v1-update-network-restrictions']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/pgsodium': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Gets project's pgsodium config */
    get: operations['v1-get-pgsodium-config']
    /** [Beta] Updates project's pgsodium config. Updating the root_key can cause all data encrypted with the older key to become inaccessible. */
    put: operations['v1-update-pgsodium-config']
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/postgrest': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's postgrest config */
    get: operations['v1-get-postgrest-service-config']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    /** Updates project's postgrest config */
    patch: operations['v1-update-postgrest-service-config']
    trace?: never
  }
  '/v1/projects/{ref}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets a specific project that belongs to the authenticated user */
    get: operations['v1-get-project']
    put?: never
    post?: never
    /** Deletes the given project */
    delete: operations['v1-delete-a-project']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/secrets': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * List all secrets
     * @description Returns all secrets you've previously added to the specified project.
     */
    get: operations['v1-list-all-secrets']
    put?: never
    /**
     * Bulk create secrets
     * @description Creates multiple secrets and adds them to the specified project.
     */
    post: operations['v1-bulk-create-secrets']
    /**
     * Bulk delete secrets
     * @description Deletes all secrets with the given names from the specified project
     */
    delete: operations['v1-bulk-delete-secrets']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/ssl-enforcement': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Get project's SSL enforcement configuration. */
    get: operations['v1-get-ssl-enforcement-config']
    /** [Beta] Update project's SSL enforcement configuration. */
    put: operations['v1-update-ssl-enforcement-config']
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/types/typescript': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Generate TypeScript types
     * @description Returns the TypeScript types of your schema for use with supabase-js.
     */
    get: operations['v1-generate-typescript-types']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/vanity-subdomain': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Gets current vanity subdomain config */
    get: operations['v1-get-vanity-subdomain-config']
    put?: never
    post?: never
    /** [Beta] Deletes a project's vanity subdomain configuration */
    delete: operations['v1-deactivate-vanity-subdomain-config']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/vanity-subdomain/check-availability': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Checks vanity subdomain availability */
    post: operations['v1-check-vanity-subdomain-availability']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/vanity-subdomain/activate': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Activates a vanity subdomain for a project. */
    post: operations['v1-activate-vanity-subdomain-config']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/upgrade': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Upgrades the project's Postgres version */
    post: operations['v1-upgrade-postgres-version']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/upgrade/eligibility': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Returns the project's eligibility for upgrades */
    get: operations['v1-get-postgres-upgrade-eligibility']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/upgrade/status': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Beta] Gets the latest status of the project's upgrade */
    get: operations['v1-get-postgres-upgrade-status']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/readonly': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Returns project's readonly mode status */
    get: operations['v1-get-readonly-mode-status']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/readonly/temporary-disable': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** Disables project's readonly mode for the next 15 minutes */
    post: operations['v1-disable-readonly-mode-temporarily']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/read-replicas/setup': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Set up a read replica */
    post: operations['v1-setup-a-read-replica']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/read-replicas/remove': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Remove a read replica */
    post: operations['v1-remove-a-read-replica']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/health': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's service health status */
    get: operations['v1-get-services-health']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/config/auth/signing-keys': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Alpha] List all signing keys for the project */
    get: operations['v1-get-project-signing-keys']
    put?: never
    /** [Alpha] Create a new signing key for the project in standby status */
    post: operations['v1-create-project-signing-key']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/config/auth/signing-keys/{id}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** [Alpha] Get information about a signing key */
    get: operations['v1-get-project-signing-key']
    put?: never
    post?: never
    /** [Alpha] Remove a signing key from a project, where the status is previously_used */
    delete: operations['v1-remove-project-signing-key']
    options?: never
    head?: never
    /** [Alpha] Update a signing key, mainly its status */
    patch: operations['v1-update-project-signing-key']
    trace?: never
  }
  '/v1/projects/{ref}/config/storage': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's storage config */
    get: operations['v1-get-storage-config']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    /** Updates project's storage config */
    patch: operations['v1-update-storage-config']
    trace?: never
  }
  '/v1/projects/{ref}/config/database/postgres': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's Postgres config */
    get: operations['v1-get-postgres-config']
    /** Updates project's Postgres config */
    put: operations['v1-update-postgres-config']
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/config/database/pgbouncer': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Get project's pgbouncer config */
    get: operations['v1-get-project-pgbouncer-config']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/config/database/pooler': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's supavisor config */
    get: operations['v1-get-pooler-config']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    /** Updates project's supavisor config */
    patch: operations['v1-update-pooler-config']
    trace?: never
  }
  '/v1/projects/{ref}/config/auth': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's auth config */
    get: operations['v1-get-auth-service-config']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    /** Updates a project's auth config */
    patch: operations['v1-update-auth-service-config']
    trace?: never
  }
  '/v1/projects/{ref}/config/auth/third-party-auth': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Lists all third-party auth integrations */
    get: operations['v1-list-project-tpa-integrations']
    put?: never
    /** Creates a new third-party auth integration */
    post: operations['v1-create-project-tpa-integration']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/config/auth/third-party-auth/{tpa_id}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Get a third-party integration */
    get: operations['v1-get-project-tpa-integration']
    put?: never
    post?: never
    /** Removes a third-party auth integration */
    delete: operations['v1-delete-project-tpa-integration']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/pause': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** Pauses the given project */
    post: operations['v1-pause-a-project']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/restore': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Lists available restore versions for the given project */
    get: operations['v1-list-available-restore-versions']
    put?: never
    /** Restores the given project */
    post: operations['v1-restore-a-project']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/restore/cancel': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** Cancels the given project restoration */
    post: operations['v1-cancel-a-project-restoration']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/billing/addons': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Lists project addons */
    get: operations['v1-list-project-addons']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    /** Applies project addon */
    patch: operations['v1-apply-project-addon']
    trace?: never
  }
  '/v1/projects/{ref}/billing/addons/{addon_variant}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    post?: never
    /** Removes project addon */
    delete: operations['v1-remove-project-addon']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/claim-token': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project claim token */
    get: operations['v1-get-project-claim-token']
    put?: never
    /** Creates project claim token */
    post: operations['v1-create-project-claim-token']
    /** Revokes project claim token */
    delete: operations['v1-delete-project-claim-token']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/advisors/performance': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Gets project performance advisors.
     * @deprecated
     * @description This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.
     */
    get: operations['v1-get-performance-advisors']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/advisors/security': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Gets project security advisors.
     * @deprecated
     * @description This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.
     */
    get: operations['v1-get-security-advisors']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/analytics/endpoints/logs.all': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Gets project's logs
     * @description Executes a SQL query on the project's logs.
     *
     *     Either the 'iso_timestamp_start' and 'iso_timestamp_end' parameters must be provided.
     *     If both are not provided, only the last 1 minute of logs will be queried.
     *     The timestamp range must be no more than 24 hours and is rounded to the nearest minute. If the range is more than 24 hours, a validation error will be thrown.
     *
     */
    get: operations['v1-get-project-logs']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/analytics/endpoints/usage.api-counts': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's usage api counts */
    get: operations['v1-get-project-usage-api-count']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/analytics/endpoints/usage.api-requests-count': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project's usage api requests count */
    get: operations['v1-get-project-usage-request-count']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/migrations': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * [Beta] List applied migration versions
     * @description Only available to selected partner OAuth apps
     */
    get: operations['v1-list-migration-history']
    /**
     * [Beta] Upsert a database migration without applying
     * @description Only available to selected partner OAuth apps
     */
    put: operations['v1-upsert-a-migration']
    /**
     * [Beta] Apply a database migration
     * @description Only available to selected partner OAuth apps
     */
    post: operations['v1-apply-a-migration']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/query': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Run sql query */
    post: operations['v1-run-a-query']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/webhooks/enable': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** [Beta] Enables Database Webhooks on the project */
    post: operations['v1-enable-database-webhook']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/context': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Gets database metadata for the given project.
     * @deprecated
     * @description This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.
     */
    get: operations['v1-get-database-metadata']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/functions': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * List all functions
     * @description Returns all functions you've previously added to the specified project.
     */
    get: operations['v1-list-all-functions']
    /**
     * Bulk update functions
     * @description Bulk update functions. It will create a new function or replace existing. The operation is idempotent. NOTE: You will need to manually bump the version.
     */
    put: operations['v1-bulk-update-functions']
    /**
     * Create a function
     * @deprecated
     * @description This endpoint is deprecated - use the deploy endpoint. Creates a function and adds it to the specified project.
     */
    post: operations['v1-create-a-function']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/functions/deploy': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /**
     * Deploy a function
     * @description A new endpoint to deploy functions. It will create if function does not exist.
     */
    post: operations['v1-deploy-a-function']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/functions/{function_slug}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Retrieve a function
     * @description Retrieves a function with the specified slug and project.
     */
    get: operations['v1-get-a-function']
    put?: never
    post?: never
    /**
     * Delete a function
     * @description Deletes a function with the specified slug from the specified project.
     */
    delete: operations['v1-delete-a-function']
    options?: never
    head?: never
    /**
     * Update a function
     * @description Updates a function with the specified slug and project.
     */
    patch: operations['v1-update-a-function']
    trace?: never
  }
  '/v1/projects/{ref}/functions/{function_slug}/body': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /**
     * Retrieve a function body
     * @description Retrieves a function body for the specified slug and project.
     */
    get: operations['v1-get-a-function-body']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/storage/buckets': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Lists all buckets */
    get: operations['v1-list-all-buckets']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/config/auth/sso/providers': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Lists all SSO providers */
    get: operations['v1-list-all-sso-provider']
    put?: never
    /** Creates a new SSO provider */
    post: operations['v1-create-a-sso-provider']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/config/auth/sso/providers/{provider_id}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets a SSO provider by its UUID */
    get: operations['v1-get-a-sso-provider']
    /** Updates a SSO provider by its UUID */
    put: operations['v1-update-a-sso-provider']
    post?: never
    /** Removes a SSO provider by its UUID */
    delete: operations['v1-delete-a-sso-provider']
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/backups': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Lists all backups */
    get: operations['v1-list-all-backups']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/backups/restore-pitr': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** Restores a PITR backup for a database */
    post: operations['v1-restore-pitr-backup']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/backups/restore-point': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Get restore points for project */
    get: operations['v1-get-restore-point']
    put?: never
    /** Initiates a creation of a restore point for a database */
    post: operations['v1-create-restore-point']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/projects/{ref}/database/backups/undo': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    get?: never
    put?: never
    /** Initiates an undo to a given restore point */
    post: operations['v1-undo']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/organizations/{slug}/members': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** List members of an organization */
    get: operations['v1-list-organization-members']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/organizations/{slug}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets information about the organization */
    get: operations['v1-get-an-organization']
    put?: never
    post?: never
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
  '/v1/organizations/{slug}/project-claim/{token}': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    /** Gets project details for the specified organization and claim token */
    get: operations['v1-get-organization-project-claim']
    put?: never
    /** Claims project for the specified organization */
    post: operations['v1-claim-project-for-organization']
    delete?: never
    options?: never
    head?: never
    patch?: never
    trace?: never
  }
}
export type webhooks = Record<string, never>
export interface components {
  schemas: {
    BranchDetailResponse: {
      ref: string
      postgres_version: string
      postgres_engine: string
      release_channel: string
      /** @enum {string} */
      status:
        | 'INACTIVE'
        | 'ACTIVE_HEALTHY'
        | 'ACTIVE_UNHEALTHY'
        | 'COMING_UP'
        | 'UNKNOWN'
        | 'GOING_DOWN'
        | 'INIT_FAILED'
        | 'REMOVED'
        | 'RESTORING'
        | 'UPGRADING'
        | 'PAUSING'
        | 'RESTORE_FAILED'
        | 'RESTARTING'
        | 'PAUSE_FAILED'
        | 'RESIZING'
      db_host: string
      db_port: number
      db_user?: string
      db_pass?: string
      jwt_secret?: string
    }
    UpdateBranchBody: {
      branch_name?: string
      git_branch?: string
      /**
       * @deprecated
       * @description This field is deprecated and will be ignored. Use v1-reset-a-branch endpoint directly instead.
       */
      reset_on_push?: boolean
      persistent?: boolean
      /** @enum {string} */
      status?:
        | 'CREATING_PROJECT'
        | 'RUNNING_MIGRATIONS'
        | 'MIGRATIONS_PASSED'
        | 'MIGRATIONS_FAILED'
        | 'FUNCTIONS_DEPLOYED'
        | 'FUNCTIONS_FAILED'
    }
    BranchResponse: {
      id: string
      name: string
      project_ref: string
      parent_project_ref: string
      is_default: boolean
      git_branch?: string
      /** Format: int32 */
      pr_number?: number
      /**
       * @deprecated
       * @description This field is deprecated and will not be populated.
       */
      latest_check_run_id?: number
      persistent: boolean
      /** @enum {string} */
      status:
        | 'CREATING_PROJECT'
        | 'RUNNING_MIGRATIONS'
        | 'MIGRATIONS_PASSED'
        | 'MIGRATIONS_FAILED'
        | 'FUNCTIONS_DEPLOYED'
        | 'FUNCTIONS_FAILED'
      created_at: string
      updated_at: string
    }
    BranchDeleteResponse: {
      /** @enum {string} */
      message: 'ok'
    }
    BranchActionBody: {
      migration_version?: string
    }
    BranchUpdateResponse: {
      workflow_run_id: string
      /** @enum {string} */
      message: 'ok'
    }
    V1ProjectWithDatabaseResponse: {
      /** @description Id of your project */
      id: string
      /** @description Slug of your organization */
      organization_id: string
      /** @description Name of your project */
      name: string
      /**
       * @description Region of your project
       * @example us-east-1
       */
      region: string
      /**
       * @description Creation timestamp
       * @example 2023-03-29T16:32:59Z
       */
      created_at: string
      /** @enum {string} */
      status:
        | 'INACTIVE'
        | 'ACTIVE_HEALTHY'
        | 'ACTIVE_UNHEALTHY'
        | 'COMING_UP'
        | 'UNKNOWN'
        | 'GOING_DOWN'
        | 'INIT_FAILED'
        | 'REMOVED'
        | 'RESTORING'
        | 'UPGRADING'
        | 'PAUSING'
        | 'RESTORE_FAILED'
        | 'RESTARTING'
        | 'PAUSE_FAILED'
        | 'RESIZING'
      database: {
        /** @description Database host */
        host: string
        /** @description Database version */
        version: string
        /** @description Database engine */
        postgres_engine: string
        /** @description Release channel */
        release_channel: string
      }
    }
    V1CreateProjectBody: {
      /** @description Database password */
      db_pass: string
      /** @description Name of your project */
      name: string
      /** @description Slug of your organization */
      organization_id: string
      /**
       * @deprecated
       * @description Subscription Plan is now set on organization level and is ignored in this request
       * @enum {string}
       */
      plan?: 'free' | 'pro'
      /**
       * @description Region you want your server to reside in
       * @enum {string}
       */
      region:
        | 'us-east-1'
        | 'us-east-2'
        | 'us-west-1'
        | 'us-west-2'
        | 'ap-east-1'
        | 'ap-southeast-1'
        | 'ap-northeast-1'
        | 'ap-northeast-2'
        | 'ap-southeast-2'
        | 'eu-west-1'
        | 'eu-west-2'
        | 'eu-west-3'
        | 'eu-north-1'
        | 'eu-central-1'
        | 'eu-central-2'
        | 'ca-central-1'
        | 'ap-south-1'
        | 'sa-east-1'
      /**
       * @deprecated
       * @description This field is deprecated and is ignored in this request
       */
      kps_enabled?: boolean
      /** @enum {string} */
      desired_instance_size?:
        | 'pico'
        | 'nano'
        | 'micro'
        | 'small'
        | 'medium'
        | 'large'
        | 'xlarge'
        | '2xlarge'
        | '4xlarge'
        | '8xlarge'
        | '12xlarge'
        | '16xlarge'
        | '24xlarge'
        | '24xlarge_optimized_memory'
        | '24xlarge_optimized_cpu'
        | '24xlarge_high_memory'
        | '48xlarge'
        | '48xlarge_optimized_memory'
        | '48xlarge_optimized_cpu'
        | '48xlarge_high_memory'
      /**
       * Format: uri
       * @description Template URL used to create the project from the CLI.
       * @example https://github.com/supabase/supabase/tree/master/examples/slack-clone/nextjs-slack-clone
       */
      template_url?: string
    }
    V1ProjectResponse: {
      /** @description Id of your project */
      id: string
      /** @description Slug of your organization */
      organization_id: string
      /** @description Name of your project */
      name: string
      /**
       * @description Region of your project
       * @example us-east-1
       */
      region: string
      /**
       * @description Creation timestamp
       * @example 2023-03-29T16:32:59Z
       */
      created_at: string
      /** @enum {string} */
      status:
        | 'INACTIVE'
        | 'ACTIVE_HEALTHY'
        | 'ACTIVE_UNHEALTHY'
        | 'COMING_UP'
        | 'UNKNOWN'
        | 'GOING_DOWN'
        | 'INIT_FAILED'
        | 'REMOVED'
        | 'RESTORING'
        | 'UPGRADING'
        | 'PAUSING'
        | 'RESTORE_FAILED'
        | 'RESTARTING'
        | 'PAUSE_FAILED'
        | 'RESIZING'
    }
    OrganizationResponseV1: {
      id: string
      name: string
    }
    CreateOrganizationV1: {
      name: string
    }
    OAuthTokenBody: {
      /** @enum {string} */
      grant_type?: 'authorization_code' | 'refresh_token'
      /** Format: uuid */
      client_id?: string
      client_secret?: string
      code?: string
      code_verifier?: string
      redirect_uri?: string
      refresh_token?: string
    }
    OAuthTokenResponse: {
      access_token: string
      refresh_token: string
      expires_in: number
      /** @enum {string} */
      token_type: 'Bearer'
    }
    OAuthRevokeTokenBody: {
      /** Format: uuid */
      client_id: string
      client_secret: string
      refresh_token: string
    }
    SnippetList: {
      data: {
        id: string
        inserted_at: string
        updated_at: string
        /** @enum {string} */
        type: 'sql'
        /** @enum {string} */
        visibility: 'user' | 'project' | 'org' | 'public'
        name: string
        description: string | null
        project: {
          id: number
          name: string
        }
        owner: {
          id: number
          username: string
        }
        updated_by: {
          id: number
          username: string
        }
      }[]
      cursor?: string
    }
    SnippetResponse: {
      id: string
      inserted_at: string
      updated_at: string
      /** @enum {string} */
      type: 'sql'
      /** @enum {string} */
      visibility: 'user' | 'project' | 'org' | 'public'
      name: string
      description: string | null
      project: {
        id: number
        name: string
      }
      owner: {
        id: number
        username: string
      }
      updated_by: {
        id: number
        username: string
      }
      content: {
        favorite: boolean
        schema_version: string
        sql: string
      }
    }
    ApiKeyResponse: {
      api_key: string
      id?: string | null
      /** @enum {string|null} */
      type?: 'publishable' | 'secret' | 'legacy' | null
      prefix?: string | null
      name: string
      description?: string | null
      hash?: string | null
      secret_jwt_template?: {
        role: string
      } | null
      /** Format: date-time */
      inserted_at?: string | null
      /** Format: date-time */
      updated_at?: string | null
    }
    LegacyApiKeysResponse: {
      enabled: boolean
    }
    CreateApiKeyBody: {
      /** @enum {string} */
      type: 'publishable' | 'secret'
      name: string
      description?: string | null
      secret_jwt_template?: {
        role: string
      } | null
    }
    UpdateApiKeyBody: {
      name?: string
      description?: string | null
      secret_jwt_template?: {
        role: string
      } | null
    }
    CreateBranchBody: {
      branch_name: string
      git_branch?: string
      persistent?: boolean
      region?: string
      /** @enum {string} */
      desired_instance_size?:
        | 'pico'
        | 'nano'
        | 'micro'
        | 'small'
        | 'medium'
        | 'large'
        | 'xlarge'
        | '2xlarge'
        | '4xlarge'
        | '8xlarge'
        | '12xlarge'
        | '16xlarge'
        | '24xlarge'
        | '24xlarge_optimized_memory'
        | '24xlarge_optimized_cpu'
        | '24xlarge_high_memory'
        | '48xlarge'
        | '48xlarge_optimized_memory'
        | '48xlarge_optimized_cpu'
        | '48xlarge_high_memory'
      /**
       * @description Release channel. If not provided, GA will be used.
       * @enum {string}
       */
      release_channel?: 'internal' | 'alpha' | 'beta' | 'ga' | 'withdrawn' | 'preview'
      /**
       * @description Postgres engine version. If not provided, the latest version will be used.
       * @enum {string}
       */
      postgres_engine?: '15' | '17' | '17-oriole'
      secrets?: {
        [key: string]: string
      }
    }
    UpdateCustomHostnameResponse: {
      /** @enum {string} */
      status:
        | '1_not_started'
        | '2_initiated'
        | '3_challenge_verified'
        | '4_origin_setup_completed'
        | '5_services_reconfigured'
      custom_hostname: string
      data: {
        success: boolean
        errors: unknown[]
        messages: unknown[]
        result: {
          id: string
          hostname: string
          ssl: {
            status: string
            validation_records: {
              txt_name: string
              txt_value: string
            }[]
            validation_errors?: {
              message: string
            }[]
          }
          ownership_verification: {
            type: string
            name: string
            value: string
          }
          custom_origin_server: string
          verification_errors?: string[]
          status: string
        }
      }
    }
    UpdateCustomHostnameBody: {
      custom_hostname: string
    }
    NetworkBanResponse: {
      banned_ipv4_addresses: string[]
    }
    NetworkBanResponseEnriched: {
      banned_ipv4_addresses: {
        banned_address: string
        identifier: string
        type: string
      }[]
    }
    RemoveNetworkBanRequest: {
      ipv4_addresses: string[]
      identifier?: string
    }
    NetworkRestrictionsResponse: {
      /** @enum {string} */
      entitlement: 'disallowed' | 'allowed'
      /** @description At any given point in time, this is the config that the user has requested be applied to their project. The `status` field indicates if it has been applied to the project, or is pending. When an updated config is received, the applied config is moved to `old_config`. */
      config: {
        dbAllowedCidrs?: string[]
        dbAllowedCidrsV6?: string[]
      }
      /** @description Populated when a new config has been received, but not registered as successfully applied to a project. */
      old_config?: {
        dbAllowedCidrs?: string[]
        dbAllowedCidrsV6?: string[]
      }
      /** @enum {string} */
      status: 'stored' | 'applied'
    }
    NetworkRestrictionsRequest: {
      dbAllowedCidrs?: string[]
      dbAllowedCidrsV6?: string[]
    }
    PgsodiumConfigResponse: {
      root_key: string
    }
    UpdatePgsodiumConfigBody: {
      root_key: string
    }
    PostgrestConfigWithJWTSecretResponse: {
      db_schema: string
      max_rows: number
      db_extra_search_path: string
      /** @description If `null`, the value is automatically configured based on compute size. */
      db_pool: number | null
      jwt_secret?: string
    }
    V1UpdatePostgrestConfigBody: {
      db_extra_search_path?: string
      db_schema?: string
      max_rows?: number
      db_pool?: number
    }
    V1PostgrestConfigResponse: {
      db_schema: string
      max_rows: number
      db_extra_search_path: string
      /** @description If `null`, the value is automatically configured based on compute size. */
      db_pool: number | null
    }
    V1ProjectRefResponse: {
      id: number
      ref: string
      name: string
    }
    SecretResponse: {
      name: string
      value: string
      updated_at?: string
    }
    CreateSecretBody: {
      /**
       * @description Secret name must not start with the SUPABASE_ prefix.
       * @example string
       */
      name: string
      value: string
    }[]
    SslEnforcementResponse: {
      currentConfig: {
        database: boolean
      }
      appliedSuccessfully: boolean
    }
    SslEnforcementRequest: {
      requestedConfig: {
        database: boolean
      }
    }
    TypescriptResponse: {
      types: string
    }
    VanitySubdomainConfigResponse: {
      /** @enum {string} */
      status: 'not-used' | 'custom-domain-used' | 'active'
      custom_domain?: string
    }
    VanitySubdomainBody: {
      vanity_subdomain: string
    }
    SubdomainAvailabilityResponse: {
      available: boolean
    }
    ActivateVanitySubdomainResponse: {
      custom_domain: string
    }
    UpgradeDatabaseBody: {
      target_version: string
      /** @enum {string} */
      release_channel?: 'internal' | 'alpha' | 'beta' | 'ga' | 'withdrawn' | 'preview'
    }
    ProjectUpgradeInitiateResponse: {
      tracking_id: string
    }
    ProjectUpgradeEligibilityResponse: {
      eligible: boolean
      current_app_version: string
      /** @enum {string} */
      current_app_version_release_channel:
        | 'internal'
        | 'alpha'
        | 'beta'
        | 'ga'
        | 'withdrawn'
        | 'preview'
      latest_app_version: string
      target_upgrade_versions: {
        /** @enum {string} */
        postgres_version: '15' | '17' | '17-oriole'
        /** @enum {string} */
        release_channel: 'internal' | 'alpha' | 'beta' | 'ga' | 'withdrawn' | 'preview'
        app_version: string
      }[]
      potential_breaking_changes: string[]
      duration_estimate_hours: number
      legacy_auth_custom_roles: string[]
      extension_dependent_objects: string[]
    }
    DatabaseUpgradeStatusResponse: {
      databaseUpgradeStatus: {
        initiated_at: string
        latest_status_at: string
        target_version: number
        /** @enum {string} */
        error?:
          | '1_upgraded_instance_launch_failed'
          | '2_volume_detachchment_from_upgraded_instance_failed'
          | '3_volume_attachment_to_original_instance_failed'
          | '4_data_upgrade_initiation_failed'
          | '5_data_upgrade_completion_failed'
          | '6_volume_detachchment_from_original_instance_failed'
          | '7_volume_attachment_to_upgraded_instance_failed'
          | '8_upgrade_completion_failed'
          | '9_post_physical_backup_failed'
        /** @enum {string} */
        progress?:
          | '0_requested'
          | '1_started'
          | '2_launched_upgraded_instance'
          | '3_detached_volume_from_upgraded_instance'
          | '4_attached_volume_to_original_instance'
          | '5_initiated_data_upgrade'
          | '6_completed_data_upgrade'
          | '7_detached_volume_from_original_instance'
          | '8_attached_volume_to_upgraded_instance'
          | '9_completed_upgrade'
          | '10_completed_post_physical_backup'
        status: number
      } | null
    }
    ReadOnlyStatusResponse: {
      enabled: boolean
      override_enabled: boolean
      override_active_until: string
    }
    SetUpReadReplicaBody: {
      /**
       * @description Region you want your read replica to reside in
       * @example us-east-1
       * @enum {string}
       */
      read_replica_region:
        | 'us-east-1'
        | 'us-east-2'
        | 'us-west-1'
        | 'us-west-2'
        | 'ap-east-1'
        | 'ap-southeast-1'
        | 'ap-northeast-1'
        | 'ap-northeast-2'
        | 'ap-southeast-2'
        | 'eu-west-1'
        | 'eu-west-2'
        | 'eu-west-3'
        | 'eu-north-1'
        | 'eu-central-1'
        | 'eu-central-2'
        | 'ca-central-1'
        | 'ap-south-1'
        | 'sa-east-1'
    }
    RemoveReadReplicaBody: {
      database_identifier: string
    }
    V1ServiceHealthResponse: {
      /** @enum {string} */
      name: 'auth' | 'db' | 'pooler' | 'realtime' | 'rest' | 'storage'
      healthy: boolean
      /** @enum {string} */
      status: 'COMING_UP' | 'ACTIVE_HEALTHY' | 'UNHEALTHY'
      info?:
        | {
            /** @enum {string} */
            name: 'GoTrue'
            version: string
            description: string
          }
        | {
            healthy: boolean
            db_connected: boolean
            connected_cluster: number
          }
      error?: string
    }
    CreateSigningKeyBody: {
      /** @enum {string} */
      algorithm: 'EdDSA' | 'ES256' | 'RS256' | 'HS256'
      /** @enum {string} */
      status?: 'in_use' | 'standby'
    }
    SigningKeyResponse: {
      /** Format: uuid */
      id: string
      /** @enum {string} */
      algorithm: 'EdDSA' | 'ES256' | 'RS256' | 'HS256'
      /** @enum {string} */
      status: 'in_use' | 'previously_used' | 'revoked' | 'standby'
      public_jwk?: unknown
      /** Format: date-time */
      created_at: string
      /** Format: date-time */
      updated_at: string
    }
    SigningKeysResponse: {
      keys: {
        /** Format: uuid */
        id: string
        /** @enum {string} */
        algorithm: 'EdDSA' | 'ES256' | 'RS256' | 'HS256'
        /** @enum {string} */
        status: 'in_use' | 'previously_used' | 'revoked' | 'standby'
        public_jwk?: unknown
        /** Format: date-time */
        created_at: string
        /** Format: date-time */
        updated_at: string
      }[]
    }
    UpdateSigningKeyBody: {
      /** @enum {string} */
      status: 'in_use' | 'previously_used' | 'revoked' | 'standby'
    }
    StorageConfigResponse: {
      /** Format: int64 */
      fileSizeLimit: number
      features: {
        imageTransformation: {
          enabled: boolean
        }
        s3Protocol: {
          enabled: boolean
        }
      }
    }
    UpdateStorageConfigBody: {
      /** Format: int64 */
      fileSizeLimit?: number
      features?: {
        imageTransformation: {
          enabled: boolean
        }
        s3Protocol: {
          enabled: boolean
        }
      }
    }
    PostgresConfigResponse: {
      effective_cache_size?: string
      logical_decoding_work_mem?: string
      maintenance_work_mem?: string
      track_activity_query_size?: string
      max_connections?: number
      max_locks_per_transaction?: number
      max_parallel_maintenance_workers?: number
      max_parallel_workers?: number
      max_parallel_workers_per_gather?: number
      max_replication_slots?: number
      max_slot_wal_keep_size?: string
      max_standby_archive_delay?: string
      max_standby_streaming_delay?: string
      max_wal_size?: string
      max_wal_senders?: number
      max_worker_processes?: number
      /** @enum {string} */
      session_replication_role?: 'origin' | 'replica' | 'local'
      shared_buffers?: string
      statement_timeout?: string
      track_commit_timestamp?: boolean
      wal_keep_size?: string
      wal_sender_timeout?: string
      work_mem?: string
    }
    UpdatePostgresConfigBody: {
      effective_cache_size?: string
      logical_decoding_work_mem?: string
      maintenance_work_mem?: string
      track_activity_query_size?: string
      max_connections?: number
      max_locks_per_transaction?: number
      max_parallel_maintenance_workers?: number
      max_parallel_workers?: number
      max_parallel_workers_per_gather?: number
      max_replication_slots?: number
      max_slot_wal_keep_size?: string
      max_standby_archive_delay?: string
      max_standby_streaming_delay?: string
      max_wal_size?: string
      max_wal_senders?: number
      max_worker_processes?: number
      /** @enum {string} */
      session_replication_role?: 'origin' | 'replica' | 'local'
      shared_buffers?: string
      statement_timeout?: string
      track_commit_timestamp?: boolean
      wal_keep_size?: string
      wal_sender_timeout?: string
      work_mem?: string
      restart_database?: boolean
    }
    V1PgbouncerConfigResponse: {
      default_pool_size?: number
      ignore_startup_parameters?: string
      max_client_conn?: number
      /** @enum {string} */
      pool_mode?: 'transaction' | 'session' | 'statement'
      connection_string?: string
    }
    SupavisorConfigResponse: {
      identifier: string
      /** @enum {string} */
      database_type: 'PRIMARY' | 'READ_REPLICA'
      is_using_scram_auth: boolean
      db_user: string
      db_host: string
      db_port: number
      db_name: string
      connection_string: string
      /** @description Use connection_string instead */
      connectionString: string
      default_pool_size: number | null
      max_client_conn: number | null
      /** @enum {string} */
      pool_mode: 'transaction' | 'session'
    }
    UpdateSupavisorConfigBody: {
      default_pool_size?: number | null
      /**
       * @description Dedicated pooler mode for the project
       * @enum {string}
       */
      pool_mode?: 'transaction' | 'session'
    }
    UpdateSupavisorConfigResponse: {
      default_pool_size: number | null
      pool_mode: string
    }
    AuthConfigResponse: {
      api_max_request_duration: number | null
      db_max_pool_size: number | null
      disable_signup: boolean | null
      external_anonymous_users_enabled: boolean | null
      external_apple_additional_client_ids: string | null
      external_apple_client_id: string | null
      external_apple_enabled: boolean | null
      external_apple_secret: string | null
      external_azure_client_id: string | null
      external_azure_enabled: boolean | null
      external_azure_secret: string | null
      external_azure_url: string | null
      external_bitbucket_client_id: string | null
      external_bitbucket_enabled: boolean | null
      external_bitbucket_secret: string | null
      external_discord_client_id: string | null
      external_discord_enabled: boolean | null
      external_discord_secret: string | null
      external_email_enabled: boolean | null
      external_facebook_client_id: string | null
      external_facebook_enabled: boolean | null
      external_facebook_secret: string | null
      external_figma_client_id: string | null
      external_figma_enabled: boolean | null
      external_figma_secret: string | null
      external_github_client_id: string | null
      external_github_enabled: boolean | null
      external_github_secret: string | null
      external_gitlab_client_id: string | null
      external_gitlab_enabled: boolean | null
      external_gitlab_secret: string | null
      external_gitlab_url: string | null
      external_google_additional_client_ids: string | null
      external_google_client_id: string | null
      external_google_enabled: boolean | null
      external_google_secret: string | null
      external_google_skip_nonce_check: boolean | null
      external_kakao_client_id: string | null
      external_kakao_enabled: boolean | null
      external_kakao_secret: string | null
      external_keycloak_client_id: string | null
      external_keycloak_enabled: boolean | null
      external_keycloak_secret: string | null
      external_keycloak_url: string | null
      external_linkedin_oidc_client_id: string | null
      external_linkedin_oidc_enabled: boolean | null
      external_linkedin_oidc_secret: string | null
      external_slack_oidc_client_id: string | null
      external_slack_oidc_enabled: boolean | null
      external_slack_oidc_secret: string | null
      external_notion_client_id: string | null
      external_notion_enabled: boolean | null
      external_notion_secret: string | null
      external_phone_enabled: boolean | null
      external_slack_client_id: string | null
      external_slack_enabled: boolean | null
      external_slack_secret: string | null
      external_spotify_client_id: string | null
      external_spotify_enabled: boolean | null
      external_spotify_secret: string | null
      external_twitch_client_id: string | null
      external_twitch_enabled: boolean | null
      external_twitch_secret: string | null
      external_twitter_client_id: string | null
      external_twitter_enabled: boolean | null
      external_twitter_secret: string | null
      external_workos_client_id: string | null
      external_workos_enabled: boolean | null
      external_workos_secret: string | null
      external_workos_url: string | null
      external_web3_solana_enabled: boolean | null
      external_zoom_client_id: string | null
      external_zoom_enabled: boolean | null
      external_zoom_secret: string | null
      hook_custom_access_token_enabled: boolean | null
      hook_custom_access_token_uri: string | null
      hook_custom_access_token_secrets: string | null
      hook_mfa_verification_attempt_enabled: boolean | null
      hook_mfa_verification_attempt_uri: string | null
      hook_mfa_verification_attempt_secrets: string | null
      hook_password_verification_attempt_enabled: boolean | null
      hook_password_verification_attempt_uri: string | null
      hook_password_verification_attempt_secrets: string | null
      hook_send_sms_enabled: boolean | null
      hook_send_sms_uri: string | null
      hook_send_sms_secrets: string | null
      hook_send_email_enabled: boolean | null
      hook_send_email_uri: string | null
      hook_send_email_secrets: string | null
      hook_before_user_created_enabled: boolean | null
      hook_before_user_created_uri: string | null
      hook_before_user_created_secrets: string | null
      jwt_exp: number | null
      mailer_allow_unverified_email_sign_ins: boolean | null
      mailer_autoconfirm: boolean | null
      mailer_otp_exp: number
      mailer_otp_length: number | null
      mailer_secure_email_change_enabled: boolean | null
      mailer_subjects_confirmation: string | null
      mailer_subjects_email_change: string | null
      mailer_subjects_invite: string | null
      mailer_subjects_magic_link: string | null
      mailer_subjects_reauthentication: string | null
      mailer_subjects_recovery: string | null
      mailer_templates_confirmation_content: string | null
      mailer_templates_email_change_content: string | null
      mailer_templates_invite_content: string | null
      mailer_templates_magic_link_content: string | null
      mailer_templates_reauthentication_content: string | null
      mailer_templates_recovery_content: string | null
      mfa_max_enrolled_factors: number | null
      mfa_totp_enroll_enabled: boolean | null
      mfa_totp_verify_enabled: boolean | null
      mfa_phone_enroll_enabled: boolean | null
      mfa_phone_verify_enabled: boolean | null
      mfa_web_authn_enroll_enabled: boolean | null
      mfa_web_authn_verify_enabled: boolean | null
      mfa_phone_otp_length: number
      mfa_phone_template: string | null
      mfa_phone_max_frequency: number | null
      password_hibp_enabled: boolean | null
      password_min_length: number | null
      /** @enum {string|null} */
      password_required_characters:
        | 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
        | 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
        | 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~'
        | ''
        | null
      rate_limit_anonymous_users: number | null
      rate_limit_email_sent: number | null
      rate_limit_sms_sent: number | null
      rate_limit_token_refresh: number | null
      rate_limit_verify: number | null
      rate_limit_otp: number | null
      rate_limit_web3: number | null
      refresh_token_rotation_enabled: boolean | null
      saml_enabled: boolean | null
      saml_external_url: string | null
      saml_allow_encrypted_assertions: boolean | null
      security_captcha_enabled: boolean | null
      /** @enum {string|null} */
      security_captcha_provider: 'turnstile' | 'hcaptcha' | null
      security_captcha_secret: string | null
      security_manual_linking_enabled: boolean | null
      security_refresh_token_reuse_interval: number | null
      security_update_password_require_reauthentication: boolean | null
      security_update_password_require_current_password: boolean | null
      sessions_inactivity_timeout: number | null
      sessions_single_per_user: boolean | null
      sessions_tags: string | null
      sessions_timebox: number | null
      site_url: string | null
      sms_autoconfirm: boolean | null
      sms_max_frequency: number | null
      sms_messagebird_access_key: string | null
      sms_messagebird_originator: string | null
      sms_otp_exp: number | null
      sms_otp_length: number
      /** @enum {string|null} */
      sms_provider: 'messagebird' | 'textlocal' | 'twilio' | 'twilio_verify' | 'vonage' | null
      sms_template: string | null
      sms_test_otp: string | null
      /** Format: date-time */
      sms_test_otp_valid_until: string | null
      sms_textlocal_api_key: string | null
      sms_textlocal_sender: string | null
      sms_twilio_account_sid: string | null
      sms_twilio_auth_token: string | null
      sms_twilio_content_sid: string | null
      sms_twilio_message_service_sid: string | null
      sms_twilio_verify_account_sid: string | null
      sms_twilio_verify_auth_token: string | null
      sms_twilio_verify_message_service_sid: string | null
      sms_vonage_api_key: string | null
      sms_vonage_api_secret: string | null
      sms_vonage_from: string | null
      smtp_admin_email: string | null
      smtp_host: string | null
      smtp_max_frequency: number | null
      smtp_pass: string | null
      smtp_port: string | null
      smtp_sender_name: string | null
      smtp_user: string | null
      uri_allow_list: string | null
    }
    UpdateAuthConfigBody: {
      site_url?: string | null
      disable_signup?: boolean | null
      jwt_exp?: number | null
      smtp_admin_email?: string | null
      smtp_host?: string | null
      smtp_port?: string | null
      smtp_user?: string | null
      smtp_pass?: string | null
      smtp_max_frequency?: number | null
      smtp_sender_name?: string | null
      mailer_allow_unverified_email_sign_ins?: boolean | null
      mailer_autoconfirm?: boolean | null
      mailer_subjects_invite?: string | null
      mailer_subjects_confirmation?: string | null
      mailer_subjects_recovery?: string | null
      mailer_subjects_email_change?: string | null
      mailer_subjects_magic_link?: string | null
      mailer_subjects_reauthentication?: string | null
      mailer_templates_invite_content?: string | null
      mailer_templates_confirmation_content?: string | null
      mailer_templates_recovery_content?: string | null
      mailer_templates_email_change_content?: string | null
      mailer_templates_magic_link_content?: string | null
      mailer_templates_reauthentication_content?: string | null
      mfa_max_enrolled_factors?: number | null
      uri_allow_list?: string | null
      external_anonymous_users_enabled?: boolean | null
      external_email_enabled?: boolean | null
      external_phone_enabled?: boolean | null
      saml_enabled?: boolean | null
      saml_external_url?: string | null
      security_captcha_enabled?: boolean | null
      /** @enum {string|null} */
      security_captcha_provider?: 'turnstile' | 'hcaptcha' | null
      security_captcha_secret?: string | null
      sessions_timebox?: number | null
      sessions_inactivity_timeout?: number | null
      sessions_single_per_user?: boolean | null
      sessions_tags?: string | null
      rate_limit_anonymous_users?: number | null
      rate_limit_email_sent?: number | null
      rate_limit_sms_sent?: number | null
      rate_limit_verify?: number | null
      rate_limit_token_refresh?: number | null
      rate_limit_otp?: number | null
      rate_limit_web3?: number | null
      mailer_secure_email_change_enabled?: boolean | null
      refresh_token_rotation_enabled?: boolean | null
      password_hibp_enabled?: boolean | null
      password_min_length?: number | null
      /** @enum {string|null} */
      password_required_characters?:
        | 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
        | 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
        | 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~'
        | ''
        | null
      security_manual_linking_enabled?: boolean | null
      security_update_password_require_reauthentication?: boolean | null
      security_update_password_require_current_password?: boolean | null
      security_refresh_token_reuse_interval?: number | null
      mailer_otp_exp?: number
      mailer_otp_length?: number | null
      sms_autoconfirm?: boolean | null
      sms_max_frequency?: number | null
      sms_otp_exp?: number | null
      sms_otp_length?: number
      /** @enum {string|null} */
      sms_provider?: 'messagebird' | 'textlocal' | 'twilio' | 'twilio_verify' | 'vonage' | null
      sms_messagebird_access_key?: string | null
      sms_messagebird_originator?: string | null
      sms_test_otp?: string | null
      /** Format: date-time */
      sms_test_otp_valid_until?: string | null
      sms_textlocal_api_key?: string | null
      sms_textlocal_sender?: string | null
      sms_twilio_account_sid?: string | null
      sms_twilio_auth_token?: string | null
      sms_twilio_content_sid?: string | null
      sms_twilio_message_service_sid?: string | null
      sms_twilio_verify_account_sid?: string | null
      sms_twilio_verify_auth_token?: string | null
      sms_twilio_verify_message_service_sid?: string | null
      sms_vonage_api_key?: string | null
      sms_vonage_api_secret?: string | null
      sms_vonage_from?: string | null
      sms_template?: string | null
      hook_mfa_verification_attempt_enabled?: boolean | null
      hook_mfa_verification_attempt_uri?: string | null
      hook_mfa_verification_attempt_secrets?: string | null
      hook_password_verification_attempt_enabled?: boolean | null
      hook_password_verification_attempt_uri?: string | null
      hook_password_verification_attempt_secrets?: string | null
      hook_custom_access_token_enabled?: boolean | null
      hook_custom_access_token_uri?: string | null
      hook_custom_access_token_secrets?: string | null
      hook_send_sms_enabled?: boolean | null
      hook_send_sms_uri?: string | null
      hook_send_sms_secrets?: string | null
      hook_send_email_enabled?: boolean | null
      hook_send_email_uri?: string | null
      hook_send_email_secrets?: string | null
      hook_before_user_created_enabled?: boolean | null
      hook_before_user_created_uri?: string | null
      hook_before_user_created_secrets?: string | null
      external_apple_enabled?: boolean | null
      external_apple_client_id?: string | null
      external_apple_secret?: string | null
      external_apple_additional_client_ids?: string | null
      external_azure_enabled?: boolean | null
      external_azure_client_id?: string | null
      external_azure_secret?: string | null
      external_azure_url?: string | null
      external_bitbucket_enabled?: boolean | null
      external_bitbucket_client_id?: string | null
      external_bitbucket_secret?: string | null
      external_discord_enabled?: boolean | null
      external_discord_client_id?: string | null
      external_discord_secret?: string | null
      external_facebook_enabled?: boolean | null
      external_facebook_client_id?: string | null
      external_facebook_secret?: string | null
      external_figma_enabled?: boolean | null
      external_figma_client_id?: string | null
      external_figma_secret?: string | null
      external_github_enabled?: boolean | null
      external_github_client_id?: string | null
      external_github_secret?: string | null
      external_gitlab_enabled?: boolean | null
      external_gitlab_client_id?: string | null
      external_gitlab_secret?: string | null
      external_gitlab_url?: string | null
      external_google_enabled?: boolean | null
      external_google_client_id?: string | null
      external_google_secret?: string | null
      external_google_additional_client_ids?: string | null
      external_google_skip_nonce_check?: boolean | null
      external_kakao_enabled?: boolean | null
      external_kakao_client_id?: string | null
      external_kakao_secret?: string | null
      external_keycloak_enabled?: boolean | null
      external_keycloak_client_id?: string | null
      external_keycloak_secret?: string | null
      external_keycloak_url?: string | null
      external_linkedin_oidc_enabled?: boolean | null
      external_linkedin_oidc_client_id?: string | null
      external_linkedin_oidc_secret?: string | null
      external_slack_oidc_enabled?: boolean | null
      external_slack_oidc_client_id?: string | null
      external_slack_oidc_secret?: string | null
      external_notion_enabled?: boolean | null
      external_notion_client_id?: string | null
      external_notion_secret?: string | null
      external_slack_enabled?: boolean | null
      external_slack_client_id?: string | null
      external_slack_secret?: string | null
      external_spotify_enabled?: boolean | null
      external_spotify_client_id?: string | null
      external_spotify_secret?: string | null
      external_twitch_enabled?: boolean | null
      external_twitch_client_id?: string | null
      external_twitch_secret?: string | null
      external_twitter_enabled?: boolean | null
      external_twitter_client_id?: string | null
      external_twitter_secret?: string | null
      external_workos_enabled?: boolean | null
      external_workos_client_id?: string | null
      external_workos_secret?: string | null
      external_workos_url?: string | null
      external_web3_solana_enabled?: boolean | null
      external_zoom_enabled?: boolean | null
      external_zoom_client_id?: string | null
      external_zoom_secret?: string | null
      db_max_pool_size?: number | null
      api_max_request_duration?: number | null
      mfa_totp_enroll_enabled?: boolean | null
      mfa_totp_verify_enabled?: boolean | null
      mfa_web_authn_enroll_enabled?: boolean | null
      mfa_web_authn_verify_enabled?: boolean | null
      mfa_phone_enroll_enabled?: boolean | null
      mfa_phone_verify_enabled?: boolean | null
      mfa_phone_max_frequency?: number | null
      mfa_phone_otp_length?: number | null
      mfa_phone_template?: string | null
    }
    CreateThirdPartyAuthBody: {
      oidc_issuer_url?: string
      jwks_url?: string
      custom_jwks?: unknown
    }
    ThirdPartyAuth: {
      /** Format: uuid */
      id: string
      type: string
      oidc_issuer_url?: string | null
      jwks_url?: string | null
      custom_jwks?: unknown
      resolved_jwks?: unknown
      inserted_at: string
      updated_at: string
      resolved_at?: string | null
    }
    GetProjectAvailableRestoreVersionsResponse: {
      available_versions: {
        version: string
        /** @enum {string} */
        release_channel: 'internal' | 'alpha' | 'beta' | 'ga' | 'withdrawn' | 'preview'
        /** @enum {string} */
        postgres_engine: '13' | '14' | '15' | '17' | '17-oriole'
      }[]
    }
    ListProjectAddonsResponse: {
      selected_addons: {
        /** @enum {string} */
        type:
          | 'custom_domain'
          | 'compute_instance'
          | 'pitr'
          | 'ipv4'
          | 'auth_mfa_phone'
          | 'auth_mfa_web_authn'
          | 'log_drain'
        variant: {
          id:
            | (
                | 'ci_micro'
                | 'ci_small'
                | 'ci_medium'
                | 'ci_large'
                | 'ci_xlarge'
                | 'ci_2xlarge'
                | 'ci_4xlarge'
                | 'ci_8xlarge'
                | 'ci_12xlarge'
                | 'ci_16xlarge'
                | 'ci_24xlarge'
                | 'ci_24xlarge_optimized_cpu'
                | 'ci_24xlarge_optimized_memory'
                | 'ci_24xlarge_high_memory'
                | 'ci_48xlarge'
                | 'ci_48xlarge_optimized_cpu'
                | 'ci_48xlarge_optimized_memory'
                | 'ci_48xlarge_high_memory'
              )
            | 'cd_default'
            | ('pitr_7' | 'pitr_14' | 'pitr_28')
            | 'ipv4_default'
            | 'auth_mfa_phone_default'
            | 'auth_mfa_web_authn_default'
            | 'log_drain_default'
          name: string
          price: {
            description: string
            /** @enum {string} */
            type: 'fixed' | 'usage'
            /** @enum {string} */
            interval: 'monthly' | 'hourly'
            amount: number
          }
          /** @description Any JSON-serializable value */
          meta?: unknown
        }
      }[]
      available_addons: {
        /** @enum {string} */
        type:
          | 'custom_domain'
          | 'compute_instance'
          | 'pitr'
          | 'ipv4'
          | 'auth_mfa_phone'
          | 'auth_mfa_web_authn'
          | 'log_drain'
        name: string
        variants: {
          id:
            | (
                | 'ci_micro'
                | 'ci_small'
                | 'ci_medium'
                | 'ci_large'
                | 'ci_xlarge'
                | 'ci_2xlarge'
                | 'ci_4xlarge'
                | 'ci_8xlarge'
                | 'ci_12xlarge'
                | 'ci_16xlarge'
                | 'ci_24xlarge'
                | 'ci_24xlarge_optimized_cpu'
                | 'ci_24xlarge_optimized_memory'
                | 'ci_24xlarge_high_memory'
                | 'ci_48xlarge'
                | 'ci_48xlarge_optimized_cpu'
                | 'ci_48xlarge_optimized_memory'
                | 'ci_48xlarge_high_memory'
              )
            | 'cd_default'
            | ('pitr_7' | 'pitr_14' | 'pitr_28')
            | 'ipv4_default'
            | 'auth_mfa_phone_default'
            | 'auth_mfa_web_authn_default'
            | 'log_drain_default'
          name: string
          price: {
            description: string
            /** @enum {string} */
            type: 'fixed' | 'usage'
            /** @enum {string} */
            interval: 'monthly' | 'hourly'
            amount: number
          }
          /** @description Any JSON-serializable value */
          meta?: unknown
        }[]
      }[]
    }
    ApplyProjectAddonBody: {
      addon_variant:
        | (
            | 'ci_micro'
            | 'ci_small'
            | 'ci_medium'
            | 'ci_large'
            | 'ci_xlarge'
            | 'ci_2xlarge'
            | 'ci_4xlarge'
            | 'ci_8xlarge'
            | 'ci_12xlarge'
            | 'ci_16xlarge'
            | 'ci_24xlarge'
            | 'ci_24xlarge_optimized_cpu'
            | 'ci_24xlarge_optimized_memory'
            | 'ci_24xlarge_high_memory'
            | 'ci_48xlarge'
            | 'ci_48xlarge_optimized_cpu'
            | 'ci_48xlarge_optimized_memory'
            | 'ci_48xlarge_high_memory'
          )
        | 'cd_default'
        | ('pitr_7' | 'pitr_14' | 'pitr_28')
        | 'ipv4_default'
      /** @enum {string} */
      addon_type:
        | 'custom_domain'
        | 'compute_instance'
        | 'pitr'
        | 'ipv4'
        | 'auth_mfa_phone'
        | 'auth_mfa_web_authn'
        | 'log_drain'
    }
    ProjectClaimTokenResponse: {
      token_alias: string
      expires_at: string
      created_at: string
      /** Format: uuid */
      created_by: string
    }
    CreateProjectClaimTokenResponse: {
      token: string
      token_alias: string
      expires_at: string
      created_at: string
      /** Format: uuid */
      created_by: string
    }
    V1ProjectAdvisorsResponse: {
      lints: {
        /** @enum {string} */
        name:
          | 'unindexed_foreign_keys'
          | 'auth_users_exposed'
          | 'auth_rls_initplan'
          | 'no_primary_key'
          | 'unused_index'
          | 'multiple_permissive_policies'
          | 'policy_exists_rls_disabled'
          | 'rls_enabled_no_policy'
          | 'duplicate_index'
          | 'security_definer_view'
          | 'function_search_path_mutable'
          | 'rls_disabled_in_public'
          | 'extension_in_public'
          | 'rls_references_user_metadata'
          | 'materialized_view_in_api'
          | 'foreign_table_in_api'
          | 'unsupported_reg_types'
          | 'auth_otp_long_expiry'
          | 'auth_otp_short_length'
          | 'ssl_not_enforced'
          | 'network_restrictions_not_set'
          | 'password_requirements_min_length'
          | 'pitr_not_enabled'
          | 'auth_leaked_password_protection'
          | 'auth_insufficient_mfa_options'
          | 'auth_password_policy_missing'
          | 'leaked_service_key'
          | 'no_backup_admin'
        title: string
        /** @enum {string} */
        level: 'ERROR' | 'WARN' | 'INFO'
        /** @enum {string} */
        facing: 'EXTERNAL'
        categories: ('PERFORMANCE' | 'SECURITY')[]
        description: string
        detail: string
        remediation: string
        metadata?: {
          schema?: string
          name?: string
          entity?: string
          /** @enum {string} */
          type?: 'table' | 'view' | 'auth' | 'function' | 'extension' | 'compliance'
          fkey_name?: string
          fkey_columns?: number[]
        }
        cache_key: string
      }[]
    }
    AnalyticsResponse: {
      result?: unknown[]
      error?:
        | string
        | {
            code: number
            errors: {
              domain: string
              location: string
              locationType: string
              message: string
              reason: string
            }[]
            message: string
            status: string
          }
    }
    V1ListMigrationsResponse: {
      version: string
      name?: string
    }[]
    V1CreateMigrationBody: {
      query: string
      name?: string
    }
    V1UpsertMigrationBody: {
      query: string
      name?: string
    }
    V1RunQueryBody: {
      query: string
      read_only?: boolean
    }
    GetProjectDbMetadataResponse: {
      databases: ({
        name: string
        schemas: ({
          name: string
        } & {
          [key: string]: unknown
        })[]
      } & {
        [key: string]: unknown
      })[]
    }
    FunctionResponse: {
      id: string
      slug: string
      name: string
      /** @enum {string} */
      status: 'ACTIVE' | 'REMOVED' | 'THROTTLED'
      version: number
      /** Format: int64 */
      created_at: number
      /** Format: int64 */
      updated_at: number
      verify_jwt?: boolean
      import_map?: boolean
      entrypoint_path?: string
      import_map_path?: string
    }
    V1CreateFunctionBody: {
      slug: string
      name: string
      body: string
      verify_jwt?: boolean
    }
    BulkUpdateFunctionBody: {
      id: string
      slug: string
      name: string
      /** @enum {string} */
      status: 'ACTIVE' | 'REMOVED' | 'THROTTLED'
      version: number
      /** Format: int64 */
      created_at?: number
      verify_jwt?: boolean
      import_map?: boolean
      entrypoint_path?: string
      import_map_path?: string
    }[]
    BulkUpdateFunctionResponse: {
      functions: {
        id: string
        slug: string
        name: string
        /** @enum {string} */
        status: 'ACTIVE' | 'REMOVED' | 'THROTTLED'
        version: number
        /** Format: int64 */
        created_at: number
        /** Format: int64 */
        updated_at: number
        verify_jwt?: boolean
        import_map?: boolean
        entrypoint_path?: string
        import_map_path?: string
      }[]
    }
    FunctionDeployBody: {
      file?: string[]
      metadata: {
        entrypoint_path: string
        import_map_path?: string
        static_patterns?: string[]
        verify_jwt?: boolean
        name?: string
      }
    }
    DeployFunctionResponse: {
      id: string
      slug: string
      name: string
      /** @enum {string} */
      status: 'ACTIVE' | 'REMOVED' | 'THROTTLED'
      version: number
      /** Format: int64 */
      created_at?: number
      /** Format: int64 */
      updated_at?: number
      verify_jwt?: boolean
      import_map?: boolean
      entrypoint_path?: string
      import_map_path?: string
    }
    FunctionSlugResponse: {
      id: string
      slug: string
      name: string
      /** @enum {string} */
      status: 'ACTIVE' | 'REMOVED' | 'THROTTLED'
      version: number
      /** Format: int64 */
      created_at: number
      /** Format: int64 */
      updated_at: number
      verify_jwt?: boolean
      import_map?: boolean
      entrypoint_path?: string
      import_map_path?: string
    }
    StreamableFile: Record<string, never>
    V1UpdateFunctionBody: {
      name?: string
      body?: string
      verify_jwt?: boolean
    }
    V1StorageBucketResponse: {
      id: string
      name: string
      owner: string
      created_at: string
      updated_at: string
      public: boolean
    }
    CreateProviderBody: {
      /**
       * @description What type of provider will be created
       * @enum {string}
       */
      type: 'saml'
      metadata_xml?: string
      metadata_url?: string
      domains?: string[]
      attribute_mapping?: {
        keys: {
          [key: string]: {
            name?: string
            names?: string[]
            default?: Record<string, never> | number | string | boolean
            array?: boolean
          }
        }
      }
    }
    CreateProviderResponse: {
      id: string
      saml?: {
        id: string
        entity_id: string
        metadata_url?: string
        metadata_xml?: string
        attribute_mapping?: {
          keys: {
            [key: string]: {
              name?: string
              names?: string[]
              default?: Record<string, never> | number | string | boolean
              array?: boolean
            }
          }
        }
      }
      domains?: {
        id: string
        domain?: string
        created_at?: string
        updated_at?: string
      }[]
      created_at?: string
      updated_at?: string
    }
    ListProvidersResponse: {
      items: {
        id: string
        saml?: {
          id: string
          entity_id: string
          metadata_url?: string
          metadata_xml?: string
          attribute_mapping?: {
            keys: {
              [key: string]: {
                name?: string
                names?: string[]
                default?: Record<string, never> | number | string | boolean
                array?: boolean
              }
            }
          }
        }
        domains?: {
          id: string
          domain?: string
          created_at?: string
          updated_at?: string
        }[]
        created_at?: string
        updated_at?: string
      }[]
    }
    GetProviderResponse: {
      id: string
      saml?: {
        id: string
        entity_id: string
        metadata_url?: string
        metadata_xml?: string
        attribute_mapping?: {
          keys: {
            [key: string]: {
              name?: string
              names?: string[]
              default?: Record<string, never> | number | string | boolean
              array?: boolean
            }
          }
        }
      }
      domains?: {
        id: string
        domain?: string
        created_at?: string
        updated_at?: string
      }[]
      created_at?: string
      updated_at?: string
    }
    UpdateProviderBody: {
      metadata_xml?: string
      metadata_url?: string
      domains?: string[]
      attribute_mapping?: {
        keys: {
          [key: string]: {
            name?: string
            names?: string[]
            default?: Record<string, never> | number | string | boolean
            array?: boolean
          }
        }
      }
    }
    UpdateProviderResponse: {
      id: string
      saml?: {
        id: string
        entity_id: string
        metadata_url?: string
        metadata_xml?: string
        attribute_mapping?: {
          keys: {
            [key: string]: {
              name?: string
              names?: string[]
              default?: Record<string, never> | number | string | boolean
              array?: boolean
            }
          }
        }
      }
      domains?: {
        id: string
        domain?: string
        created_at?: string
        updated_at?: string
      }[]
      created_at?: string
      updated_at?: string
    }
    DeleteProviderResponse: {
      id: string
      saml?: {
        id: string
        entity_id: string
        metadata_url?: string
        metadata_xml?: string
        attribute_mapping?: {
          keys: {
            [key: string]: {
              name?: string
              names?: string[]
              default?: Record<string, never> | number | string | boolean
              array?: boolean
            }
          }
        }
      }
      domains?: {
        id: string
        domain?: string
        created_at?: string
        updated_at?: string
      }[]
      created_at?: string
      updated_at?: string
    }
    V1BackupsResponse: {
      region: string
      walg_enabled: boolean
      pitr_enabled: boolean
      backups: {
        is_physical_backup: boolean
        /** @enum {string} */
        status: 'COMPLETED' | 'FAILED' | 'PENDING' | 'REMOVED' | 'ARCHIVED' | 'CANCELLED'
        inserted_at: string
      }[]
      physical_backup_data: {
        earliest_physical_backup_date_unix?: number
        latest_physical_backup_date_unix?: number
      }
    }
    V1RestorePitrBody: {
      /** Format: int64 */
      recovery_time_target_unix: number
    }
    V1RestorePointPostBody: {
      name: string
    }
    V1RestorePointResponse: {
      name: string
      /** @enum {string} */
      status: 'AVAILABLE' | 'PENDING' | 'REMOVED'
    }
    V1UndoBody: {
      name: string
    }
    V1OrganizationMemberResponse: {
      user_id: string
      user_name: string
      email?: string
      role_name: string
      mfa_enabled: boolean
    }
    V1OrganizationSlugResponse: {
      id: string
      name: string
      /** @enum {string} */
      plan?: 'free' | 'pro' | 'team' | 'enterprise'
      opt_in_tags: (
        | 'AI_SQL_GENERATOR_OPT_IN'
        | 'AI_DATA_GENERATOR_OPT_IN'
        | 'AI_LOG_GENERATOR_OPT_IN'
      )[]
      allowed_release_channels: ('internal' | 'alpha' | 'beta' | 'ga' | 'withdrawn' | 'preview')[]
    }
    OrganizationProjectClaimResponse: {
      project: {
        ref: string
        name: string
      }
      preview: {
        valid: boolean
        warnings: {
          key: string
          message: string
        }[]
        errors: {
          key: string
          message: string
        }[]
        info: {
          key: string
          message: string
        }[]
        members_exceeding_free_project_limit: {
          name: string
          limit: number
        }[]
        target_organization_eligible: boolean | null
        target_organization_has_free_project_slots: boolean | null
        /** @enum {string} */
        source_subscription_plan: 'free' | 'pro' | 'team' | 'enterprise'
        /** @enum {string|null} */
        target_subscription_plan: 'free' | 'pro' | 'team' | 'enterprise' | null
      }
      expires_at: string
      created_at: string
      /** Format: uuid */
      created_by: string
    }
  }
  responses: never
  parameters: never
  requestBodies: never
  headers: never
  pathItems: never
}
export type $defs = Record<string, never>
export interface operations {
  'v1-get-a-branch-config': {
    parameters: {
      query?: never
      header?: never
      path: {
        /** @description Branch ID */
        branch_id: string
      }
      cookie?: never
    }
    requestBody?: never
    responses: {
      200: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['BranchDetailResponse']
        }
      }
      /** @description Failed to retrieve database branch */
      500: {
        headers: {
          [name: string]: unknown
        }
        content?: never
      }
    }
  }
  'v1-delete-a-branch': {
    parameters: {
      query?: never
      header?: never
      path: {
        /** @description Branch ID */
        branch_id: string
      }
      cookie?: never
    }
    requestBody?: never
    responses: {
      200: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['BranchDeleteResponse']
        }
      }
      /** @description Failed to delete database branch */
      500: {
        headers: {
          [name: string]: unknown
        }
        content?: never
      }
    }
  }
  'v1-update-a-branch-config': {
    parameters: {
      query?: never
      header?: never
      path: {
        /** @description Branch ID */
        branch_id: string
      }
      cookie?: never
    }
    requestBody: {
      content: {
        'application/json': components['schemas']['UpdateBranchBody']
      }
    }
    responses: {
      200: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['BranchResponse']
        }
      }
      /** @description Failed to update database branch */
      500: {
        headers: {
          [name: string]: unknown
        }
        content?: never
      }
    }
  }
  'v1-push-a-branch': {
    parameters: {
      query?: never
      header?: never
      path: {
        /** @description Branch ID */
        branch_id: string
      }
      cookie?: never
    }
    requestBody: {
      content: {
        'application/json': components['schemas']['BranchActionBody']
      }
    }
    responses: {
      201: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['BranchUpdateResponse']
        }
      }
      /** @description Failed to push database branch */
      500: {
        headers: {
          [name: string]: unknown
        }
        content?: never
      }
    }
  }
  'v1-merge-a-branch': {
    parameters: {
      query?: never
      header?: never
      path: {
        /** @description Branch ID */
        branch_id: string
      }
      cookie?: never
    }
    requestBody: {
      content: {
        'application/json': components['schemas']['BranchActionBody']
      }
    }
    responses: {
      201: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['BranchUpdateResponse']
        }
      }
      /** @description Failed to merge database branch */
      500: {
        headers: {
          [name: string]: unknown
        }
        content?: never
      }
    }
  }
  'v1-reset-a-branch': {
    parameters: {
      query?: never
      header?: never
      path: {
        /** @description Branch ID */
        branch_id: string
      }
      cookie?: never
    }
    requestBody: {
      content: {
        'application/json': components['schemas']['BranchActionBody']
      }
    }
    responses: {
      201: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['BranchUpdateResponse']
        }
      }
      /** @description Failed to reset database branch */
      500: {
        headers: {
          [name: string]: unknown
        }
        content?: never
      }
    }
  }
  'v1-diff-a-branch': {
    parameters: {
      query?: {
        included_schemas?: string
      }
      header?: never
      path: {
        /** @description Branch ID */
        branch_id: string
      }
      cookie?: never
    }
    requestBody?: never
    responses: {
      200: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'text/plain': string
        }
      }
      /** @description Failed to diff database branch */
      500: {
        headers: {
          [name: string]: unknown
        }
        content?: never
      }
    }
  }
  'v1-list-all-projects': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: never
    }
    requestBody?: never
    responses: {
      200: {
        headers: {
          [name: string]: unknown
        }
        content: {
          'application/json': components['schemas']['V1ProjectWithDatabaseResponse'][]
        }
      }
    }
  }
  'v1-create-a-project': {
    parameters: {
      query?: never
      header?: never
      path?: never
      cookie?: n
[truncated — showing 100000 of 179991 characters]
registry/default/platform/platform-kit-nextjs/lib/management-api.ts
import createClient from 'openapi-fetch'

import type { paths } from './management-api-schema'

export const client = createClient<paths>({
  baseUrl: '/api/supabase-proxy',
})
registry/default/platform/platform-kit-nextjs/lib/pg-meta/sql.ts
export const columnsSql = `
SELECT
  c.oid :: int8 AS table_id,
  nc.nspname AS schema,
  c.relname AS table,
  (c.oid || '.' || a.attnum) AS id,
  a.attnum AS ordinal_position,
  a.attname AS name,
  CASE
    WHEN a.atthasdef THEN pg_get_expr(ad.adbin, ad.adrelid)
    ELSE NULL
  END AS default_value,
  CASE
    WHEN t.typtype = 'd' THEN CASE
      WHEN bt.typelem <> 0 :: oid
      AND bt.typlen = -1 THEN 'ARRAY'
      WHEN nbt.nspname = 'pg_catalog' THEN format_type(t.typbasetype, NULL)
      ELSE 'USER-DEFINED'
    END
    ELSE CASE
      WHEN t.typelem <> 0 :: oid
      AND t.typlen = -1 THEN 'ARRAY'
      WHEN nt.nspname = 'pg_catalog' THEN format_type(a.atttypid, NULL)
      ELSE 'USER-DEFINED'
    END
  END AS data_type,
  COALESCE(bt.typname, t.typname) AS format,
  a.attidentity IN ('a', 'd') AS is_identity,
  CASE
    a.attidentity
    WHEN 'a' THEN 'ALWAYS'
    WHEN 'd' THEN 'BY DEFAULT'
    ELSE NULL
  END AS identity_generation,
  a.attgenerated IN ('s') AS is_generated,
  NOT (
    a.attnotnull
    OR t.typtype = 'd' AND t.typnotnull
  ) AS is_nullable,
  (
    c.relkind IN ('r', 'p')
    OR c.relkind IN ('v', 'f') AND pg_column_is_updatable(c.oid, a.attnum, FALSE)
  ) AS is_updatable,
  uniques.table_id IS NOT NULL AS is_unique,
  check_constraints.definition AS "check",
  array_to_json(
    array(
      SELECT
        enumlabel
      FROM
        pg_catalog.pg_enum enums
      WHERE
        enums.enumtypid = coalesce(bt.oid, t.oid)
        OR enums.enumtypid = coalesce(bt.typelem, t.typelem)
      ORDER BY
        enums.enumsortorder
    )
  ) AS enums,
  col_description(c.oid, a.attnum) AS comment
FROM
  pg_attribute a
  LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid
  AND a.attnum = ad.adnum
  JOIN (
    pg_class c
    JOIN pg_namespace nc ON c.relnamespace = nc.oid
  ) ON a.attrelid = c.oid
  JOIN (
    pg_type t
    JOIN pg_namespace nt ON t.typnamespace = nt.oid
  ) ON a.atttypid = t.oid
  LEFT JOIN (
    pg_type bt
    JOIN pg_namespace nbt ON bt.typnamespace = nbt.oid
  ) ON t.typtype = 'd'
  AND t.typbasetype = bt.oid
  LEFT JOIN (
    SELECT DISTINCT ON (table_id, ordinal_position)
      conrelid AS table_id,
      conkey[1] AS ordinal_position
    FROM pg_catalog.pg_constraint
    WHERE contype = 'u' AND cardinality(conkey) = 1
  ) AS uniques ON uniques.table_id = c.oid AND uniques.ordinal_position = a.attnum
  LEFT JOIN (
    -- We only select the first column check
    SELECT DISTINCT ON (table_id, ordinal_position)
      conrelid AS table_id,
      conkey[1] AS ordinal_position,
      substring(
        pg_get_constraintdef(pg_constraint.oid, true),
        8,
        length(pg_get_constraintdef(pg_constraint.oid, true)) - 8
      ) AS "definition"
    FROM pg_constraint
    WHERE contype = 'c' AND cardinality(conkey) = 1
    ORDER BY table_id, ordinal_position, oid asc
  ) AS check_constraints ON check_constraints.table_id = c.oid AND check_constraints.ordinal_position = a.attnum
WHERE
  NOT pg_is_other_temp_schema(nc.oid)
  AND a.attnum > 0
  AND NOT a.attisdropped
  AND (c.relkind IN ('r', 'v', 'm', 'f', 'p'))
  AND (
    pg_has_role(c.relowner, 'USAGE')
    OR has_column_privilege(
      c.oid,
      a.attnum,
      'SELECT, INSERT, UPDATE, REFERENCES'
    )
  )
`

export const tablesSql = `
SELECT
  c.oid :: int8 AS id,
  nc.nspname AS schema,
  c.relname AS name,
  c.relrowsecurity AS rls_enabled,
  c.relforcerowsecurity AS rls_forced,
  CASE
    WHEN c.relreplident = 'd' THEN 'DEFAULT'
    WHEN c.relreplident = 'i' THEN 'INDEX'
    WHEN c.relreplident = 'f' THEN 'FULL'
    ELSE 'NOTHING'
  END AS replica_identity,
  pg_total_relation_size(format('%I.%I', nc.nspname, c.relname)) :: int8 AS bytes,
  pg_size_pretty(
    pg_total_relation_size(format('%I.%I', nc.nspname, c.relname))
  ) AS size,
  pg_stat_get_live_tuples(c.oid) AS live_rows_estimate,
  pg_stat_get_dead_tuples(c.oid) AS dead_rows_estimate,
  obj_description(c.oid) AS comment,
  coalesce(pk.primary_keys, '[]') as primary_keys,
  coalesce(
    jsonb_agg(relationships) filter (where relationships is not null),
    '[]'
  ) as relationships
FROM
  pg_namespace nc
  JOIN pg_class c ON nc.oid = c.relnamespace
  left join (
    select
      table_id,
      jsonb_agg(_pk.*) as primary_keys
    from (
      select
        n.nspname as schema,
        c.relname as table_name,
        a.attname as name,
        c.oid :: int8 as table_id
      from
        pg_index i,
        pg_class c,
        pg_attribute a,
        pg_namespace n
      where
        i.indrelid = c.oid
        and c.relnamespace = n.oid
        and a.attrelid = c.oid
        and a.attnum = any (i.indkey)
        and i.indisprimary
    ) as _pk
    group by table_id
  ) as pk
  on pk.table_id = c.oid
  left join (
    select
      c.oid :: int8 as id,
      c.conname as constraint_name,
      nsa.nspname as source_schema,
      csa.relname as source_table_name,
      sa.attname as source_column_name,
      nta.nspname as target_table_schema,
      cta.relname as target_table_name,
      ta.attname as target_column_name
    from
      pg_constraint c
    join (
      pg_attribute sa
      join pg_class csa on sa.attrelid = csa.oid
      join pg_namespace nsa on csa.relnamespace = nsa.oid
    ) on sa.attrelid = c.conrelid and sa.attnum = any (c.conkey)
    join (
      pg_attribute ta
      join pg_class cta on ta.attrelid = cta.oid
      join pg_namespace nta on cta.relnamespace = nta.oid
    ) on ta.attrelid = c.confrelid and ta.attnum = any (c.confkey)
    where
      c.contype = 'f'
  ) as relationships
  on (relationships.source_schema = nc.nspname and relationships.source_table_name = c.relname)
  or (relationships.target_table_schema = nc.nspname and relationships.target_table_name = c.relname)
WHERE
  c.relkind IN ('r', 'p')
  AND NOT pg_is_other_temp_schema(nc.oid)
  AND (
    pg_has_role(c.relowner, 'USAGE')
    OR has_table_privilege(
      c.oid,
      'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER'
    )
    OR has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES')
  )
group by
  c.oid,
  c.relname,
  c.relrowsecurity,
  c.relforcerowsecurity,
  c.relreplident,
  nc.nspname,
  pk.primary_keys
`

export const extensionsSql = `
SELECT
  e.name,
  n.nspname AS schema,
  e.default_version,
  x.extversion AS installed_version,
  e.comment
FROM
  pg_available_extensions() e(name, default_version, comment)
  LEFT JOIN pg_extension x ON e.name = x.extname
  LEFT JOIN pg_namespace n ON x.extnamespace = n.oid
`
registry/default/platform/platform-kit-nextjs/lib/pg-meta/index.ts
import { stripIndent } from 'common-tags'

import { columnsSql, extensionsSql, tablesSql } from './sql'

export const SYSTEM_SCHEMAS = [
  'information_schema',
  'pg_catalog',
  'pg_toast',
  '_timescaledb_internal',
]

/**
 * Generates the SQL query to list tables in the database.
 */
export function listTablesSql(schemas: string[] = []) {
  let sql = stripIndent`
    with
      tables as (${tablesSql}),
      columns as (${columnsSql})
    select
      *,
      ${coalesceRowsToArray('columns', 'columns.table_id = tables.id')}
    from tables
  `

  sql += '\n'

  if (schemas.length > 0) {
    sql += `where schema in (${schemas.map((s) => `'${s}'`).join(',')})`
  } else {
    sql += `where schema not in (${SYSTEM_SCHEMAS.map((s) => `'${s}'`).join(',')})`
  }

  return sql
}

/**
 * Generates the SQL query to list all extensions in the database.
 */
export function listExtensionsSql() {
  return extensionsSql
}

/**
 * Generates a SQL segment that coalesces rows into an array of JSON objects.
 */
export const coalesceRowsToArray = (source: string, filter: string) => {
  return stripIndent`
    COALESCE(
      (
        SELECT
          array_agg(row_to_json(${source})) FILTER (WHERE ${filter})
        FROM
          ${source}
      ),
      '{}'
    ) AS ${source}
  `
}
registry/default/platform/platform-kit-nextjs/lib/pg-meta/types.ts
import { z } from 'zod'

export const postgresPrimaryKeySchema = z.object({
  schema: z.string(),
  table_name: z.string(),
  name: z.string(),
  table_id: z.number().int(),
})

export const postgresRelationshipSchema = z.object({
  id: z.number().int(),
  constraint_name: z.string(),
  source_schema: z.string(),
  source_table_name: z.string(),
  source_column_name: z.string(),
  target_table_schema: z.string(),
  target_table_name: z.string(),
  target_column_name: z.string(),
})

export const postgresColumnSchema = z.object({
  table_id: z.number().int(),
  schema: z.string(),
  table: z.string(),
  id: z.string().regex(/^(\d+)\.(\d+)$/),
  ordinal_position: z.number().int(),
  name: z.string(),
  default_value: z.any(),
  data_type: z.string(),
  format: z.string(),
  is_identity: z.boolean(),
  identity_generation: z.union([z.literal('ALWAYS'), z.literal('BY DEFAULT'), z.null()]),
  is_generated: z.boolean(),
  is_nullable: z.boolean(),
  is_updatable: z.boolean(),
  is_unique: z.boolean(),
  enums: z.array(z.string()),
  check: z.union([z.string(), z.null()]),
  comment: z.union([z.string(), z.null()]),
})

export const postgresTableSchema = z.object({
  id: z.number().int(),
  schema: z.string(),
  name: z.string(),
  rls_enabled: z.boolean(),
  rls_forced: z.boolean(),
  replica_identity: z.union([
    z.literal('DEFAULT'),
    z.literal('INDEX'),
    z.literal('FULL'),
    z.literal('NOTHING'),
  ]),
  bytes: z.number().int(),
  size: z.string(),
  live_rows_estimate: z.number().int(),
  dead_rows_estimate: z.number().int(),
  comment: z.string().nullable(),
  columns: z.array(postgresColumnSchema).optional(),
  primary_keys: z.array(postgresPrimaryKeySchema),
  relationships: z.array(postgresRelationshipSchema),
})

export const postgresExtensionSchema = z.object({
  name: z.string(),
  schema: z.union([z.string(), z.null()]),
  default_version: z.string(),
  installed_version: z.union([z.string(), z.null()]),
  comment: z.union([z.string(), z.null()]),
})

export type PostgresPrimaryKey = z.infer<typeof postgresPrimaryKeySchema>
export type PostgresRelationship = z.infer<typeof postgresRelationshipSchema>
export type PostgresColumn = z.infer<typeof postgresColumnSchema>
export type PostgresTable = z.infer<typeof postgresTableSchema>
export type PostgresExtension = z.infer<typeof postgresExtensionSchema>
registry/default/platform/platform-kit-nextjs/lib/schemas/auth.ts
import { z } from 'zod'

// Field labels mapping for form generation
export const authFieldLabels: Record<
  string,
  string | { label: string; options?: Record<string, string> }
> = {
  // General Settings
  disable_signup: 'Disable Signup',
  external_anonymous_users_enabled: 'Allow Anonymous Sign-ins',

  // Email Provider
  external_email_enabled: 'Enable Email Provider',
  mailer_autoconfirm: 'Confirm Email',
  mailer_secure_email_change_enabled: 'Secure Email Change',
  security_update_password_require_reauthentication: 'Secure Password Change',
  security_update_password_require_current_password: 'Require Password Change',
  password_hibp_enabled: 'Prevent Leaked Passwords',
  password_min_length: 'Minimum Password Length',
  password_required_characters: {
    label: 'Password Requirements',
    options: {
      NO_REQUIRED_CHARS: 'No requirements',
      'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789': 'Letters and numbers',
      'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789':
        'Lowercase, uppercase, and numbers',
      'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~':
        'Letters, numbers, and symbols',
    },
  },
  mailer_otp_exp: 'Email OTP Expiration (seconds)',
  mailer_otp_length: 'Email OTP Length',

  // Phone Provider
  external_phone_enabled: 'Enable Phone Provider',
  sms_provider: {
    label: 'SMS Provider',
    options: {
      twilio: 'Twilio',
      messagebird: 'MessageBird',
      textlocal: 'TextLocal',
      vonage: 'Vonage',
      twilio_verify: 'Twilio Verify',
    },
  },
  sms_twilio_account_sid: 'Twilio Account SID',
  sms_twilio_auth_token: 'Twilio Auth Token',
  sms_twilio_message_service_sid: 'Twilio Message Service SID',
  sms_twilio_content_sid: 'Twilio Content SID',
  sms_twilio_verify_account_sid: 'Twilio Verify Account SID',
  sms_twilio_verify_auth_token: 'Twilio Verify Auth Token',
  sms_twilio_verify_message_service_sid: 'Twilio Verify Message Service SID',
  sms_messagebird_access_key: 'MessageBird Access Key',
  sms_messagebird_originator: 'MessageBird Originator',
  sms_textlocal_api_key: 'TextLocal API Key',
  sms_textlocal_sender: 'TextLocal Sender',
  sms_vonage_api_key: 'Vonage API Key',
  sms_vonage_api_secret: 'Vonage API Secret',
  sms_vonage_from: 'Vonage From Number',
  sms_autoconfirm: 'Enable Phone Confirmations',
  sms_otp_exp: 'SMS OTP Expiry (seconds)',
  sms_otp_length: 'SMS OTP Length',
  sms_template: 'SMS Message Template',
  sms_test_otp: 'Test Phone Numbers and OTPs',
  sms_test_otp_valid_until: 'Test OTPs Valid Until',

  // Google Provider
  external_google_enabled: 'Enable Google Sign-in',
  external_google_client_id: 'Google Client ID(s)',
  external_google_secret: 'Google OAuth Client Secret',
  external_google_skip_nonce_check: 'Skip Nonce Check',

  // Secrets
  name: 'Secret Name',
  value: 'Secret Value',
  secretNames: 'Secret Names',
}

// New schemas for auth settings

export const authGeneralSettingsSchema = z.object({
  disable_signup: z.boolean().optional().describe('This will prevent new users from signing up.'),
  external_anonymous_users_enabled: z
    .boolean()
    .optional()
    .describe('This will enable anonymous users to sign in to your application.'),
})
export type AuthGeneralSettingsSchema = z.infer<typeof authGeneralSettingsSchema>

const NO_REQUIRED_CHARACTERS = 'NO_REQUIRED_CHARS'

export const authEmailProviderSchema = z
  .object({
    external_email_enabled: z
      .boolean()
      .optional()
      .describe('This will enable Email based signup and login for your application.'),
    mailer_autoconfirm: z
      .boolean()
      .optional()
      .describe(
        'Users will need to confirm their email address before signing in for the first time.'
      ),
    mailer_secure_email_change_enabled: z
      .boolean()
      .optional()
      .describe(
        'Users will be required to confirm any email change on both the old email address and new email address. If disabled, only the new email is required to confirm.'
      ),
    security_update_password_require_reauthentication: z
      .boolean()
      .optional()
      .describe(
        'Users will need to be recently logged in to change their password without requiring reauthentication. (A user is considered recently logged in if the session was created within the last 24 hours.) If disabled, a user can change their password at any time.'
      ),
    security_update_password_require_current_password: z
      .boolean()
      .optional()
      .describe(
        'Users will need to provide their current password in order to change their password. Works alongside security_update_password_require_reauthentication and both can be enabled at the same time.'
      ),
    password_hibp_enabled: z
      .boolean()
      .optional()
      .describe(
        'Rejects the use of known or easy to guess passwords on sign up or password change. Powered by the HaveIBeenPwned.org Pwned Passwords API.'
      ),
    password_min_length: z
      .number()
      .int()
      .min(6, 'Must be greater or equal to 6.')
      .describe(
        'Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more.'
      ),
    password_required_characters: z
      .preprocess(
        (val) => (val === '' || val === null || val === undefined ? NO_REQUIRED_CHARACTERS : val),
        z.enum([
          NO_REQUIRED_CHARACTERS,
          'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789',
          'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789',
          'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~',
        ])
      )
      .optional()
      .transform((val) => (val === NO_REQUIRED_CHARACTERS ? '' : val))
      .describe('Passwords that do not have at least one of each will be rejected as weak.'),
    mailer_otp_exp: z
      .number()
      .int()
      .min(0, 'Must be more than 0')
      .max(86400, 'Must be no more than 86400')
      .describe('Duration before an email otp / link expires in seconds.'),
    mailer_otp_length: z
      .number()
      .int()
      .min(6, 'Must be at least 6')
      .max(10, 'Must be no more than 10')
      .optional()
      .describe('Number of digits in the email OTP'),
  })
  .describe('Email provider settings.')
export type AuthEmailProviderSchema = z.infer<typeof authEmailProviderSchema>

export const authPhoneProviderSchema = z
  .object({
    external_phone_enabled: z
      .boolean()
      .optional()
      .describe('This will enable phone based login for your application'),
    sms_provider: z
      .enum(['twilio', 'messagebird', 'textlocal', 'vonage', 'twilio_verify'])
      .optional()
      .describe('External provider that will handle sending SMS messages'),
    // Twilio
    sms_twilio_account_sid: z.string().optional(),
    sms_twilio_auth_token: z.string().optional(),
    sms_twilio_message_service_sid: z.string().optional(),
    sms_twilio_content_sid: z
      .string()
      .optional()
      .describe('Twilio Content SID (Optional, For WhatsApp Only)'),
    // Twilio Verify
    sms_twilio_verify_account_sid: z.string().optional(),
    sms_twilio_verify_auth_token: z.string().optional(),
    sms_twilio_verify_message_service_sid: z.string().optional(),
    // Messagebird
    sms_messagebird_access_key: z.string().optional(),
    sms_messagebird_originator: z.string().optional(),
    // Textlocal
    sms_textlocal_api_key: z.string().optional(),
    sms_textlocal_sender: z.string().optional(),
    // Vonage
    sms_vonage_api_key: z.string().optional(),
    sms_vonage_api_secret: z.string().optional(),
    sms_vonage_from: z.string().optional(),
    // SMS Confirm settings
    sms_autoconfirm: z
      .boolean()
      .optional()
      .describe('Users will need to confirm their phone number before signing in.'),
    sms_otp_exp: z
      .number()
      .int()
      .optional()
      .describe('Duration before an SMS OTP expires in seconds.'),
    sms_otp_length: z.number().int().optional().describe('Number of digits in OTP.'),
    sms_template: z.string().optional().describe('To format the OTP code use `{{ .Code }}`'),
    sms_test_otp: z
      .string()
      .optional()
      .describe(
        'Register phone number and OTP combinations for testing as a comma separated list of <phone number>=<otp> pairs. Example: `18005550123=789012`'
      ),
    sms_test_otp_valid_until: z
      .string()
      .datetime({ message: 'Invalid datetime string.' })
      .optional()
      .describe(
        "Test phone number and OTP combinations won't be active past this date and time (local time zone)."
      ),
  })
  .describe('Phone provider settings.')
export type AuthPhoneProviderSchema = z.infer<typeof authPhoneProviderSchema>

export const authGoogleProviderObject = z.object({
  external_google_enabled: z.boolean().optional().describe('Enable Sign in with Google'),
  external_google_client_id: z
    .string()
    .regex(
      /^([a-z0-9-]+\.[a-z0-9-]+(\.[a-z0-9-]+)*(,[a-z0-9-]+\.[a-z0-9-]+(\.[a-z0-9-]+)*)*)$/i,
      'Google Client IDs should be a comma-separated list of domain-like strings.'
    )
    .optional(),
  external_google_secret: z
    .string()
    .regex(
      /^[a-z0-9.\/_-]*$/i,
      'Google OAuth Client Secrets usually contain letters, numbers, dots, dashes, and underscores.'
    )
    .optional(),
  external_google_skip_nonce_check: z
    .boolean()
    .optional()
    .describe('Allows ID tokens with any nonce to be accepted, which is less secure.'),
})

export const authGoogleProviderSchema = authGoogleProviderObject
  .superRefine((data, ctx) => {
    if (data.external_google_enabled && !data.external_google_client_id) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['external_google_client_id'],
        message: 'At least one Client ID is required when Google sign-in is enabled.',
      })
    }
    if (data.external_google_client_id && data.external_google_client_id.includes(' ')) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ['external_google_client_id'],
        message: 'Client IDs should not contain spaces.',
      })
    }
  })
  .describe('Google provider settings.')
export type AuthGoogleProviderSchema = z.infer<typeof authGoogleProviderSchema>

export const authConfigUpdateSchema = authGeneralSettingsSchema
  .merge(authEmailProviderSchema)
  .merge(authPhoneProviderSchema)
  .merge(authGoogleProviderObject)

export type AuthConfigUpdateSchema = z.infer<typeof authConfigUpdateSchema>

// A version used for partial updates (all fields optional)
export const authConfigUpdatePayloadSchema = authConfigUpdateSchema.partial()
registry/default/platform/platform-kit-nextjs/lib/schemas/secrets.ts
import z from 'zod'

export const secretsSchema = z
  .object({
    secrets: z.array(
      z.object({
        name: z
          .string()
          .min(1, 'Secret name is required.')
          .regex(
            /^[a-zA-Z_][a-zA-Z0-9_]*$/,
            'Must contain letters, numbers, and underscores, starting with a letter or underscore.'
          ),
        value: z.string().min(1, 'Secret value is required.'),
      })
    ),
  })
  .describe('Secrets schema for managing environment variables.')

export type SecretsSchema = z.infer<typeof secretsSchema>

export const deleteSecretsSchema = z
  .object({
    secretNames: z
      .array(z.string().min(1, 'Secret name cannot be empty.'))
      .min(1, 'At least one secret name is required.'),
  })
  .describe('Schema for deleting secrets by name.')
export type DeleteSecretsSchema = z.infer<typeof deleteSecretsSchema>
registry/default/platform/platform-kit-nextjs/contexts/SheetNavigationContext.tsx
'use client'

import { createContext, ReactNode, useCallback, useContext, useMemo, useState } from 'react'

type StackItem = {
  title: string
  component: ReactNode
}

type SheetNavigationContextType = {
  stack: StackItem[]
  push: (item: StackItem) => void
  pop: () => void
  popTo: (index: number) => void
  reset: () => void
}

const SheetNavigationContext = createContext<SheetNavigationContextType | undefined>(undefined)

export function SheetNavigationProvider({
  children,
  onStackEmpty,
  initialStack = [],
}: {
  children: ReactNode
  onStackEmpty?: () => void
  initialStack?: StackItem[]
}) {
  const [stack, setStack] = useState<StackItem[]>(initialStack)

  const push = useCallback((item: StackItem) => {
    setStack((prev) => [...prev, item])
  }, [])

  const pop = useCallback(() => {
    setStack((prev) => {
      const newStack = prev.slice(0, -1)
      if (newStack.length === 0) {
        onStackEmpty?.()
      }
      return newStack
    })
  }, [onStackEmpty])

  const popTo = useCallback((index: number) => {
    if (index < 0) return
    setStack((prev) => prev.slice(0, index + 1))
  }, [])

  const reset = useCallback(() => {
    setStack([])
    onStackEmpty?.()
  }, [onStackEmpty])

  const value = useMemo(
    () => ({ stack, push, pop, popTo, reset }),
    [stack, push, pop, popTo, reset]
  )

  return <SheetNavigationContext.Provider value={value}>{children}</SheetNavigationContext.Provider>
}

export function useSheetNavigation() {
  const context = useContext(SheetNavigationContext)
  if (context === undefined) {
    throw new Error('useSheetNavigation must be used within a SheetNavigationProvider')
  }
  return context
}

More from supabase

Browse all components in this library →