badgerhoneymoon starter .cursorrules file for TypeScript

# ALIEN

Check README.md for the idea and core pillars.

## Tech Stack

- Frontend: Next.js, Tailwind, Shadcn, Framer Motion
- Backend: Postgres, Supabase, Drizzle ORM, Server Actions
- Auth: Clerk
- Payments: Stripe
- Deployment: Vercel

## Project Structure

### General Structure

- `actions` - Server actions
  - `db` - Database-related actions (queries, mutations)
  - Other domain-specific actions
- `app` - Next.js app router
  - `api` - API routes
  - `route` - Route-specific code
    - `_components` - One-off components
    - `layout.tsx` - Layout
    - `page.tsx` - Page
- `components` - Shared components
  - `ui` - Reusable UI elements (buttons, inputs, etc.)
  - `utilities` - Helper components (layouts, providers, etc.)
- `db` - Database
  - `migrations` - Auto-generated migrations
  - `queries` - Database queries
  - `schema` - Database schemas
- `lib` - Library code
  - `hooks` - React hooks for shared logic
- `prompts` - AI prompt templates and configurations
- `public` - Static assets
- `types` - Type definitions

## Rules

### General Rules
- Use `@` to import anything from the project unless otherwise specified
- Use kebab case for all files and folders except:
  - React components (PascalCase)
  - React hooks (camelCase)
  - Database schemas (kebab-case)
- All components must be tagged with either `use server` or `use client`
- All code goes in root-level folders, DO NOT create a `src` directory
- Static assets:
  - Use `public` for external files (CSV, images, etc.)
  - Use `app` for route-specific assets

### Data Flow Rules
- Fetch data in server components and pass down as props to client components
- Use server actions from `/actions` for data mutations
- Use `auth` from `@clerk/nextjs/server` for user authentication
- Prefer existing types over new interfaces

### Environment Rules

- All environment variables should go in `.env.local`
- Do not expose environment variables to the frontend
- Use `NEXT_PUBLIC_` prefix for environment variables that need to be accessed from the frontend
- You may import environment variables in server actions and components by using `process.env.VARIABLE_NAME`

### Type Rules

- When importing types, use `@/types`
- Name files like `example-types.ts`
- All types should go in `types`
- Make sure to export the types in `types/index.ts`
- Prefer interfaces over type aliases
- If referring to db types, use `@/db/schema` such as `SelectAction` from `example-schema.ts`

An example of a type:

`types/actions-types.ts`

```ts
export type ActionState<T> = {
  isSuccess: boolean
  message: string
  data?: T
}
```

And exporting it:

`types/index.ts`

```ts
export * from "./actions-types"
```

### Frontend Rules

Follow these rules when working on the frontend.

It uses Next.js, Tailwind, Shadcn, and Framer Motion.

#### General Rules

- Use `lucide-react` for icons
- Use mobile first approach

#### Components

- When editing existing components preserve styles and classes - it's figma designs we should follow
- Use divs instead of other html tags unless otherwise specified
- Separate the main parts of a component's html with an extra blank line for visual spacing
- Use actions, not queries, in the app
- Always tag a component with either `use server` or `use client` at the top, including layouts and pages

##### Organization

- All components be named using kebab case like `example-component.tsx` unless otherwise specified
- Put components in `/_components` in the route if one-off components
- Put components in `/components` from the root if shared components

##### Server Components

- Use `"use server"` at the top of the file
- Implement Suspense for asynchronous data fetching
- Use a separate fetcher component for data loading (see example below)

Example of a server page:

```tsx
"use server"

import { Suspense } from "react"
import { SomeAction } from "@/actions/some-actions"
import SomeComponent from "./_components/some-component"
import SomeSkeleton from "./_components/some-skeleton"

export default async function ExampleServerPage({
  params
}: {
  params: { id: string }
}) {
  return (
    <Suspense fallback={<SomeSkeleton className="some-class" />}>
      <SomeComponentFetcher id={params.id} />
    </Suspense>
  )
}

async function SomeComponentFetcher({ id }: { id: string }) {
  const { data } = await SomeAction(id)

  <SomeComponent className="some-class" initialData={data || []} id={id} />
}
```

Example of a server component:

```tsx
"use server"

interface ExampleServerComponentProps {
  // Your props here
}

export async function ExampleServerComponent({
  props
}: ExampleServerComponentProps) {
  // Your code here
}
```

##### Client Components

- Use `"use client"` at the top of the file

Example of a client page:

```tsx
"use client"

export default function ExampleClientPage() {
  // Your code here
}
```

Example of a client component:

```tsx
"use client"

interface ExampleClientComponentProps {
  // Your props here
}

export default function ExampleClientComponent({
  props
}: ExampleClientComponentProps) {
  // Your code here
}
```

### Backend Rules

