{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "storage-supabase",
  "title": "CodeRabbit Supabase Adapter",
  "author": "Ray <hello@ramonclaudio.com>",
  "description": "Supabase (PostgreSQL) adapter with Row Level Security support. Includes SQL schema and RLS policies.",
  "dependencies": [
    "@supabase/supabase-js"
  ],
  "registryDependencies": [
    "https://coderabbit-shadcn-registry.vercel.app/r/types.json",
    "https://coderabbit-shadcn-registry.vercel.app/r/storage-adapter.json"
  ],
  "files": [
    {
      "path": "registry/default/lib/storage-supabase.ts",
      "content": "import type {\n  ReportStorageAdapter,\n  ListReportsResponse,\n} from '@/registry/default/lib/storage-adapter'\nimport type {\n  StoredReport,\n  ReportStatus,\n  ReportResult,\n  FilterConfig,\n} from '@/registry/default/lib/types'\nimport type { SupabaseClient } from '@supabase/supabase-js'\n\nexport interface SupabaseStorageConfig {\n  client: SupabaseClient\n  tableName?: string\n  enableRLS?: boolean\n}\n\n/**\n * Database row type matching the Supabase schema (snake_case columns)\n */\ninterface DatabaseReportRow {\n  id: string\n  status: ReportStatus\n  from_date: string\n  to_date: string\n  prompt_template: string | null\n  custom_prompt: string | null\n  group_by: string | null\n  subgroup_by: string | null\n  org_id: string | null\n  parameters: FilterConfig[] | null\n  results: ReportResult[] | null\n  error: string | null\n  duration_ms: number | null\n  created_at: string\n  user_id: string | null\n}\n\n/**\n * Supabase storage adapter for CodeRabbit reports\n *\n * @example\n * ```typescript\n * import { createClient } from '@supabase/supabase-js'\n * import { SupabaseStorageAdapter } from '@/registry/default/lib/storage-supabase'\n *\n * const supabase = createClient(\n *   process.env.SUPABASE_URL!,\n *   process.env.SUPABASE_ANON_KEY!\n * )\n *\n * const storage = new SupabaseStorageAdapter({ client: supabase })\n * ```\n *\n * Database schema:\n * ```sql\n * create table coderabbit_reports (\n *   id uuid primary key default gen_random_uuid(),\n *   status text not null check (status in ('pending', 'completed', 'failed')),\n *   from_date text not null,\n *   to_date text not null,\n *   prompt_template text,\n *   custom_prompt text,\n *   group_by text,\n *   subgroup_by text,\n *   org_id text,\n *   parameters jsonb default '[]'::jsonb,\n *   results jsonb,\n *   error text,\n *   duration_ms integer,\n *   created_at timestamptz default now(),\n *   user_id uuid references auth.users(id)\n * );\n *\n * -- Indexes for better performance\n * create index idx_reports_status on coderabbit_reports(status);\n * create index idx_reports_user_id on coderabbit_reports(user_id);\n * create index idx_reports_created_at on coderabbit_reports(created_at desc);\n *\n * -- Enable RLS (optional)\n * alter table coderabbit_reports enable row level security;\n *\n * -- RLS Policy: Users can only see their own reports\n * create policy \"Users can view own reports\"\n *   on coderabbit_reports for select\n *   using (auth.uid() = user_id);\n *\n * create policy \"Users can insert own reports\"\n *   on coderabbit_reports for insert\n *   with check (auth.uid() = user_id);\n * ```\n */\nexport class SupabaseStorageAdapter implements ReportStorageAdapter {\n  private client: SupabaseClient\n  private tableName: string\n\n  constructor(config: SupabaseStorageConfig) {\n    this.client = config.client\n    this.tableName = config.tableName || 'coderabbit_reports'\n  }\n\n  async create(data: Omit<StoredReport, 'id' | 'createdAt'>): Promise<string> {\n    const { data: report, error } = await this.client\n      .from(this.tableName)\n      .insert({\n        status: data.status,\n        from_date: data.fromDate,\n        to_date: data.toDate,\n        prompt_template: data.promptTemplate,\n        custom_prompt: data.customPrompt,\n        group_by: data.groupBy,\n        subgroup_by: data.subgroupBy,\n        org_id: data.orgId,\n        parameters: data.parameters || [],\n        results: data.results,\n        error: data.error,\n        duration_ms: data.durationMs,\n      })\n      .select('id')\n      .single()\n\n    if (error) {\n      throw new Error(`Failed to create report: ${error.message}`)\n    }\n\n    return report.id\n  }\n\n  async updateSuccess(\n    id: string,\n    results: ReportResult[],\n    durationMs: number\n  ): Promise<void> {\n    const { error } = await this.client\n      .from(this.tableName)\n      .update({\n        status: 'completed' as ReportStatus,\n        results,\n        duration_ms: durationMs,\n      })\n      .eq('id', id)\n\n    if (error) {\n      throw new Error(`Failed to update report: ${error.message}`)\n    }\n  }\n\n  async updateFailure(\n    id: string,\n    errorMessage: string,\n    durationMs: number\n  ): Promise<void> {\n    const { error } = await this.client\n      .from(this.tableName)\n      .update({\n        status: 'failed' as ReportStatus,\n        error: errorMessage,\n        duration_ms: durationMs,\n      })\n      .eq('id', id)\n\n    if (error) {\n      throw new Error(`Failed to update report: ${error.message}`)\n    }\n  }\n\n  async get(id: string): Promise<StoredReport | null> {\n    const { data, error } = await this.client\n      .from(this.tableName)\n      .select('*')\n      .eq('id', id)\n      .single()\n\n    if (error) {\n      if (error.code === 'PGRST116') return null // Not found\n      throw new Error(`Failed to get report: ${error.message}`)\n    }\n\n    if (!data) return null\n\n    return this.mapToStoredReport(data)\n  }\n\n  async list(options?: {\n    limit?: number\n    offset?: number\n    status?: ReportStatus\n  }): Promise<ListReportsResponse> {\n    let query = this.client\n      .from(this.tableName)\n      .select('*', { count: 'exact' })\n      .order('created_at', { ascending: false })\n\n    if (options?.status) {\n      query = query.eq('status', options.status)\n    }\n\n    if (options?.limit) {\n      query = query.limit(options.limit)\n    }\n\n    if (options?.offset) {\n      query = query.range(\n        options.offset,\n        options.offset + (options.limit || 10) - 1\n      )\n    }\n\n    const { data, error, count } = await query\n\n    if (error) {\n      throw new Error(`Failed to list reports: ${error.message}`)\n    }\n\n    return {\n      reports: data?.map((r) => this.mapToStoredReport(r)) || [],\n      total: count || 0,\n    }\n  }\n\n  async delete(id: string): Promise<void> {\n    const { error } = await this.client\n      .from(this.tableName)\n      .delete()\n      .eq('id', id)\n\n    if (error) {\n      throw new Error(`Failed to delete report: ${error.message}`)\n    }\n  }\n\n  private mapToStoredReport(data: DatabaseReportRow): StoredReport {\n    return {\n      id: data.id,\n      status: data.status,\n      fromDate: data.from_date,\n      toDate: data.to_date,\n      promptTemplate: data.prompt_template,\n      customPrompt: data.custom_prompt,\n      groupBy: data.group_by,\n      subgroupBy: data.subgroup_by,\n      orgId: data.org_id,\n      parameters: data.parameters || [],\n      results: data.results,\n      error: data.error,\n      durationMs: data.duration_ms,\n      createdAt: new Date(data.created_at).getTime(),\n    }\n  }\n}\n",
      "type": "registry:lib"
    }
  ],
  "docs": "Create a Supabase client and pass to SupabaseStorageAdapter. Run the SQL schema from the adapter file to create the coderabbit_reports table with RLS policies.",
  "categories": [
    "storage",
    "database"
  ],
  "type": "registry:lib"
}