Initial commit
This commit is contained in:
586
skills/code-style/REFERENCE.md
Normal file
586
skills/code-style/REFERENCE.md
Normal file
@@ -0,0 +1,586 @@
|
||||
# Technical Reference
|
||||
|
||||
Complete configuration files and detailed technical information for Grey Haven coding standards.
|
||||
|
||||
## TypeScript/React Configuration Files
|
||||
|
||||
### .prettierrc
|
||||
|
||||
Complete Prettier configuration from cvi-template:
|
||||
|
||||
```json
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"printWidth": 90,
|
||||
"singleQuote": false,
|
||||
"endOfLine": "lf",
|
||||
"trailingComma": "all",
|
||||
"plugins": ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"]
|
||||
}
|
||||
```
|
||||
|
||||
**Field Explanations:**
|
||||
|
||||
- `tabWidth: 2` - Use 2 spaces for indentation (NOT 4)
|
||||
- `semi: true` - Always add semicolons at the end of statements
|
||||
- `printWidth: 90` - Wrap lines at 90 characters (NOT 80 or 120)
|
||||
- `singleQuote: false` - Use double quotes for strings
|
||||
- `endOfLine: "lf"` - Use Unix-style line endings (\\n)
|
||||
- `trailingComma: "all"` - Add trailing commas wherever possible
|
||||
- `prettier-plugin-organize-imports` - Auto-organize imports by type
|
||||
- `prettier-plugin-tailwindcss` - Auto-sort Tailwind CSS classes
|
||||
|
||||
### .eslintrc
|
||||
|
||||
Complete ESLint configuration from cvi-template:
|
||||
|
||||
```json
|
||||
{
|
||||
"root": true,
|
||||
"env": { "browser": true, "es2020": true },
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["react-refresh"],
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"no-unused-vars": "off",
|
||||
"react-refresh/only-export-components": "off"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Rule Explanations:**
|
||||
|
||||
- `react-hooks/exhaustive-deps: "off"` - Don't enforce exhaustive deps in useEffect (manage manually)
|
||||
- `@typescript-eslint/no-explicit-any: "off"` - Allow `any` type for flexibility
|
||||
- `@typescript-eslint/no-unused-vars: "off"` - Don't error on unused variables (clean up manually)
|
||||
- `no-unused-vars: "off"` - Same as above for non-TypeScript files
|
||||
- `react-refresh/only-export-components: "off"` - Allow exporting non-components from module
|
||||
|
||||
**Philosophy:** Grey Haven takes a pragmatic approach over pedantic enforcement. The codebase values developer velocity and allows `any` types, unused variables, and manual dependency management when appropriate.
|
||||
|
||||
### tsconfig.json
|
||||
|
||||
Complete TypeScript configuration from cvi-template:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"jsx": "react-jsx",
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"allowJs": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
},
|
||||
"noEmit": true,
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"]
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", ".wrangler", ".output"]
|
||||
}
|
||||
```
|
||||
|
||||
**Key Settings:**
|
||||
|
||||
- `strict: true` - Enable all strict type checking options
|
||||
- `target: "ES2022"` - Compile to ES2022 JavaScript
|
||||
- `module: "ESNext"` - Use ESNext module syntax
|
||||
- `moduleResolution: "Bundler"` - Use bundler resolution for Vite/TanStack
|
||||
- `jsx: "react-jsx"` - Use React 17+ JSX transform
|
||||
- `paths: { "~/*": ["./src/*"] }` - Path alias for imports (e.g., `import { foo } from "~/lib/utils"`)
|
||||
- `noEmit: true` - Don't emit compiled files (Vite handles this)
|
||||
|
||||
### package.json Scripts
|
||||
|
||||
Standard scripts from cvi-template:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "vinxi dev",
|
||||
"build": "vinxi build",
|
||||
"start": "vinxi start",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"prepare": "husky"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Python/FastAPI Configuration Files
|
||||
|
||||
### pyproject.toml (Ruff)
|
||||
|
||||
Complete Ruff configuration from cvi-backend-template:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
fix-only = true
|
||||
show-fixes = true
|
||||
indent-width = 4
|
||||
line-length = 130 # NOT 80 or 88!
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E", # pycodestyle errors
|
||||
"W", # pycodestyle warnings
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"B", # flake8-bugbear
|
||||
"C4", # flake8-comprehensions
|
||||
"UP", # pyupgrade
|
||||
]
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
line-ending = "lf"
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
known-first-party = ["app"]
|
||||
```
|
||||
|
||||
**Field Explanations:**
|
||||
|
||||
- `line-length = 130` - Wrap lines at 130 characters (CRITICAL: NOT 80 or 88!)
|
||||
- `indent-width = 4` - Use 4 spaces for indentation
|
||||
- `fix-only = true` - Auto-fix issues without showing unfixable errors
|
||||
- `show-fixes = true` - Show what was fixed
|
||||
- `select = [...]` - Enable specific linter rules:
|
||||
- `E`, `W` - pycodestyle style enforcement
|
||||
- `F` - pyflakes error detection
|
||||
- `I` - isort import sorting
|
||||
- `B` - flake8-bugbear bug detection
|
||||
- `C4` - flake8-comprehensions list/dict comprehension improvements
|
||||
- `UP` - pyupgrade automatic Python version upgrades
|
||||
|
||||
### pyproject.toml (MyPy)
|
||||
|
||||
Complete MyPy configuration from cvi-backend-template:
|
||||
|
||||
```toml
|
||||
[tool.mypy]
|
||||
python_version = "3.11"
|
||||
warn_return_any = true
|
||||
warn_unused_configs = true
|
||||
disallow_untyped_defs = true
|
||||
check_untyped_defs = true
|
||||
ignore_missing_imports = false
|
||||
strict_optional = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_no_return = true
|
||||
warn_unreachable = true
|
||||
```
|
||||
|
||||
**Key Settings:**
|
||||
|
||||
- `disallow_untyped_defs: true` - **CRITICAL**: Require type hints on all function definitions
|
||||
- `python_version = "3.11"` - Target Python 3.11+ (projects use 3.12+)
|
||||
- `strict_optional: true` - Strict checking of Optional types
|
||||
- `warn_return_any: true` - Warn when returning Any from typed functions
|
||||
- `warn_unused_configs: true` - Warn about unused mypy config
|
||||
|
||||
**Philosophy:** Type hints are **required** in Grey Haven Python projects. Unlike TypeScript's relaxed rules, Python enforces strict typing.
|
||||
|
||||
### pyproject.toml (Pytest)
|
||||
|
||||
Complete pytest configuration from cvi-backend-template:
|
||||
|
||||
```toml
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = ["."]
|
||||
asyncio_mode = "auto"
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
python_classes = ["Test*"]
|
||||
python_functions = ["test_*"]
|
||||
markers = [
|
||||
"unit: Unit tests (fast, isolated, mocked)",
|
||||
"integration: Integration tests (database, external services)",
|
||||
"e2e: End-to-end tests (full system)",
|
||||
"benchmark: Performance benchmark tests",
|
||||
"slow: Tests that take > 1 second",
|
||||
]
|
||||
addopts = [
|
||||
"-ra", # Show summary of all test outcomes
|
||||
"--strict-markers", # Require markers to be registered
|
||||
"--strict-config", # Raise on config warnings
|
||||
"--tb=short", # Shorter traceback format
|
||||
]
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["app"]
|
||||
omit = ["tests/*", "*/migrations/*"]
|
||||
|
||||
[tool.coverage.report]
|
||||
precision = 2
|
||||
show_missing = true
|
||||
skip_covered = false
|
||||
```
|
||||
|
||||
**Marker Explanations:**
|
||||
|
||||
- `@pytest.mark.unit` - Fast, isolated tests with mocked dependencies
|
||||
- `@pytest.mark.integration` - Tests with database or external services
|
||||
- `@pytest.mark.e2e` - Full system end-to-end tests
|
||||
- `@pytest.mark.benchmark` - Performance measurement tests
|
||||
- `@pytest.mark.slow` - Tests taking over 1 second
|
||||
|
||||
**Coverage:** Aim for >80% code coverage across the project.
|
||||
|
||||
## Project Structure Details
|
||||
|
||||
### TypeScript/React Structure (cvi-template)
|
||||
|
||||
```plaintext
|
||||
cvi-template/
|
||||
├── src/
|
||||
│ ├── routes/ # TanStack Router file-based routing
|
||||
│ │ ├── __root.tsx # Root layout
|
||||
│ │ ├── index.tsx # Homepage (/)
|
||||
│ │ ├── _authenticated/ # Protected routes (requires auth)
|
||||
│ │ │ └── dashboard.tsx # /dashboard
|
||||
│ │ └── settings/
|
||||
│ │ ├── index.tsx # /settings
|
||||
│ │ └── profile.tsx # /settings/profile
|
||||
│ │
|
||||
│ ├── lib/
|
||||
│ │ ├── components/ # React components
|
||||
│ │ │ ├── ui/ # Shadcn UI components (button, card, etc.)
|
||||
│ │ │ ├── auth/ # Authentication components
|
||||
│ │ │ └── layout/ # Layout components (header, sidebar)
|
||||
│ │ │
|
||||
│ │ ├── server/ # Server-side code
|
||||
│ │ │ ├── schema/ # Drizzle database schemas (snake_case!)
|
||||
│ │ │ ├── functions/ # TanStack Start server functions
|
||||
│ │ │ ├── auth.ts # Better-auth configuration
|
||||
│ │ │ └── db.ts # Database connection with RLS
|
||||
│ │ │
|
||||
│ │ ├── config/ # Configuration files
|
||||
│ │ │ └── env.ts # Environment validation (@t3-oss/env-core)
|
||||
│ │ │
|
||||
│ │ ├── hooks/ # Custom React hooks (use-* naming)
|
||||
│ │ ├── utils/ # Utility functions
|
||||
│ │ └── types/ # TypeScript type definitions
|
||||
│ │
|
||||
│ ├── clients/ # API client code
|
||||
│ ├── data/ # Zod schemas for data validation
|
||||
│ ├── middleware/ # Route middleware and guards
|
||||
│ ├── services/ # Business logic services
|
||||
│ ├── workers/ # Cloudflare Workers code
|
||||
│ ├── index.css # Global styles (Tailwind)
|
||||
│ └── router.tsx # Router configuration
|
||||
│
|
||||
├── public/ # Static assets
|
||||
├── tests/ # Test files
|
||||
├── .prettierrc # Prettier config
|
||||
├── .eslintrc # ESLint config
|
||||
├── tsconfig.json # TypeScript config
|
||||
├── drizzle.config.ts # Drizzle ORM config
|
||||
├── vite.config.ts # Vite bundler config
|
||||
├── wrangler.jsonc # Cloudflare Workers config
|
||||
└── package.json # Dependencies and scripts
|
||||
```
|
||||
|
||||
### Python/FastAPI Structure (cvi-backend-template)
|
||||
|
||||
```plaintext
|
||||
cvi-backend-template/
|
||||
├── app/
|
||||
│ ├── config/ # Application configuration
|
||||
│ │ └── settings.py # Pydantic settings with env vars
|
||||
│ │
|
||||
│ ├── db/ # Database layer
|
||||
│ │ ├── models/ # SQLModel database models (snake_case!)
|
||||
│ │ │ ├── account.py # Account model
|
||||
│ │ │ ├── tenant.py # Tenant model (multi-tenant)
|
||||
│ │ │ └── user.py # User model
|
||||
│ │ │
|
||||
│ │ ├── repositories/ # Repository pattern (data access)
|
||||
│ │ │ ├── base.py # Base repository
|
||||
│ │ │ ├── account_repository.py
|
||||
│ │ │ ├── tenant_repository.py
|
||||
│ │ │ └── user_repository.py
|
||||
│ │ │
|
||||
│ │ ├── db_types.py # Custom database types (UTCDateTime)
|
||||
│ │ └── session.py # Database session management
|
||||
│ │
|
||||
│ ├── routers/ # FastAPI routers (endpoints)
|
||||
│ │ ├── accounts.py # /accounts endpoints
|
||||
│ │ ├── tenants.py # /tenants endpoints
|
||||
│ │ └── users.py # /users endpoints
|
||||
│ │
|
||||
│ ├── services/ # Business logic layer
|
||||
│ │ ├── auth_service.py # Authentication service
|
||||
│ │ └── billing_service.py # Billing service
|
||||
│ │
|
||||
│ ├── schemas/ # Pydantic schemas (API contracts)
|
||||
│ │ ├── accounts.py # Account request/response schemas
|
||||
│ │ ├── tenants.py # Tenant request/response schemas
|
||||
│ │ └── users.py # User request/response schemas
|
||||
│ │
|
||||
│ ├── utils/ # Utility functions
|
||||
│ │ ├── logging.py # Logging configuration
|
||||
│ │ └── security.py # Security utilities
|
||||
│ │
|
||||
│ ├── dependencies.py # FastAPI dependencies
|
||||
│ └── main.py # FastAPI app entry point
|
||||
│
|
||||
├── tests/ # Test files
|
||||
│ ├── unit/ # Unit tests (@pytest.mark.unit)
|
||||
│ ├── integration/ # Integration tests (@pytest.mark.integration)
|
||||
│ ├── e2e/ # E2E tests (@pytest.mark.e2e)
|
||||
│ ├── conftest.py # Pytest fixtures
|
||||
│ └── __init__.py
|
||||
│
|
||||
├── alembic/ # Database migrations (if using Alembic)
|
||||
├── pyproject.toml # Python project config (Ruff, MyPy, pytest)
|
||||
├── requirements.txt # Python dependencies
|
||||
└── .env.example # Example environment variables
|
||||
```
|
||||
|
||||
## Database Naming Standards
|
||||
|
||||
### Field Naming Rules
|
||||
|
||||
**ALWAYS use snake_case for database columns:**
|
||||
|
||||
```text
|
||||
✅ CORRECT ❌ WRONG
|
||||
created_at createdAt
|
||||
tenant_id tenantId
|
||||
email_address emailAddress
|
||||
is_active isActive
|
||||
first_name firstName
|
||||
last_name lastName
|
||||
phone_number phoneNumber
|
||||
billing_tier billingTier
|
||||
max_retries maxRetries
|
||||
api_key_hash apiKeyHash
|
||||
```
|
||||
|
||||
### Table Naming Rules
|
||||
|
||||
**Use lowercase plural names:**
|
||||
|
||||
```text
|
||||
✅ CORRECT ❌ WRONG
|
||||
users User, Users, user
|
||||
accounts Account, ACCOUNTS
|
||||
tenants Tenant, Tenants
|
||||
organizations Organization
|
||||
subscriptions Subscription
|
||||
```
|
||||
|
||||
### Index Naming Rules
|
||||
|
||||
**Use descriptive index names:**
|
||||
|
||||
```text
|
||||
Format: {table}_{column}_idx
|
||||
|
||||
✅ CORRECT ❌ WRONG
|
||||
user_email_idx idx_1, email_index
|
||||
user_tenant_id_idx tenant_idx
|
||||
organization_slug_idx slug
|
||||
```
|
||||
|
||||
### Foreign Key Naming Rules
|
||||
|
||||
**Reference the parent table:**
|
||||
|
||||
```text
|
||||
Format: {parent_table}_{singular}_id
|
||||
|
||||
✅ CORRECT ❌ WRONG
|
||||
tenant_id tenant
|
||||
account_id acc_id, accountId
|
||||
organization_id org_id, orgId
|
||||
user_id userId, uid
|
||||
```
|
||||
|
||||
## Multi-Tenant Architecture
|
||||
|
||||
### Tenant Isolation Levels
|
||||
|
||||
Grey Haven projects use **row-level** tenant isolation:
|
||||
|
||||
1. **Every table** includes a `tenant_id` or `account_id` field
|
||||
2. **Every query** filters by tenant ID
|
||||
3. **Row Level Security (RLS)** policies enforce tenant boundaries
|
||||
4. **Repository pattern** centralizes tenant filtering
|
||||
|
||||
### RLS Policy Pattern
|
||||
|
||||
Standard RLS policy structure:
|
||||
|
||||
```typescript
|
||||
// TypeScript/Drizzle RLS helper
|
||||
export const inSameTenant = (tenantIdCol: AnyPgColumn, query: SQL): SQL =>
|
||||
sql`${isSameTenant(tenantIdCol)} and (${query})`;
|
||||
|
||||
// Apply to table
|
||||
pgPolicy("table_name_select", {
|
||||
for: "select",
|
||||
to: "public",
|
||||
using: inSameTenant(table.tenantId, sql`true`),
|
||||
});
|
||||
```
|
||||
|
||||
```sql
|
||||
-- SQL RLS Policy
|
||||
CREATE POLICY tenant_isolation_policy ON users
|
||||
USING (tenant_id = current_setting('app.tenant_id')::text);
|
||||
```
|
||||
|
||||
### Repository Tenant Filtering
|
||||
|
||||
Every repository method filters by tenant:
|
||||
|
||||
```python
|
||||
# Python repository pattern
|
||||
async def get_by_id(self, id: UUID, tenant_id: UUID) -> Optional[Model]:
|
||||
"""Get record by ID with tenant isolation."""
|
||||
result = await self.session.execute(
|
||||
select(self.model)
|
||||
.where(self.model.id == id)
|
||||
.where(self.model.tenant_id == tenant_id) # Tenant filter!
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
```
|
||||
|
||||
## Import Organization
|
||||
|
||||
### TypeScript Import Order
|
||||
|
||||
Auto-organized by `prettier-plugin-organize-imports`:
|
||||
|
||||
```typescript
|
||||
// 1. External libraries
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
// 2. Internal modules (path alias)
|
||||
import { Button } from "~/lib/components/ui/button";
|
||||
import { useAuth } from "~/lib/hooks/use-auth";
|
||||
import { env } from "~/lib/config/env";
|
||||
|
||||
// 3. Relative imports
|
||||
import { helpers } from "./helpers";
|
||||
```
|
||||
|
||||
### Python Import Order
|
||||
|
||||
Auto-organized by Ruff `isort`:
|
||||
|
||||
```python
|
||||
# 1. Standard library imports
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
# 2. Third-party imports
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import select
|
||||
from pydantic import BaseModel
|
||||
|
||||
# 3. Local imports (app.*)
|
||||
from app.db.models.user import User
|
||||
from app.db.repositories.user_repository import UserRepository
|
||||
from app.schemas.user import UserCreate, UserResponse
|
||||
```
|
||||
|
||||
## Line Length Reasoning
|
||||
|
||||
### TypeScript: 90 Characters
|
||||
|
||||
**Why 90?**
|
||||
|
||||
- Comfortable reading width on modern displays
|
||||
- Allows two editor panes side-by-side
|
||||
- Balances readability with code density
|
||||
- TailwindCSS classes can be long - 90 gives room
|
||||
- Standard 80 is too restrictive for modern development
|
||||
|
||||
### Python: 130 Characters
|
||||
|
||||
**Why 130?**
|
||||
|
||||
- Type hints can be verbose: `Annotated[AccountRepository, Depends(get_account_repository)]`
|
||||
- Pydantic field definitions: `Field(default=None, description="Long description here")`
|
||||
- Allows descriptive variable names without constant wrapping
|
||||
- FastAPI decorators are long: `@router.post("/endpoint", response_model=ResponseSchema)`
|
||||
- PEP 8's 79 is outdated for modern development
|
||||
|
||||
## Pre-commit Hooks
|
||||
|
||||
Both templates use pre-commit hooks for code quality:
|
||||
|
||||
### TypeScript Pre-commit
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
# Run linting
|
||||
npm run lint
|
||||
|
||||
# Run formatting check
|
||||
npm run format:check
|
||||
|
||||
# Run type checking
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
### Python Pre-commit
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Activate virtual environment
|
||||
source .venv/bin/activate
|
||||
|
||||
# Run Ruff formatter
|
||||
ruff format .
|
||||
|
||||
# Run Ruff linter
|
||||
ruff check --fix .
|
||||
|
||||
# Run MyPy type checking
|
||||
mypy app/
|
||||
|
||||
# Run tests
|
||||
pytest -m "not slow"
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **Frontend Template**: [cvi-template](https://github.com/greyhaven-ai/cvi-template) - TanStack Start + React 19 + Drizzle
|
||||
- **Backend Template**: [cvi-backend-template](https://github.com/greyhaven-ai/cvi-backend-template) - FastAPI + SQLModel + PostgreSQL
|
||||
- **Prettier Docs**: https://prettier.io/docs/en/configuration.html
|
||||
- **ESLint Docs**: https://eslint.org/docs/latest/use/configure/
|
||||
- **Ruff Docs**: https://docs.astral.sh/ruff/
|
||||
- **Drizzle ORM**: https://orm.drizzle.team/docs/overview
|
||||
- **TanStack Start**: https://tanstack.com/router/latest/docs/framework/react/start/overview
|
||||
Reference in New Issue
Block a user