|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Dafthunk is a visual workflow automation platform built on Cloudflare infrastructure (Workers, D1, R2, AI). Users create workflows by connecting 50+ node types in a visual editor (React Flow). |
| 8 | + |
| 9 | +**Monorepo structure** (pnpm workspaces): |
| 10 | +- `apps/api` - Backend (Hono on Cloudflare Workers) |
| 11 | +- `apps/web` - Frontend (React 19 + React Router v7 + Vite) |
| 12 | +- `packages/types` - Shared TypeScript types |
| 13 | +- `packages/utils` - Shared utilities |
| 14 | + |
| 15 | +## Development Commands |
| 16 | + |
| 17 | +### Common commands |
| 18 | +```bash |
| 19 | +pnpm dev # Start all services |
| 20 | +pnpm build # Build all packages and apps |
| 21 | +pnpm typecheck # Type check all workspaces |
| 22 | +pnpm lint # Lint and type check |
| 23 | +pnpm fix # Auto-fix linting + format |
| 24 | +pnpm test # Run tests |
| 25 | + |
| 26 | +# Workspace-specific (use --filter) |
| 27 | +pnpm --filter '@dafthunk/api' dev # API dev server (port 3001) |
| 28 | +pnpm --filter '@dafthunk/web' dev # Web dev server (port 3000) |
| 29 | +pnpm --filter '@dafthunk/api' test:integration # Integration tests |
| 30 | + |
| 31 | +# Database migrations |
| 32 | +pnpm --filter '@dafthunk/api' db:migrate # Apply migrations locally |
| 33 | +pnpm --filter '@dafthunk/api' db:generate # Generate new migrations |
| 34 | +pnpm --filter '@dafthunk/api' db:prod:migrate # Apply to production |
| 35 | +``` |
| 36 | + |
| 37 | +## Architecture |
| 38 | + |
| 39 | +### Backend: API (`apps/api/`) |
| 40 | + |
| 41 | +**Routes** (`src/routes/`) |
| 42 | +- Organized by feature (workflows, executions, objects, etc.) |
| 43 | +- Stateless: each request is self-contained |
| 44 | +- Auth in `src/auth.ts` (JWT + API Keys) |
| 45 | +- Multi-tenant: always scope by `organizationId` from context (`c.get("organizationId")`) |
| 46 | +- Validate with Zod + `@hono/zod-validator` |
| 47 | + |
| 48 | +**Database** (`src/db/`) |
| 49 | +- D1 (SQLite) + Drizzle ORM |
| 50 | +- Schema: `schema/index.ts` |
| 51 | +- Queries: `queries.ts` |
| 52 | +- Migrations: `migrations/` (generate with `drizzle-kit`) |
| 53 | +- Convention: `snake_case` in SQL, `camelCase` in TypeScript |
| 54 | + |
| 55 | +**Workflow Runtime** (`src/runtime/`) |
| 56 | +- `runtime.ts` - Cloudflare Workflows for durable execution |
| 57 | +- Durable Objects manage state |
| 58 | +- `object-store.ts` - Node outputs (R2 + transient storage) |
| 59 | +- Executes nodes by graph topology |
| 60 | + |
| 61 | +**Node System** (`src/nodes/`) |
| 62 | +- node types in category folders: `text/`, `image/`, `audio/`, `browser/`, `logic/`, `math/`, `javascript/`, `anthropic/`, `openai/`, `gemini/`, `3d/`, `date/`, `document/`, `email/`, `geo/`, `json/`, `net/`, `parameter/`, `rag/` |
| 63 | +- Registry: `base-node-registry.ts` and `cloudflare-node-registry.ts` |
| 64 | +- All implement common interface from `packages/types` |
| 65 | + |
| 66 | +### Frontend: Web (`apps/web/`) |
| 67 | + |
| 68 | +**Structure** |
| 69 | +- Pages: `src/pages/` (one file per route) |
| 70 | +- Components: `src/components/` (`ui/` = shadcn/ui, `workflow/` = React Flow editor) |
| 71 | +- Routes: `src/routes.tsx` (React Router v7) |
| 72 | +- Services: `src/services/` (API clients) |
| 73 | + |
| 74 | +**Patterns** |
| 75 | +- Data fetching: SWR (consolidate related calls) |
| 76 | +- Styling: Tailwind CSS only (use `cn()` utility) |
| 77 | +- State: Avoid `useEffect`, prefer derived state |
| 78 | + |
| 79 | +### Shared: Types (`packages/types/`) |
| 80 | +- Single source of truth for data structures |
| 81 | +- Backend serializes, frontend deserializes/validates |
| 82 | +- Ensures type safety across stack |
| 83 | + |
| 84 | +## Design Principles |
| 85 | + |
| 86 | +When writing or refactoring code: |
| 87 | + |
| 88 | +### Simplify Interfaces |
| 89 | +- Export only what's necessary—hide everything else |
| 90 | +- Keep public APIs small (fewer exports = less complexity) |
| 91 | +- Use barrel exports (`index.ts`) to define module boundaries |
| 92 | +- If a function/class can't be described in one sentence, split it |
| 93 | + |
| 94 | +### Manage Complexity |
| 95 | +- Push complexity into lower-level modules with simple APIs |
| 96 | +- Eliminate unnecessary state, conditionals, and abstractions |
| 97 | +- Keep related logic together; separate unrelated concerns |
| 98 | +- Depend on interfaces/types, not concrete implementations |
| 99 | + |
| 100 | +### Prioritize Maintainability |
| 101 | +- Write the calling code you want first, then implement to match |
| 102 | +- After code works, refactor to simplify the interface |
| 103 | +- Use comments for *why* (design decisions, trade-offs), not *what* (code explains itself) |
| 104 | +- Front-load architectural decisions (module boundaries, data flow); defer details (naming, parameters) |
| 105 | + |
| 106 | +## Code Guidelines |
| 107 | + |
| 108 | +### TypeScript Style |
| 109 | +- Strict mode: never use `any` or `unknown` |
| 110 | +- Prefer `interface` over `type` for object shapes |
| 111 | +- Always use `import type` for type-only imports |
| 112 | +- Use early returns to avoid deep nesting |
| 113 | + |
| 114 | +### Naming Conventions |
| 115 | +``` |
| 116 | +Files: kebab-case.tsx |
| 117 | +Functions: camelCase() |
| 118 | +Hooks: useCamelCase() |
| 119 | +Event handlers: handleClick() |
| 120 | +Components: PascalCase |
| 121 | +``` |
| 122 | + |
| 123 | +### React (apps/web) |
| 124 | +```tsx |
| 125 | +// ✓ Correct |
| 126 | +import { Link } from 'react-router' // not react-router-dom |
| 127 | +import type { User } from '@dafthunk/types' |
| 128 | +export function MyComponent() { ... } // functional component |
| 129 | + |
| 130 | +// Data fetching |
| 131 | +const { data } = useSWR(['/users', '/posts'], fetchAll) // consolidate |
| 132 | + |
| 133 | +// Styling |
| 134 | +<div className={cn('base-class', isActive && 'active')} /> |
| 135 | + |
| 136 | +// Avoid useEffect - prefer derived state or move logic outside React |
| 137 | +``` |
| 138 | + |
| 139 | +### Hono API (apps/api) |
| 140 | +```ts |
| 141 | +// Routes by feature |
| 142 | +const workflows = new Hono() |
| 143 | +workflows.get('/', zValidator('query', schema), (c) => { |
| 144 | + const orgId = c.get('organizationId') // always scope by org |
| 145 | + // ... |
| 146 | +}) |
| 147 | +app.route('/workflows', workflows) |
| 148 | + |
| 149 | +// Database |
| 150 | +const users = sqliteTable('users', { |
| 151 | + createdAt: text('created_at'), // snake_case in DB |
| 152 | +}) |
| 153 | +export type User = InferModel<typeof users> |
| 154 | +``` |
| 155 | +
|
| 156 | +### Testing |
| 157 | +```ts |
| 158 | +// Unit tests: *.test.ts |
| 159 | +import { describe, it, expect } from 'vitest' |
| 160 | + |
| 161 | +// Integration tests: *.integration.ts |
| 162 | +``` |
0 commit comments