Uses Clerk (Auth), Supabase (DB), Drizzle ORM, and Server Actions.

#### API Routes
- New API routes go to app/[api-name]/[route].ts

#### Database Structure and Flow
1. Create schema first: `/db/schema/[name]-schema.ts`
   - Export in `/db/schema/index.ts`
   - Add to schema in `/db/db.ts`
2. Create queries next: `/db/queries/[name]-queries.ts`
   - All database operations go here
   - Return `ActionResult` type
   - Handle try/catch blocks
3. Create actions last: `/actions/[name]-actions.ts`
   - Server actions wrap queries
   - Never write DB logic here
   - Only handle data passing and validation

#### Database Setup
- Create `/db/db.ts` with the following structure:

```typescript
import { config } from "dotenv"
import { drizzle } from "drizzle-orm/postgres-js"
import postgres from "postgres"
// Import your schemas here
import { /* your schemas */ } from "./schema"

config({ path: ".env.local" })

const schema = {
  // Add your schemas here
}

const client = postgres(process.env.DATABASE_URL!)

export const db = drizzle(client, { schema }) 
```

Required environment variables:
- `DATABASE_URL`: Supabase connection string

Required package.json scripts:

```json
{
  "scripts": {
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate"
  }
}
```

Notes:
- Import schemas from `./schema` as they're created
- Add each schema to the schema object
- Use `.env.local` for environment variables
- Run migrations after schema changes:
  ```bash
  npm run db:generate
  npm run db:migrate
  ```

### AI Rules

#### Model Usage
- Use `gpt-4o-mini` for all LLM tasks unless otherwise specified
- Use `text-embedding-3-small` with 256 dimensions for embeddings

#### Embeddings Service
- Create `/lib/services/embeddings-service.ts` with this structure:

```typescript
"use server";

import OpenAI from "openai";

const openai = new OpenAI();

export async function generateEmbeddings(texts: string[]) {
  const response = await openai.embeddings.create({
    model: "text-embedding-3-small",
    dimensions: 256,
    input: texts
  });

  return response.data.map((item) => item.embedding);
}
```

#### Retrieval Service
- Create `/lib/services/retrieval-service.ts` with this structure:

```typescript
import { db } from "@/db";
import { documentsTable } from "@/db/schema/documents-data";
import { cosineDistance, desc, gt, sql } from "drizzle-orm";
import { generateEmbeddings } from "../generation/generate-embeddings";

export async function retrieveDocuments(
  input: string, 
  options: { 
    limit?: number; 
    minSimilarity?: number 
  } = {}
) {
  const { 
    limit = 10, 
    minSimilarity = 0.3 
  } = options;

  const embeddings = await generateEmbeddings([input]);
  const similarity = sql<number>`1 - (${cosineDistance(documentsTable.embedding, embeddings[0])})`;

  const documents = await db
    .select({
      content: documentsTable.content,
      similarity
    })
    .from(documentsTable)
    .where(gt(similarity, minSimilarity))
    .orderBy((t) => desc(t.similarity))
    .limit(limit);

  return documents;
}
```

#### Structured Output Service
- Create `/lib/services/structured-output-service.ts` with this structure:

```typescript
import OpenAI from 'openai';
import { z } from 'zod';
import { zodResponseFormat } from "openai/helpers/zod";

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

export interface StructuredOutputResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
}

export class StructuredOutputService {
  private static createErrorResponse(error: string): StructuredOutputResponse<any> {
    return { success: false, error };
  }

  static async process<T>(
    input: string,
    schema: z.ZodSchema,
    prompt: string,
    modelName: string = "gpt-4o-mini"
  ): Promise<StructuredOutputResponse<T>> {
    try {
      const completion = await openai.beta.chat.completions.parse({
        model: modelName,
        messages: [
          { role: "system", content: prompt },
          { role: "user", content: input }
        ],
        response_format: zodResponseFormat(schema, "data")
      });

      const parsedData = completion.choices[0].message.parsed;
      
      if (!parsedData) {
        return this.createErrorResponse('Failed to parse data');
      }

      return {
        success: true,
        data: parsedData as T
      };

    } catch (error) {
      console.error('Error structuring data:', error);
      return this.createErrorResponse(
        error instanceof Error ? error.message : 'Failed to structure data'
      );
    }
  }
}
```

Required environment variables:
- `OPENAI_API_KEY`: OpenAI API key

Notes:
- All services should be in `/lib/services/`
- Each service has a single responsibility
- Services can be composed together as needed

# NOTES
- When the file is exceeding 500 lines inform me
clerk
css
drizzle-orm
golang
javascript
less
next.js
npm
+9 more

First Time Repository

TypeScript

Languages:

CSS: 2.4KB
JavaScript: 0.2KB
TypeScript: 153.4KB
Created: 10/27/2024
Updated: 12/15/2024

All Repositories (1)