feat: add item creation tools
All checks were successful
Deploy to Production / test (push) Successful in 37s
All checks were successful
Deploy to Production / test (push) Successful in 37s
This commit is contained in:
187
CLAUDE.md
Normal file
187
CLAUDE.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Development
|
||||
bun --watch bot/index.ts # Run bot + API with hot reload
|
||||
docker compose up # Start all services (bot, API, database)
|
||||
docker compose up app # Start just the app (bot + API)
|
||||
docker compose up db # Start just the database
|
||||
|
||||
# Testing
|
||||
bun test # Run all tests
|
||||
bun test path/to/file.test.ts # Run a single test file
|
||||
bun test shared/modules/economy # Run tests in a directory
|
||||
bun test --watch # Watch mode
|
||||
|
||||
# Database
|
||||
bun run db:push:local # Push schema changes (local)
|
||||
bun run db:studio # Open Drizzle Studio (localhost:4983)
|
||||
bun run generate # Generate Drizzle migrations (Docker)
|
||||
bun run migrate # Apply migrations (Docker)
|
||||
|
||||
# Admin Panel
|
||||
bun run panel:dev # Start Vite dev server for dashboard
|
||||
bun run panel:build # Build React dashboard for production
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
Aurora is a Discord RPG bot + REST API running as a **single Bun process**. The bot and API share the same database client and services.
|
||||
|
||||
```
|
||||
bot/ # Discord bot
|
||||
├── commands/ # Slash commands by category (admin, economy, inventory, etc.)
|
||||
├── events/ # Discord event handlers
|
||||
├── lib/ # BotClient, handlers, loaders, embed helpers, commandUtils
|
||||
├── modules/ # Feature modules (views, interactions per domain)
|
||||
└── graphics/ # Canvas-based image generation (@napi-rs/canvas)
|
||||
|
||||
shared/ # Shared between bot and API
|
||||
├── db/ # Drizzle ORM client + schema (users, economy, inventory, quests, etc.)
|
||||
├── lib/ # env, config, errors, logger, types, utils
|
||||
└── modules/ # Domain services (economy, user, inventory, quest, moderation, etc.)
|
||||
|
||||
api/ # REST API (Bun HTTP server)
|
||||
└── src/routes/ # Route handlers for each domain
|
||||
|
||||
panel/ # React admin dashboard (Vite + Tailwind + Radix UI)
|
||||
```
|
||||
|
||||
**Key architectural details:**
|
||||
- Bot and API both import from `shared/` — do not duplicate logic.
|
||||
- Services in `shared/modules/` are singleton objects, not classes.
|
||||
- The database uses PostgreSQL 16+ via Drizzle ORM with `bigint` mode for Discord IDs and currency.
|
||||
- Feature modules follow a strict file suffix convention (see below).
|
||||
|
||||
## Import Conventions
|
||||
|
||||
Use path aliases (defined in `tsconfig.json`). Order: external packages → aliases → relative.
|
||||
|
||||
```typescript
|
||||
import { SlashCommandBuilder } from "discord.js"; // external
|
||||
import { economyService } from "@shared/modules/economy/economy.service"; // alias
|
||||
import { users } from "@db/schema"; // alias
|
||||
import { createErrorEmbed } from "@lib/embeds"; // alias
|
||||
import { localHelper } from "./helper"; // relative
|
||||
```
|
||||
|
||||
**Aliases:**
|
||||
- `@/*` → `bot/`
|
||||
- `@shared/*` → `shared/`
|
||||
- `@db/*` → `shared/db/`
|
||||
- `@lib/*` → `bot/lib/`
|
||||
- `@modules/*` → `bot/modules/`
|
||||
- `@commands/*` → `bot/commands/`
|
||||
|
||||
## Code Patterns
|
||||
|
||||
### Module File Suffixes
|
||||
|
||||
- `*.view.ts` — Creates Discord embeds/components
|
||||
- `*.interaction.ts` — Handles button/select/modal interactions
|
||||
- `*.service.ts` — Business logic (lives in `shared/modules/`)
|
||||
- `*.types.ts` — Module-specific TypeScript types
|
||||
- `*.test.ts` — Tests (co-located with source)
|
||||
|
||||
### Command Definition
|
||||
|
||||
```typescript
|
||||
export const commandName = createCommand({
|
||||
data: new SlashCommandBuilder().setName("name").setDescription("desc"),
|
||||
execute: async (interaction) => {
|
||||
await withCommandErrorHandling(interaction, async () => {
|
||||
const result = await service.method();
|
||||
await interaction.editReply({ embeds: [createSuccessEmbed(result)] });
|
||||
}, { ephemeral: true });
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
`withCommandErrorHandling` (from `@lib/commandUtils`) handles `deferReply`, `UserError` display, and unexpected error logging automatically.
|
||||
|
||||
### Service Pattern
|
||||
|
||||
```typescript
|
||||
export const serviceName = {
|
||||
methodName: async (params: ParamType): Promise<ReturnType> => {
|
||||
return await withTransaction(async (tx) => {
|
||||
// database operations
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
```typescript
|
||||
import { UserError, SystemError } from "@shared/lib/errors";
|
||||
|
||||
throw new UserError("You don't have enough coins!"); // shown to user
|
||||
throw new SystemError("DB connection failed"); // logged, generic message shown
|
||||
```
|
||||
|
||||
### Database Transactions
|
||||
|
||||
```typescript
|
||||
import { withTransaction } from "@/lib/db";
|
||||
|
||||
return await withTransaction(async (tx) => {
|
||||
const user = await tx.query.users.findFirst({ where: eq(users.id, id) });
|
||||
await tx.update(users).set({ coins: newBalance }).where(eq(users.id, id));
|
||||
return user;
|
||||
}, existingTx); // pass existing tx for nested transactions
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
Mock modules **before** imports. Use `bun:test`.
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect, mock, beforeEach } from "bun:test";
|
||||
|
||||
mock.module("@shared/db/DrizzleClient", () => ({
|
||||
DrizzleClient: { query: mockQuery },
|
||||
}));
|
||||
|
||||
describe("serviceName", () => {
|
||||
beforeEach(() => mockFn.mockClear());
|
||||
it("should handle expected case", async () => {
|
||||
mockFn.mockResolvedValue(testData);
|
||||
const result = await service.method(input);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
| Element | Convention | Example |
|
||||
| ---------------- | ---------------------- | -------------------------------- |
|
||||
| Files | camelCase or kebab-case | `BotClient.ts`, `economy.service.ts` |
|
||||
| Classes | PascalCase | `CommandHandler`, `UserError` |
|
||||
| Functions | camelCase | `createCommand`, `handleShopInteraction` |
|
||||
| Constants | UPPER_SNAKE_CASE | `EVENTS`, `BRANDING` |
|
||||
| Enums | PascalCase | `TimerType`, `TransactionType` |
|
||||
| Services | camelCase singleton | `economyService`, `userService` |
|
||||
| Types/Interfaces | PascalCase | `Command`, `Event`, `GameConfigType` |
|
||||
| DB tables | snake_case | `users`, `moderation_cases` |
|
||||
| Custom IDs | snake_case with prefix | `shop_buy_`, `trade_accept_` |
|
||||
| API routes | kebab-case | `/api/guild-settings` |
|
||||
|
||||
## Key Files
|
||||
|
||||
| Purpose | File |
|
||||
| ----------------- | -------------------------- |
|
||||
| Bot entry point | `bot/index.ts` |
|
||||
| Discord client | `bot/lib/BotClient.ts` |
|
||||
| DB schema index | `shared/db/schema.ts` |
|
||||
| Error classes | `shared/lib/errors.ts` |
|
||||
| Environment vars | `shared/lib/env.ts` |
|
||||
| Config loader | `shared/lib/config.ts` |
|
||||
| Embed helpers | `bot/lib/embeds.ts` |
|
||||
| Command utils | `bot/lib/commandUtils.ts` |
|
||||
| API server | `api/src/server.ts` |
|
||||
Reference in New Issue
Block a user