A new README for the combined project with setup from the Flashpoint Ultimate torrent. New feature docs for streaming, proxy sign-in and game saves; deployment, upgrade and streaming READMEs updated for the single compose file; the project renamed throughout.
5.8 KiB
5.8 KiB
Coding Standards
Coding standards and best practices for Dotcom Arcade development.
TypeScript Standards
Strict Mode
All services use TypeScript strict mode with noUnusedLocals,
noUnusedParameters, and noFallthroughCasesInSwitch enabled.
Type Annotations
- Use explicit return types for public functions
- Use type inference for obvious cases (const count = 0)
- Prefer interfaces over type aliases for object shapes
- Use
unknowntype guard instead ofany - Prefer
nullfor intentional absence,undefinedfor optional parameters - Prefer union types over enums (unless you need reverse mapping)
Example:
// Good
export function getGameById(id: string): Game | null {
return games.find((g) => g.id === id) ?? null;
}
// Good - interfaces for objects, unions for types
interface User {
id: string;
name: string;
}
type Status = 'active' | 'inactive';
// Avoid
export function getGameById(id: string) {} // implicit return type
Naming Conventions
Files
- Backend: Services
PascalCase.ts, routeskebab-case.ts, middleware/utilscamelCase.ts - Frontend: Components
PascalCase.tsx, hooksuseXxxx.ts, stores/utilscamelCase.ts - Game Service: All files
kebab-case.ts
Variables & Functions
- Variables/functions:
camelCase - Constants:
SCREAMING_SNAKE_CASE - Private properties: prefix with underscore
_cache - Boolean variables:
is/has/shouldprefix - Classes:
PascalCase - Interfaces:
PascalCase(noIprefix) - React components:
PascalCase - Component props:
ComponentNameProps - Event handlers:
handleprefix
Code Organization
Import Order
- Node.js built-in modules
- External packages (alphabetical)
- Internal modules with
@alias (alphabetical) - Relative imports
- Type imports (separate)
Exports
- Prefer named exports (except React components where default export is acceptable)
File Structure
Services: imports → types/interfaces → constants → class → helper functions
React components: imports → types → constants → component → helper components
ESLint Rules
Backend (.eslintrc.json)
{
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-non-null-assertion": "warn"
}
Frontend (.eslintrc.json)
{
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
Run with npm run lint or auto-fix with npm run lint -- --fix
Comment Standards
Documentation Comments
Use JSDoc for public APIs:
/**
* Retrieves a game by its ID from the database.
* @param id - The unique identifier of the game
* @returns The game object if found, null otherwise
*/
export async function getGameById(id: string): Promise<Game | null> {
// Implementation
}
Inline Comments
- Explain "why", not "what"
- Use TODO, FIXME, HACK prefixes for issues
- Avoid obvious comments
Error Handling
Backend
// Use try-catch for async operations
try {
const game = await db.query('SELECT * FROM game WHERE id = ?', [id]);
return game ?? null;
} catch (error) {
logger.error('Failed to get game', { id, error });
throw new DatabaseError('Failed to retrieve game', { cause: error });
}
// Express route handlers pass errors to middleware with next(error)
Frontend
// React Query error handling
const { data, error, isError } = useQuery({ queryKey: ['games'], queryFn: fetchGames });
if (isError) return <div>Failed: {error.message}</div>;
Backend Standards
Database
- Use prepared statements to prevent SQL injection
- Use transactions for multi-step operations
- Never write to flashpoint.sqlite (read-only)
- Use UserDatabaseService for writes to user.db
Service Layer
export class GameService {
// Simple queries return null for not found
public getGameById(id: string): Game | null {}
// List queries return empty arrays
public searchGames(query: GameQuery): Game[] {}
// Mutations throw errors on failure
public async updateGame(id: string, updates: Partial<Game>): Promise<Game> {}
}
Routes
- Validate input with Zod
- Return consistent response structure
- Use 404 for not found, 400 for validation, 500 for server errors
- Pass errors to middleware with
next(error)
Frontend Standards
React Components
- Prefer function components
- Extract custom hooks for reusable logic
- Destructure props in function signature
- Use appropriate state management (React Query for server state, Zustand for UI state)
API Calls
CRITICAL: Use authenticated axios instance from frontend/src/lib/api.ts
// CORRECT - authenticated
import { api } from '@/lib/api';
const { data } = await api.get('/api/games');
// WRONG - no authentication
const response = await fetch('/api/games'); // ❌ Bypasses auth!
Styling
- Group related Tailwind classes
- Use
clsxfor conditional classes - Extract common styles to constants
Git Commit Standards
Format
<type>(<scope>): <subject>
<body>
<footer>
Types
feat: New featurefix: Bug fixdocs: Documentationstyle: Formatting (not CSS)refactor: Code refactoringtest: Testschore: Maintenance
Examples
feat(backend): add game favoriting functionality
fix(frontend): resolve infinite scroll pagination bug
docs(development): add coding standards documentation
Branches
feature/<description>bugfix/<description>docs/<description>refactor/<description>