# 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