Contributing to OpenChat
Thanks for your interest in contributing to OpenChat! This guide will help you get started.
Before You Start
Install Prerequisites
- Bun 1.3+ - Package manager and runtime
- Node.js 20+ - Required for some tooling
- Git - Version control
- Docker (optional) - For container-based development
Read the Codebase
Familiarize yourself with: Set Up Development Environment
Follow the Quickstart to get OpenChat running locally.
Development Workflow
1. Create a Branch
# Sync with main
git checkout main
git pull --rebase origin main
# Create feature branch
git checkout -b feat/your-feature-name
Branch naming conventions:
feat/ - New features
fix/ - Bug fixes
docs/ - Documentation changes
refactor/ - Code refactoring
chore/ - Maintenance tasks
2. Make Changes
Follow our coding standards:
| Convention | Example |
|---|
| Indentation | Tabs |
| File names | kebab-case (sign-in.tsx) |
| Components | apps/web/src/components/ |
| Type imports | import type { Foo } from "..." |
3. Verify Your Changes
Run these before pushing:
# Lint
bun check
# Type check
bun check-types
# Run tests
bun test
# Build (for build-critical changes)
bun build
4. Commit with Conventional Commits
Use Conventional Commits format:
# Feature
git commit -m "feat(web): add workspace picker"
# Bug fix
git commit -m "fix(server): prevent duplicate chat titles"
# Documentation
git commit -m "docs: update deployment guide"
# Chore
git commit -m "chore: update dependencies"
Scopes: web, server, docs, extension, or omit for cross-cutting changes.
5. Open a Pull Request
Include in your PR:
- Summary - What changed and why
- Scope - Which apps affected (web/server/both)
- Testing notes - How to verify the change
- Screenshots/GIFs - For UI changes
Link related issues with Fixes #123 or Closes #123 to auto-close them on merge.
Coding Standards
TypeScript
// ✅ Good: Type-only import
import type { User } from "@/lib/types";
// ✅ Good: Named exports
export function useAuth() { ... }
export const AUTH_COOKIE = "ba_session";
// ❌ Avoid: Default exports (except for pages)
export default function Component() { ... }
React Components
// ✅ Good: Clear, focused component
function ChatMessage({ message }: { message: Message }) {
return (
<div className="p-4">
<span>{message.content}</span>
</div>
);
}
// ✅ Good: Use cn() for conditional classes
import { cn } from "@/lib/utils";
<div className={cn(
"base-class",
isActive && "active-class"
)} />
Convex Functions
// ✅ Good: Use new syntax
export const myQuery = query({
args: { id: v.id("users") },
handler: async (ctx, args) => {
return ctx.db.get(args.id);
},
});
// ✅ Good: Rate limit mutations
export const myMutation = mutation({
args: { ... },
handler: async (ctx, args) => {
const { ok, retryAfter } = await rateLimiter.limit(ctx, "myMutation", {
key: args.userId,
});
if (!ok) throwRateLimitError("action", retryAfter);
// ... rest of handler
},
});
Testing
Running Tests
# All tests
bun test
# Specific workspace
bun test:web
bun test:server
# Watch mode
bun test:watch
# With coverage
bun test -- --coverage
Writing Tests
Colocate tests with source files:
src/
lib/
utils.ts
utils.test.ts
components/
Button.tsx
Button.test.tsx
Example test:
// utils.test.ts
import { describe, it, expect } from "vitest";
import { formatDate } from "./utils";
describe("formatDate", () => {
it("formats date correctly", () => {
const date = new Date("2024-01-15");
expect(formatDate(date)).toBe("January 15, 2024");
});
});
Convex Tests
Use convex-test for Convex function tests:
// chats.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { convexTest } from "convex-test";
import { api } from "./_generated/api";
describe("chats", () => {
it("creates a chat", async () => {
const t = convexTest(schema);
// Insert test user
const userId = await t.run(async (ctx) => {
return ctx.db.insert("users", { ... });
});
// Test the mutation
const chatId = await t.mutation(api.chats.create, {
userId,
title: "Test Chat",
});
expect(chatId).toBeDefined();
});
});
Documentation
Update docs when:
- Adding user-facing features
- Changing configuration options
- Modifying API behavior
- Updating deployment steps
Docs Structure
docs-site/ # Mintlify documentation
├── index.mdx # Introduction
├── quickstart.mdx # Getting started
├── guides/ # In-depth guides
│ ├── architecture.mdx
│ ├── authentication.mdx
│ └── ...
└── self-hosting/ # Deployment docs
├── docker.mdx
└── environment.mdx
Local Docs Preview
cd docs-site
bunx @mintlify/cli dev
# Open http://localhost:3000
Pull Request Review
What We Look For
Automated Checks
PRs trigger:
- Lint - oxlint
- Type check - TypeScript
- Tests - Vitest
- Build - Production build verification
- CodeQL - Security scanning
Getting Reviews
- Self-review first - Check your own PR before requesting review
- Respond to feedback - Address comments or explain why you disagree
- Keep it small - Smaller PRs get faster, better reviews
Common Tasks
Adding a New Page
- Create route file in
apps/web/src/routes/
- Use auth guard pattern if needed
- Add navigation link to sidebar
// apps/web/src/routes/new-page.tsx
import { createFileRoute } from "@tanstack/react-router";
import { useAuth } from "@/lib/auth-client";
export const Route = createFileRoute("/new-page")({
component: NewPage,
});
function NewPage() {
const { isAuthenticated, loading } = useAuth();
if (loading) return <Loading />;
if (!isAuthenticated) return <SignInPrompt />;
return <div>Your content here</div>;
}
Adding a Convex Function
- Add to appropriate file in
apps/server/convex/
- Update schema if adding new table
- Run
bun x convex codegen to update types
// apps/server/convex/example.ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
export const myQuery = query({
args: { id: v.id("users") },
handler: async (ctx, args) => {
return ctx.db.get(args.id);
},
});
Adding UI Components
- Use shadcn/ui primitives when possible
- Add to
apps/web/src/components/
- Follow existing patterns in the codebase
# Add shadcn component
cd apps/web
bunx shadcn@latest add button
Getting Help
Code of Conduct
We follow the Contributor Covenant. Be respectful, inclusive, and constructive.
For security issues, please email the maintainers directly rather than opening a public issue.
Thank you for contributing to OpenChat! Every contribution helps make the project better.