Instruction file imported from wraglet/wraglet (
.cursor/rules/wraglet.mdc). Copyright stays with the author.
Wraglet Project Guidelines
Import Patterns
// Correct import order and structure
import { useState, useEffect } from 'react'
import { NextResponse } from 'next/server'
import { z } from 'zod'
import { useForm } from 'react-hook-form'
import { User } from '@/models/User'
import type { IUserDocument } from '@/models/User'
import '@/styles/globals.css'
TypeScript Patterns
// Model Interface Pattern
export interface IUser {
_id: string
email: string
firstName: string
lastName: string
}
export interface IUserDocument extends Omit<IUser, '_id'>, Document {
_id: Types.ObjectId
}
// Props Interface Pattern
interface UserCardProps {
user: IUser
onSelect: (userId: string) => void
}
// Literal Types
const STATUS = {
ACTIVE: 'active',
INACTIVE: 'inactive'
} as const
type Status = typeof STATUS[keyof typeof STATUS]
Function Patterns
// Arrow Function Pattern
export const getUserById = async (id: string): Promise<IUser | null> => {
try {
await client()
const user = await User.findById(id).lean()
return user
} catch (error) {
console.error('Error fetching user:', error)
return null
}
}
// Named Export Pattern
export const formatDate = (date: Date): string => {
return new Intl.DateTimeFormat('en-US').format(date)
}
API Route Patterns
// Route Handler Pattern
export const GET = async (
request: Request,
{ params }: { params: Promise<{ id: string }> }
) => {
try {
const { id } = await params
const user = await getUserById(id)
if (!user) {
return NextResponse.json(
{ error: 'User not found' },
{ status: 404 }
)
}
return NextResponse.json(user)
} catch (error) {
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
Server Actions
// Server Action Pattern
'use server'
import { z } from 'zod'
import { revalidatePath } from 'next/cache'
const createUserSchema = z.object({
email: z.string().email(),
password: z.string().min(8)
})
export const createUser = async (formData: FormData) => {
try {
const validatedData = createUserSchema.parse({
email: formData.get('email'),
password: formData.get('password')
})
await client()
const user = await User.create(validatedData)
revalidatePath('/users')
return { success: true, user }
} catch (error) {
return { success: false, error: 'Failed to create user' }
}
}
Database Patterns
// Model Definition Pattern
import { Schema, model, models } from 'mongoose'
const UserSchema = new Schema({
email: { type: String, required: true, unique: true },
firstName: { type: String, required: true },
lastName: { type: String, required: true }
}, { timestamps: true })
const User = models?.User || model('User', UserSchema)
export default User
Form Handling
// Form Validation Pattern
// validations.ts
import { z } from 'zod'
export const userSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters')
})
// Form Component Pattern
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { userSchema } from '@/validations'
export const UserForm = () => {
const form = useForm({
resolver: zodResolver(userSchema),
defaultValues: {
email: '',
password: ''
}
})
const onSubmit = async (data: z.infer<typeof userSchema>) => {
// Handle form submission
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
)
}
State Management
// Zustand Store Pattern
import { create } from 'zustand'
interface UserStore {
user: IUser | null
setUser: (user: IUser) => void
clearUser: () => void
}
const useUserStore = create<UserStore>((set) => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null })
}))
export default useUserStore
Storage Patterns
// Cloudflare R2 Upload Pattern
export const uploadFile = async (file: File): Promise<string> => {
try {
const key = `${Date.now()}-${file.name}`
const buffer = await file.arrayBuffer()
await s3Client.send(new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME,
Key: key,
Body: buffer,
ContentType: file.type
}))
return key
} catch (error) {
throw new Error('Failed to upload file')
}
}
Real-time Patterns
// Ably Channel Pattern
import { useChannel } from '@ably-labs/react-hooks'
export const ChatComponent = () => {
const [messages, setMessages] = useState<Message[]>([])
const [channel] = useChannel('chat', (message) => {
setMessages((prev) => [...prev, message.data])
})
const sendMessage = async (content: string) => {
await channel.publish('message', { content })
}
return (
// Chat UI implementation
)
}
Component Patterns
// Functional Component Pattern
interface ButtonProps {
variant?: 'primary' | 'secondary'
children: React.ReactNode
onClick?: () => void
}
export const Button = ({
variant = 'primary',
children,
onClick
}: ButtonProps) => {
return (
<button
className={cn(
'px-4 py-2 rounded',
variant === 'primary' ? 'bg-blue-500' : 'bg-gray-500'
)}
onClick={onClick}
>
{children}
</button>
)
}
Error Handling
// Error Boundary Pattern
export class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
{ hasError: boolean }
> {
constructor(props: { children: React.ReactNode }) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError() {
return { hasError: true }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo)
}
render() {
if (this.state.hasError) {
return <div>Something went wrong</div>
}
return this.props.children
}
}
Authentication
// NextAuth Configuration Pattern
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
CredentialsProvider({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' }
},
async authorize(credentials) {
try {
await client()
const user = await User.findOne({
email: credentials?.email
}).lean()
if (user && await bcrypt.compare(credentials?.password, user.hashedPassword)) {
return {
id: user._id.toString(),
email: user.email
}
}
return null
} catch (error) {
return null
}
}
})
],
session: {
strategy: 'jwt'
}
})
API Response Pattern
// Standard API Response Pattern
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
}
export const apiResponse = <T>(
success: boolean,
data?: T,
error?: string
): ApiResponse<T> => ({
success,
...(data && { data }),
...(error && { error })
})
Type Guards
// Type Guard Pattern
export const isUser = (value: unknown): value is IUser => {
return (
typeof value === 'object' &&
value !== null &&
'email' in value &&
'firstName' in value &&
'lastName' in value
)
}
Utility Functions
// Utility Function Pattern
export const formatDate = (date: Date): string => {
return new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date)
}
export const generateId = (): string => {
return crypto.randomUUID()
}
Environment Variables
// Environment Variables Pattern
declare module '@env' {
export const MONGODB_URI: string
export const AUTH_SECRET: string
export const R2_BUCKET_NAME: string
export const ABLY_API_KEY: string
}
Constants
// Constants Pattern
export const ROUTES = {
HOME: '/',
LOGIN: '/login',
REGISTER: '/register',
PROFILE: '/profile'
} as const
export const API_ENDPOINTS = {
USERS: '/api/users',
POSTS: '/api/posts',
COMMENTS: '/api/comments'
} as const
Testing (Wraglet standard)
Do not duplicate the full testing section here — it is maintained in .cursor/rules/general.mdc under Testing (Wraglet standard) (examples, stack, and mocks). Project source of truth: docs/TESTING.md.
Documentation Patterns
/**
* Creates a new user in the database
* @param {Object} userData - The user data to create
* @param {string} userData.email - The user's email address
* @param {string} userData.password - The user's password
* @returns {Promise<IUser>} The created user object
* @throws {Error} If user creation fails
*/
export const createUser = async (userData: {
email: string
password: string
}): Promise<IUser> => {
// Implementation
}
Code Style
- Follow ESLint configuration
- Use Prettier for code formatting
- Use proper naming conventions
- Use proper indentation
- Use proper spacing
- Use proper line breaks
- Use proper comments
Error Handling
- Use proper error boundaries
- Use proper error messages
- Use proper error logging
- Use proper error recovery
- Use proper error types
- Use proper error handling patterns
Security
- Use proper authentication
- Use proper authorization
- Use proper input validation
- Use proper output encoding
- Use proper error handling
- Use proper logging
- Use proper monitoring
Performance
- Use proper code splitting
- Use proper lazy loading
- Use proper caching
- Use proper optimization
- Use proper monitoring
- Use proper profiling
- Use proper debugging
Code Structure and Organization
- Follow Next.js 13+ app directory structure
- Keep components modular and reusable
- Use TypeScript for type safety
- Implement proper error boundaries
- Follow React best practices for hooks and state management
Authentication and Security
- Always use secure authentication methods
- Implement proper session management
- Follow OWASP security guidelines
- Use environment variables for sensitive data
- Implement proper input validation
Real-time Features
- Use Ably for real-time communications
- Implement proper error handling for real-time features
- Use WebRTC for peer-to-peer communication
- Ensure proper cleanup of real-time connections
Content Management
- Implement proper content moderation
- Use proper validation for user-generated content
- Follow content guidelines from VISION.md
- Implement proper caching strategies
Accessibility
- Follow WCAG 2.1 guidelines
- Implement proper ARIA attributes
- Ensure keyboard navigation
- Test with screen readers
Documentation
- Document complex logic
- Keep README files updated
- Document API endpoints
- Follow JSDoc standards
Community Standards
- No hate speech or harassment
- No explicit content
- No violent content
- No harmful activities
- No misinformation
Trust and Safety
- Implement proper moderation tools
- Follow community guidelines
- Use proper reporting mechanisms
- Implement proper blocking features
Additional Wraglet Rules
- Always use arrow functions for all functions and React components.
- Do not stack more than one React component in a single file. Each component must be in its own file.